Book/도메인 주도 개발 시작하기

5장 스프링 데이터 JPA를 시용한 조회 기능

버스는그만 2023. 12. 7. 22:44

5.1 시작에 앞서

CQRS

  • 명령 모델과 조회 모델을 분리하는 패턴
  • 명령  모델: 상태를 변경하는 기능(회원 가입, 비밀번호 변경 등)
  • 조회 모델: 데이터를 조회하는 기능(주문 목록, 주문 상세)
  • 엔티티, 애그리거트, 리포지터리 등 앞에서 보았던 도메인 모델은 주로 명령 모델
  • 리포지터리(도메인 모델)과 DAO(데이터 접근)으로 혼용해서 언급할 것 이다.

5.2 검색을 위한 스펙

검색 조건이 고정되어 있다면 간단하게 만들 수 있지만 다양한 조건을 조합해야 할 때가 있다. JPA의 find 메서드를 정의할 수 있지만 조건이 복잡할 수록 정의해야할 find 메서드도 함께 증가하기 때문에 좋은 방법은 아니다. 이렇게 다양하게 조합해야 할 때 사용할 수 있는 것이 스펙(specification)이다. 스펙은 애그리거트가 특정 조건을 충족하는지 검사할 때 사용하는 인터페이스다. 

public interface Specification<T> {
  public boolean isSatisfiedBy(T agg);
}

isSatisfiedBy 메서드의 agg 파라미터는 검사 대상이 되는 객체이다. 리포지터리에서는 애그리거트 루트가 되고 DAO에서는 검색 결과로 리턴할 데이터 객체가 된다. 리턴값은 검사 대상 객체가 조건을 충족하면 true를, 충족하지 못하면 false를 리턴한다.

5.3 스프링 데이터 JPA를 이용한 스펙 구현

아래는 스프링 JPA에서 제공해주는 Specification 인터페이스로 JPA 크리테리아 API에서 조건을 표현하는 Predicate를 생성한다.

public interface Specification<T> {
  Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}

스펙 구현 클래스를 개별적으로 만들지 않고 별도 클래스에 스펙 생성 기능을 모아도 된다.

5.4 리포지터리/DAO에서 스펙 사용하기

findAll 메서드는 스펙 인터페이스를 파라미터로 갖는다. 레포지터리 인터페이스에서 스펙을 파라미터로 갖는 findAll을 선언 후 스펙 구현체를 사용하면 특정 조건을 충족하는 엔티티를 검색 할 수 있다.

5.5 조합

스프링 데이터 JPA에서는 스펙 인터페이스에서 스펙을 조합할 수 있게 and, or, not 그리고 where를 제공하고 있다.

5.6 정렬 지정하기

스프링 데이터 JPA에서늗 2가지 방법으로 정렬을 지정할 수 있다.

  • 메서드명에 OrderBy 지정: findByIdOrderByNymberDesc(String id)
  • Sort를 인자로 전달: findById(String id, Sort sort)

Sort를 생성 후 정렬 기준(asc, desc)를 정할 수 있으며 2가지 이상의 기준으로 정렬하고 싶으면 and 와 or를 이용해 두 Sort를 연결하면 된다.

5.7 페이징 처리하기

목록을 보여주는 페이징 처리는 Pageable 타입 파라미터를 사용하면 된다. Pageable 타입은 PageRequest 클래스를 이용해 생성한다. Pageable를 파라미터로 사용하면서 리턴 타입이 Page 타입을 사용하면 데이터 목록뿐만 아니라 조건에 해당하는 전체 개수도 구할 수 있으며 findAll 메서드에서도 사용할 수 있다.(Page 타입이면 COUNT 쿼리를 날려서 전체 갯수를 구한다. List일 경우에는 COUNT 쿼리를 날리지 않는다.)

만약 처음을 기준으로 10개가 필요하면 findFirstN, 마지막을 기준이라면 findTopN 등을 사용하면 COUNT 쿼리를 날리지 않고 구할 수 있다.

5.8 스펙 조합을 위한 스펙 빌더 클래스

  • 동적인 쿼리를 생성하기 위해 if else를 사용해서 스펙에 조건을 추가하는 것 보다는 SpecBuilder를 사용하면 조건을 표현하면서 메서드 호출 체은으로 연속된 변수 할당을 줄여 코드 가독성을 높이고 단순한 구조를 유지할 수 있다.

5.9 동적 인스턴스 생성

  • JPQL의 new 키워드 참조
  • 동적 인스턴스의 장점은 JPQL을 그대로 사용하므로 객체 기준으로 쿼리를 작성하면서도 동시에 지연/즉시 로딩과 같은 고민없이 데이터 그대로 조회 가능