[springboot-webserivce] Spring Data JPA 적용과 테스트

Updated:

[2020.12.28] 부터 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 책을 통해 스프링 공부를 시작했다.

공부한 내용을 글로 정리하려고 한다.

그래야 나중에 다시 찾아보며 공부 할 수 있을테니까 !!

🍪 JPA, ORM

JPA (Java Persistence API) 의 줄임말로써 자바 ORM 기술에 대한 표준이라고 할 수 있다.

ORM (Object Relational Mapping) 은 무엇일까 ?

바로 객체가 테이블이 될 수 있도록 매핑 시켜주는 프레임워크이다.

결국 JPA 는 ORM 을 사용하기 위한 인터페이스를 모아 둔 것으로 이해하면 된다.

그렇다면 ORM 을 왜 사용하려고 하는 것 일까?

관계형 데이터베이스는 어떻게 데이터를 저장할지에 초점이 맞추어진 반면

자바와 같은 객체지향 언어는 기능과 속성을 한 곳에서 관리한다.

이렇게 각 기술에 대한 패러다임이 맞지 않아 생기는 문제를 패러다임 불일치 라고 하는데, 예를 들어보자

User user = findUser();
Group group = user.getGroup();

이 코드는 누가 봐도 user 를 통해 group 을 가져오기 때문에 group - user 간 부모 - 자식 관계를 알 수 있다.

하지만 이 코드에 데이터베이스가 추가 되면

User user = userDao.findUser();
Group group = groupDao.findGroup(user.getUserID());

user 와 group 을 따로 검색하는 문제가 발생한다.

JPA 는 이 문제를 해결하기 위해 사용한다.

개발자는 객체지향적으로 프로그래밍을 하면 JPA 가 알아서 SQL 문으로 바꾸어 실행을 해준다.

🍼 Spring Data JPA

JPA 는 인터페이스를 모아둔 것 이라고 했다.

때문에 Hibernate, EclipseLink 등 JPA 를 구현해둔 구현체를 사용한다.

하지만 스프링에서는 이 구현체들을 직접 다루지 않고 Spring Data JPA 라는 모듈을 통해 관리한다.

JPA ← Hibernate ← Spring Data JPA

이렇게 Spring Data JPA 를 사용하면 두 가지 장점이 있다.

  1. 구현체 교체의 용이성
  2. 저장소 교체의 용이성

Spring Data JPA 는 구현체를 자동으로 매핑 해주기 때문에 구현체를 Hibernate 에서 다른 것으로 바꾸어도 된다.

또 데이터베이스를 MySQL 에서 MongoDB 로 바꾸고 싶은 경우에도 손쉽게 바꿀 수 있다.

Spring Data JPA 를 Spring Data MongoDB 로 바꾸기만 하면 된다.

Spring Data 하위 프로젝트의 CRUD 구현 인터페이스는 모두 같기 때문이다.

⚙️ Spring Data JPA 적용

먼저 build.gradle 파일에 다음 두 가지 라이브러리를 등록한다.

compile(‘org.springframework.boot:spring-boot-starter-data-jpa’)

compile(‘com.h2database:h2’)

buildscript {
    ext {
        springBootVersion = '2.1.9.RELEASE'
    }
    repositories {
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group 'com.boks'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.projectlombok:lombok')
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('com.h2database:h2')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

H2 데이터베이스는 인메모리 관계형 데이터베이스로 별도의 설치가 필요 없이 라이브러리로 관리할 수 있다.

메모리에서 돌아가기 때문에 프로그램을 재시작할 때마다 초기화가 되어 테스트 용도로 많이 사용된다.


스크린샷 2021-01-01 오후 5 47 31

다음으로 domain 패키지 안에 posts 패키지를 만들고 Posts 클래스를 만들자.

Posts 클래스는 Entity 클래스로 데이터베이스의 테이블과 매핑 될 클래스이다.

package com.boks.springboot.domain.posts;


import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter  // 롬복의 Get 생성 어노테이션
@NoArgsConstructor // 롬복의 기본 생성자 자동 추가 어노테이션
@Entity  // DB의 테이블과 매칭 될 클래스임을 나타냄
public class Posts {
    @Id  // 해당 테이블의 PK를 나타냄
    @GeneratedValue (strategy = GenerationType.IDENTITY)  // PK auto_increment
    private Long id;

    @Column (length = 500, nullable = false)
    private String title;

    @Column (columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder
    public Posts (String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
}

@Entity 를 통해 클래스를 생성하면 클래스는 데이터베이스의 테이블이 된다.

@Id 을 통해 기본키를 설정하고 @Column 을 통해 테이블의 칼럼을 만든다.

@Column 을 안써도 필드는 모두 칼럼이 되지만 속성을 변경하고 싶은 경우에 사용한다.

@Builder 는 굉장히 중요한데 기본 생성자와 기능은 같지만

생성을 하는 경우에 어느 필드에 어떤 값을 넣어야 할지 정확히 알 수 있다.

이 클래스는 id,title, content, author 를 칼럼으로 가지는 테이블이 됐다.


이렇게 Entity 클래스를 생성 했으면 이 클래스로 데이터베이스를 접근하게 해 줄 PostsRepository 인터페이스를 생성한다.

package com.boks.springboot.domain.posts;

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostsRepository extends JpaRepository<Posts, Long> {

}

인터페이스로 만든 뒤 JpaRepository<Entity Class, PK 타입> 을 상속받으면 기본적인 CRUD 메소드가 생성된다.

이 때 가장 중요한 부분은 Entity 클래스와 EntityRepository 인터페이스가 같은 위치에 있어야 한다.

🔨 Spring Data JPA 테스트

Entity 클래스도 만들고 Repository 도 만들었으니 정상 작동을 하는지 테스트 할 시간이다.

앞서 테스트 했던 것 처럼 test 폴더에 같은 경로로 패키지를 만들고 PostsRepositoryTest 클래스를 만들자.

스크린샷 2021-01-01 오후 6 05 11

package com.boks.springboot.domain.posts;

import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {

    @Autowired
    PostsRepository postsRepository;

    @After  // 테스트가 끝난 뒤 수행 할 메소드 지정
    public void cleanUp() {
        postsRepository.deleteAll();
    }

    @Test
    public void findAllPosts() {
        // given
        String title = "TEST 제목";
        String content = "TEST 본문";

        postsRepository.save(Posts.builder()
                .title(title)
                .content(content)
                .author("nkb7714@gmail.com")
                .build());

        // when
        List<Posts> postsList = postsRepository.findAll();

        // then
        Posts posts = postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
}

[Spring] 롬복을 이용한 DTO 생성과 테스트 의 테스트코드와 비슷하다.

save 메소드는 posts 테이블에 insert / update 쿼리를 실행 한다.

findAll 메소드는 테이블에 모든 데이터를 가져온다.

그 다음 assertThat 을 통해 잘 가져왔는지 확인한다.

스크린샷 2021-01-01 오후 6 13 18

결과는 통과!

📡 쿼리 로그 확인

아무런 설정없이 위 테스트 과정을 따라왔다면 테스트 로그에 쿼리문은 보이지 않는다.

테스트를 할 때 어떤 쿼리문이 동작하는지 알고 싶다면 다음과 같은 설정을 하면 된다.

main → resources 폴더에 application.properties 파일을 생성한다.

spring.jpa.show-sql=true

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

그리고 두 가지 옵션을 추가하면 된다.

첫 번째 옵션은 말 그대로 로그에 쿼리문을 보이게 해주는 옵션이고

첫 번째 옵션만 적용한 경우 H2DB 의 쿼리문을 보여준다.

두 번째 옵션은 H2DB 의 쿼리문을 MySQL 로 바꾸어 보여준다.

두 가지 옵션을 적용한 뒤 로그를 다시 보면

image

쿼리문도 로그로 확인을 할 수 있다.

📕 정리

ORM 은 객체가 테이블이 될 수 있도록 만들어 주는 것..

JPA 는 ORM 의 기능을 인터페이스로 모아 둔 것..

JPA 를 구현한 구현체는 Hibernate, Eclipse Link 등이 있으나 Spring Data JPA 를 사용..

테이블을 의미하는 객체를 Entity 클래스 라고 함..

이제 등록 / 수정 / 조회 API 만들 차례..



Categories:

Updated:

Leave a comment