본문 바로가기
스프링/스프링부트

[Spring Boot] 스프링부트 프로젝트에 Spring Data JPA 적용하기

by 책 읽는 개발자_테드 2021. 1. 15.
반응형

스프링부트 프로젝트에 Spring Data JPA 적용하기

 

`스프링 부트와 AWS로 혼자 구현하는 웹 서비스`의 내용을 정리한 글입니다. 이 글에서는 스프링부트에 JPA를 설치하고, 사용하는 방법을 다룹니다. 그리고 작성한 JPA 코드를 JUnit을 통해 테스트합니다.

 

학습 목표

Spring Data JPA 설치

Spring Data JPA 사용

Spring Data JPA 테스트 코드 작성


Spring Data JPA 설치

 

⭐ 스프링부트 프로젝트에 build.gradle에 아래와 같이 의존성을 추가한다.

 

dependencies {

   compile('org.springframework.boot:spring-boot-starter-web')
   testCompile('org.springframework.boot:spring-boot-starter-test')
   compile('org.projectlombok:lombok')
   testCompile "org.projectlombok:lombok"
   annotationProcessor('org.projectlombok:lombok')
   testAnnotationProcessor('org.projectlombok:lombok')

   compile('org.springframework.boot:spring-boot-starter-data-jpa') //1
   compile('com.h2database:h2') //2

}

 

1. spring-boot-starter-data-jpa: 스프링 부트용 Spring Data JPA 추상화 라이브러리다. 스프링 부트 버전에 맞춰 자동으로 JPA관련 라이브러리들의 버전을 관리해준다.

 

2. h2: 인메모리 관계형 데이터베이스다. 메모리에서 실행되어 애플리케이션을 재시작하면 초기화되는 특성으로 테스트에 많이 사용된다. 별도의 설치 없이 프로젝트 의존성만으로 관리할 수 있다. 

 

다음으로 domain 패키지를 생성한다. domain 패키지는 도메인을 담을 패키지다. 도메인이란 게시글, 댓글, 회원, 정산 ,결제 등 소프트웨어에 대한 요구사항 혹은 문제영역을 뜻한다.

 

 

기존에 MyBatis와 같은 매퍼에서는 dao 패키지에 xml에 쿼리를 담고, 클래스는 오로지 쿼리의 결과만 담는다. JPA에서는 이런 모든 일이 도메인 클래스에서 해결된다.

 

Spring Data JPA 사용하기

 

⭐ 스프링부트 프로젝트 domain 패키지에 posts 패키지와 Posts 클래스를 만든다. 

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Getter //6
@NoArgsConstructor //5
@Entity //1
public class Posts {

   @Id //2
   @GeneratedValue(strategy = GenerationType.IDENTITY) //3
   private Long id;

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

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

   private String autor;

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



Posts 클래스는 실제 DB의 테이블과 매칭되는 클래스 Entity 클래스라고 한다. JPA를 사용하여 DB 데이터에 작업할 경우 실제 쿼리를 날리기보다 Entity 클래스의 수정을 통해 작업한다. 위 코드는 아래와 같은 JPA에서 제공하는 어노테이션들을 사용한다.

 

1. @Entity: 테이블과 링크될 클래스임을 나타낸다. 기본값으로 클래스의 camel case 이름을 언어스코어 네이밍으로 테이블 이름을 매칭한다. ex)SalesManager.java -> sales_manager table

2. @id: 해당 테이블의 PK 필드를 나타낸다.

3. @GeneratedValue: PK 생성 규칙을 나타낸다. 스프링부트 2.0에서는  GenerationType.IDENTITY 옵션을 추가해야만 auto_increment가 된다.

4. @Column: 테이블의 칼럼을 나타낸다. 선언하지 않아도 해당 클래스의 필드는 모두 칼럼이되지만, 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용한다. 예를들어 문자열 기본값은 VARCHAR(255)의 사이즈를 늘리는 등에 사용할 수 있다.

 

더보기

Entity의 PK는 Long 타입(MySQL 기준 bigint)의 auto_increment가 추천된다. 주민등록번화와 같은 비즈니스상 유니크 키나, 여러 키를 조합한 복합키로 PK를 잡으면 다음과 같은 단점이 있다.

 

1. FK를 맺을 때 다른 테이블에서 복합키 전부를 갖고 있거나, 중간 테이블을 하나 더 둬야 하는 상황 발생

2. 인덱스에 부정적 영향을 끼침

3. 유니크한 조건이 변경될 경우 PK 전체를 수정해야 하는 일 발생

 

나머지 어노테이션은 롬복 라이브러리에서 제공하는 어노테이션들이다. 서비스 초기 구축 단계에서 테이블 설계가 빈번히 변경될 수 있다. 이때  롬복의 어노테이션들은 코드 변경량을 최소화 한다.

 

5. @NoArgsConstructor : 기본 생성자를 자동 추가. public Posts()와 같은 효과

6. @Getter : 클래스 내 모든 필드의 Getter 메소드를 자동 생성

7.@Builder:  해당 클래스의 빌더 패턴 클래스를 생성. 생성자 상단에 선언시 생성자에 포함된 필드만 빌더에 포함.

 

Entity 클래스에 값을 채워 DB에 삽입할 때는 생성자 또는 @Builder를 통해 최종값을 채운 후 DB에 삽입한다. 

 

Example.builder()

.a(a)

.b(b)

.build();

Entity 클래스에 값을 채우기 위해 Setter를 만들지 않는다. Setter 메소드는 클래스의 인스턴스 값들이 언제 어디서 변해야하는지 코드상으로 명확히 구분할 수 없게 만들어 차후 기능 변경 시 복잡함을 더한다.

 

⭐ 다음으로 Posts 클래스로 Database를 접근하게 해줄 JpaRepository 인터페이스를 생성한다. 이름은 PostsRepository로 한다.

 



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

public interface PostsRepository extends JpaRepository<Posts, Long> {

}

 

 

MyBatis 등의 SQL Mapper에서 DAO라고 불리는 DB Layer 접근자를 JPA에선 Repository라고 부르며 인터페이스로 생성한다.

 

단순히 인터페이스를 생성 후, JpaRepository<Entity 클래스, PK 타입>을 상속하면 기본적인 CRUD 메소드가 자동으로 생성된다. 이때 Entity 클래스와 Entity Repository는 서로가 없으면 제대로 역할을 할 수 없다. 두 파일을 한 패키지에 함께 위치해야 하는 점을 유의하자.



Spring Data JPA 테스트 코드 작성

 

PostsRepository 클래스 이름에 대고 마우스 오른쪽을 눌러 Go To - Test - Create New Test버튼을 클릭한다.

 

 

그리고 등장하는 Create Test 팝업에서 OK 버튼을 눌러 테스트를 생성한다.

 

⭐ test 디렉토리에 domain.posts 패키지와 그 내부에 PostsRepositoryTest가 생성되었다. 여기에 save, findAll 기능을 테스트하는 코드를 작성하자.

 

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;

@ExtendWith(SpringExtension.class)
@SpringBootTest
class PostsRepositoryTest {

   @Autowired
   PostsRepository postsRepository;

   @AfterEach
   public void cleanup(){
       postsRepository.deleteAll();
   }

   @Test
   public void 게시글저장_불러오기(){
       //given
       String title = "테스트 게시글";
       String content = "테스트 본문";

       postsRepository.save(Posts.builder() //2
               .title(title)
               .content(content)
               .autor("jojoldu@gmail.com")
               .build());

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

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

 

위 코드의 의미는 다음과 같다.

 

1. @AfterEach: Junit에서 단위 테스트가 끝날 때마다 수행되는 메소드를 지정한다. 보통 배포 전 전체 테스트간 데이터 침범을 막기 위해 사용된다. 

2. postsRepository.save: posts 테이블에 insert/update 쿼리를 실행한다. id 값이 있다면 update가, 없다면 insert 쿼리가 실행한다.

3. postsRepository.findAll:테이블 posts에 있는 모든 데이터를 조회하는 메소드다.

 

⭐ 테스트를 실행하면, 테스트가 통과하는 걸 확인할 수 있다.

 별다른 설정 없이 @SpringBootTest를 사용할 경우 H2 데이터베이스를 자동으로 실행한다. 

 

 

⭐여기서 실제 실행된 쿼리를 확인하고 싶다면 src/main/resources 디렉토리 아래에 application.properties 파일을 생성하고, 

 

 

아래와 같은 코드를 작성한다.

 

spring.jpa.show_sql=true

 

그럼 다음과 같이 콘솔에서 쿼리 로그를 볼 수 있다.

 

위의 로그에는 H2 쿼리 문법이 보인다. H2는 가장 많은 사람들이 사용하는 RDBMS 중 하나인 MySQL의 쿼리를 수행해도 정상 작동한다. 이번에는 출력되는 쿼리 로그를 MySQL 버전으로 변경해 보자.

 

application.properties의 코드를 추가하고, 다시 테스트를 수행해보자.

 

spring.jpa..show_sql=true

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

spring.jpa.properties.hibernate.dialect.storage_engine=innodb

spring.datasource.hikari.jdbc-url=jdbc:h2:mem:testdb;MODE=MYSQL

spring.datasource.hikari.username=sa

 

옵션이 정상적으로 적용되었다.

 

다음글에서 Spring Data JPA를 사용해서 등록/수정/조회 (CRUD)를 작성해보자.

 

 

출처

스프링 부트와 AWS로 혼자 구현하는 웹 서비스

반응형

댓글