Blog

[Spring-LDW] 스프링부트에서 JPA적용 및 간단 테스트 코드

Category
Author
citeFred
citeFred
PinOnMain
1 more property
스프링 부트와 AWS로 혼자 구현하는 웹 서비스  : 인텔리제이, JPA, JUnit 테스트, 그레이들, 소셜 로그인, AWS 인프라로 무중단 배포까지 이동욱 저
3장 스프링부트에서 JPA로 데이터베이스 다뤄보자
Table of Content

MyBatis(iBatis)→JPA 개요

과거(MyBatis 를 사용하는 환경)에는 실제로 개발하는 시간보다 SQL 맵퍼를 통해 쿼리를 작성, 다루는 시간이 더 많았다. 또한 Java를 배우면서 객체지향 프로그래밍을 배웠는데 객체지향 프로그래밍보다는 테이블 모델링에 집중해야했다.
하지만 아직도 Spring, MyBatis 환경을 많이 사용하고 있다.
객체를 테이블에 맞추어 데이터 전달 역할만 하는 형태로 개발을 하다보니 전반적인 Java를 통한 서비스 개발의 방향(패러다임)의 불일치가 발생했다.
이 패러다임 불일치를 해소하고 객체지향 프로그래밍을 데이터베이스에 적용 할 수 있는 JPA 기술을 사용하고자 한다.

JPA 적용해보기

의존성 추가

build.gradle 에 JPA 의존성을 추가하고 테스트용 DB인 H2도 설치해준다.
... dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' // Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' // JPA implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // in-memory H2 DB for testing implementation 'com.h2database:h2' } ...
Java
복사

Posts.java 작성

domain.posts 패키지를 생성하고 내부에 게시글 클래스인 Posts.java를 생성한다.
package com.citefred.ldwspring.domain.posts; import jakarta.persistence.*; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter @NoArgsConstructor @Entity public class Posts { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) 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; } }
Java
복사

@Entity

데이터베이스 테이블과 링크될 클래스임을 나타냄
기본값으로 클래스의 카멜케이스 이름을 언더스코어(스네이크)(_)로 테이블 이름을 매칭
ex) SalesManager.java → sales_manager 테이블

@Id

해당 테이블의 PK 필드를 나타냄

@GeneratedValue

PK의 생성 규칙을 나타냄
스프링 부트 2.0에서는 GenerationType.IDENTITY 옵션을 추가해야만 auto_increment가 적용

@Column

테이블의 컬럼을 나타내며 굳이 선언하지 않더라도 해당 클래스의 필드는 모두 컬럼이 된다.
사용하는 이유는 기본값 외 추가적인 필요 옵션이 있는 경우에 사용
문자열의 경우 VARCHAR(255)가 기본값인데 사이즈를 500으로 늘리고 싶거나, 타입을 varchar가 아닌 더 큰 TEXT로 변경하는 등 사용 함

@Builder

롬복의 어노테이션임 해당 클래스의 빌더 패턴 클래스를 생성
생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함

이전과 다르게 @Setter 메소드가 없다?

Setter는 해당 클래스 인스턴스의 값들이 언제 어디서 변해야하는지 명확하게 구분 할 수 없다 → 값이 변경되는것을 예측 불가 할 수 있음
그래서 Entity 클래스에서는 절대 Setter 메소드를 만들지 않는다.
→ 가끔 테스트 코드작성할때 IDE는 Setter를 만들도록 권장하는데 이것을 조심해야하고 Mock모의 객체 등을 통해 해결해야 한다.

JpaRepository 생성

데이터베이스에 접근하게 해 줄 JpaRepository를 생성해야 한다.
package com.citefred.ldwspring.domain.posts; import org.springframework.data.jpa.repository.JpaRepository; public interface PostsRepository extends JpaRepository<Posts, Long> { }
Java
복사

DB 계층 접근

보통 iBatis, MyBatis 에서 Dao라 불리는 DB Layer 접근자를 JPA에서는 Repository라고 부르며 인터페이스로 생성한다. JpaRepository 클래스를 상속받아 구현한다. 내부적으로 SimpleJpaRepository를 통해서 자동적으로 CRUD 메소드가 생성된다.
@Repository 어노테이션을 추가할 필요도 없다.
주의할 점은 Entity클래스와 기본 Entity Repository는 함께 위치해야 한다. 둘은 밀접한 관계이며 기본 Repository없이는 제대로 역할을 할 수 없기 때문이다.
추후 프로젝트 규모가 커져 도메인별로 분리해야 한다면
Entity클래스와 기본 Repository는 함께 움직여야 하므로 도메인 패키지에서 함께 관리한다.

Spring Data JPA 테스트 코드 작성

PostsRepositoryTest.java 를 통해 JPA 테스트하기
package com.citefred.ldwspring.domain.posts; 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 java.util.List; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(SpringExtension.class) @SpringBootTest public class PostsRepositoryTest { @Autowired PostsRepository postsRepository; //@After @AfterEach public void cleanup(){ postsRepository.deleteAll(); } @Test public void 게시글저장_불러오기(){ //given String title = "테스트 게시글"; String content = "테스트 본문"; postsRepository.save(Posts.builder() .title(title) .content(content) .author("citefred@gmail.com") .build()); //when List<Posts> postsList = postsRepository.findAll(); //then Posts posts = postsList.get(0); assertThat(posts.getTitle().equals(title)); assertThat(posts.getContent().equals(content)); } }
Java
복사

@After , @AfterEach

단위 테스트가 끝날 때 마다 수행되는 메소드를 지정하는 어노테이션
여러 테스트가 동시 수행되면 DB에 데이터가 남아있어 다음 테스트에 실패할 수 있기 때문에 초기화해주는 역할
교재에는 @After가 사용되었지만 JUnit5에서는 @AfterEach 로 변경되었다.

posts.Repository.save()

테이블 posts에 insert/update 쿼리를 실행
id값이 있다면 update가, 없으면 insert 쿼리가 실행

postsRepository.findAll()

테이블 posts에 있는 모든 데이터를 조회(read)

테스트코드 실행

실제 쿼리문 확인하기

application.propertiesspring.jpa.show-sql=true 를 추가하면 JPA로 실행된 쿼리문을 콘솔에서 확인 할 수 있다.
Search
 | Main Page | Category |  Tags | About Me | Contact | Portfolio