Spring Data JPA에서는 엔티티 매니저를 직접 이용해 코드를 작성하지 않아도 된다. 그 대신에 Data Access Object의 역할을 하는 Repository 인터페이스를 설계한 후 사용하는 것만으로도 충분하다. 하지만 엔티티 매니저를 직접 이용하여 코드를 작성할 줄 알아야 JPA가 엔티티를 어떻게 관리하는지 알 수 있기 때문에 이에 대한 사용법도 숙지하고 있어야 한다.
package com.shop.repository;
import com.shop.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ItemRepository extends JpaRepository<Item, Long> {
}
위 코드는 JpaRepository를 상속받는 ItemRepository이다. JpaRepository는 2개의 제네릭 타입을 사용하는데 첫 번째에는 엔티티의 타입 클래스를 넣어주고, 두 번째에는 기본키 타입인 Long을 넣어주었다. JpaRepository는 기본적인 CRUD 및 페이징 처리를 위한 메소드가 정의되어 있다. 메소드 몇 가지를 살펴보면 엔티티를 저장하거나, 삭제, 또는 엔티티의 개수 출력 등의 메소드를 볼 수 있다.
메소드 | 기능 |
---|---|
< S extends T> save(S entity) | 엔티티 저장 및 수정 |
void delete(T entity) | 엔티티 삭제 |
count() | 엔티티 총 개수 반환 |
Iterable findAll() | 모든 엔티티 조회 |
개발을 하다 보면 기획 요건이 변경되어 코드를 수정하거나, 기존의 소스코드를 수정해야 하는 상황이 종종 발생한다. 로직이 복잡하지 않다면 기존 소스를 금방 해석해서 코드를 추가할 수 있을 것이다. 하지만 로직이 복잡할 때 코드 수정 이후 코드가 버그 없이 제대로 동작하는지 테스트 하는 것은 매우 중요하다. 테스트 코드도 유지보수를 해야 하기 때문에 비용이 발생한다. 따라서 의미 있는 테스트 케이스를 작성하고 결과가 예상과 맞는지 검사하는 로직을 작성해야 한다. 가능한 테스트 케이스가 여러 개라면 애플리케이션을 실행하고 테스트하는 것도 시간이 많이 소요되며 테스트 케이스를 놓칠 수도 있다. 잘 만들어진 테스트 케이스는 유지보수 및 소스코드의 안전성을 위해서 중요하다.
package com.shop.repository;
import com.shop.constant.ItemSellStatus;
import com.shop.entity.Item;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
@Test
@DisplayName("상품 저장 테스트")
public void createItemTest() {
Item item = new Item();
item.setItemNm("테스트 상품");
item.setPrice(10_000);
item.setItemDetail("테스트 상품 상세 설명");
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
item.setRegTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
Item savedItem = itemRepository.save(item);
System.out.println(savedItem.toString());
}
}
@SpringBootTest는 통합 테스트를 위해 스프링 부트에서 제공하는 어노테이션이다. 실제 애플리케이션을 구동할 때처럼 모든 Bean을 IoC 컨테이너에 등록한다. 애플리케이션의 규모가 크면 속도가 느려질 수도 있다.
@TestPropertySource는 테스트 코드 실행 시 application.properties에 설정해둔 설정과 같은 설정이 application-test.properties에 존재한다면 application-test.properties에 더 높은 우선순위를 부여해주는 어노테이션이다. 기존에는 MySQL을 사용했지만 테스트 코드 실행 시에는 H2 데이터베이스를 사용하게 된다.
ItemRepository를 사용하기 위해서는 @Autowired 어노테이션을 이용하여 Bean을 주입한다.
@DisplayName 어노테이션은 JUnit5에서 새롭게 추가되었는데 테스트 코드 실행 시 @DisplayName에 지정한 테스트명이 노출된다.
테스트 코드가 실행되면 콘솔창에 실행되는 쿼리문을 볼 수 있다. hibernate_sequence라는 키생성 전용 테이블로부터 다음에 저장할 상품의 기본키(PK)를 가져와서 item 테이블의 기본키 값으로 넣어준다.
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
item
(item_detail, item_nm, item_sell_status, price, reg_time, stock_number, update_time, item_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
insert query문을 따로 작성하지도 않았는데 ItemRepository 인터페이스를 작성했따는 이유만으로 상품 테이블에 데이터를 insert한 것과 같은 결과를 얻을 수 있다. 이것이 가능한 이유는 Spring Data JPA는 인터페이스만 작성하면 런타임 시점에 자바의 Dynamic Proxy를 이용해서 객체를 동적으로 생성해주기 때문이다. 따로 Data Access Object(DAO)와 xml 파일에 쿼리문을 작성하지 않아도 된다.
해당 게시글은 변구훈, 『스프링 부트 쇼핑몰 프로젝트 with JPA』, 로드북, 2021를 참고하여 작성하였습니다.