2024. 3. 12. 01:23ㆍJPA/스프링 데이터 JPA
한국쪽 강의나 프로젝트를 보면 대부분 JpaRepository로 JPA를 사용하는데 해외 강의를 보면 CrudRepository를 상속 받아서 사용하는 편인 것 같다. 둘의 구분이 뭔지 궁금해졌고 스프링 3.X버전을 기준으로 이를 설명해보려고 한다.
[ JpaRepository와 CrudRepository의 관계 ]
JpaRepository는 결론부터 JPA를 사용하기 위해서 정의된 것으로 내부에 ListCrudRepository, ListPagingAndSortingRepository, QueryByExampleExecutor를 상속 받아서 만들어졌다. 이 중 ListCrudRepository는 CrudRespotiroy를 포함한다. 그래서 종합적인 기능은 JpaRepository에 들어있다.
UML을 그려보면 아래와 같다. 인텔리제이 얼티밋 버전이 너무 갖고 싶다.
[ 공통 CRUD 기능에서 CrudRepository와 JpaRepository의 차이 ]
CrudRepository에서 제공하는 기능은 내부 소스 코드를 보면 알겠지만 기본 CRUD와 관련된 기능을 제공한다. 그리고 JpaRepository에서도 이를 제공한다. 그럼 무슨 차이가 있을까?
눈에 띄는 다른 점은 컬렉션 객체를 반환하는 방법이다. CrudRepository에서는 입력하는 값과 반환하는 값들이 Iterable로 되어있다. 아래는 CrudRepository에서의 saveAll이다.
반면 JpaRepository에서는 이걸 전부 List로 치환해놓았다. 이는 ListCrudRepository.java에 들어가서 JpaRepository에서 사용하는 saveAll을 어떻게 오버라이드했는지 확인해보면 알 수 있다.
그래서 Id와 이름 필드만을 가진 Member 엔티티를 각각 CrudRepository로 만든 CrudRepositoryV1과 JpaRepository로 만든 JpaRepositoryV1을 통해 알아보면 실제로 사용할 때 둘 사이의 차이점이 확연하게 느껴진다. JpaRepository가 List로 처리가 되어있어 훨신 사용이 편하다. 특히 반복문은 그렇다쳐도 List의 size나 특정 순서의 객체를 가져오는 차이가 편리함에 있어 차이가 크다고 생각한다.
확인용 코드:
@Service
@RequiredArgsConstructor
public class MemberService {
private final CrudRepositoryV1 crudRepositoryV1;
private final JpaRepositoryV1 jpaRepositoryV1;
private void save(){
Member member1 = new Member("member1");
Member member2 = new Member("member2");
Iterable<Member> members = crudRepositoryV1.saveAll(List.of(member1, member2));
members.forEach((member) -> {
System.out.println("member = " + member.getName());
});
List<Member> memberList = jpaRepositoryV1.saveAll(List.of(member1, member2));
for(Member member: memberList){
System.out.println("member = " + member.getName());
}
}
}
하지만 이런 단점은 직접 스프링 데이터에서 제공해주는 메서드 생성을 통해 사용하면 List로 반환할 수 있게 CrudRepository에서도 만들 수 있으므로 큰 단점은 아니라고 생각한다. 아래의 코드처럼 사용하면 더 익숙한 List로 객체를 받아올 수 있다.
확인용 코드:
public interface CrudRepositoryV1 extends CrudRepository<Member, Long> {
List<Member> findByName(String name);
}
추가적으로 그럼 단일 Member 객체는 어떻게 반환하는지 알고 싶을 수 있는데, 둘 다 Optional로 가져온다. 이는 아래의 코드를 통해 확인할 수 있다.
확인용 코드:
private void findById(){
Optional<Member> byId = crudRepositoryV1.findById(1L);
Optional<Member> byId1 = jpaRepositoryV1.findById(1L);
}
결과적으로 공통적인 기능에서의 차이점을 이야기해보면 CRUD와 관련된 부분은 동일하지만 JpaRepository는 컬렉션을 List로 변환하여 편리한 컬렉션을 제공한다. 정도의 차이가 있는 것 같다.
[ JpaRepository만의 부가적인 기능 ]
앞에서는 공통적인 부분을 알아봤다면 다른 JpaRepository에서 제공하는 부가 기능은 무엇이 있을까? JpaRepository에서는 페이징과 정렬 기능을 추가적으로 제공한다.
다음은 QueryByExampleExecutor에 있는 findAll 함수에 관한 설명이다. 예시와 Pageable 객체를 통해 페이지 사이즈와 정렬 조건에 관해 작성하면 예시에 맞는 Member를 Page 객체에 담아 반환한다.
이 외에도 예시없는 페이징과 단순 정렬 기능도 사용할 수 있다. 이는 ListPagingAndSortingRepository에 가보면 아래처럼 그 내용들이 있다.
[ JpaRepository를 사용하는 게 좋은건가? ]
그건 본인이 어떤 DB를 사용하느냐에 따라 다른 것 같다. 정렬은 그렇다 치더라도 페이징 같은 기능은 MySQL, Oracle 같은 RDBMS를 위한 기능이다. 그래서 JpaRespository를 상속받아서 만드는 리포지토리는 RDBMS 기술에 강하게 의존하게 된다. 만약 추후 해당 리포지토리에 MongoDB 같은 NoSQL를 사용하려면 따로 리포지토리를 작성해야 할 수 있다.
이런 상황을 경험해본 적은 없지만 정답은 없는 것 같다. 상황에 따라서 RDBMS를 계속 쓸 거 같으면 JpaRepository를 사용하고 아니고 페이징을 쓸 일이 없으면 CrudRepository를 선택할 것 같다. 그리고 참고자료의 stackoverflow의 글처럼 추가적으로 필요한 게 있다면 JpaRepository 상위 계층에서 필요한 걸 골라서 상속 받아 사용하는 것도 괜찮은 선택인 것 같다.
[ 참고 자료 ]
https://stratosphere.tistory.com/243
'JPA > 스프링 데이터 JPA' 카테고리의 다른 글
스프링 데이터 JPA - Auditing (0) | 2024.02.02 |
---|---|
스프링 데이터 JPA - Hint(조회용임을 알림) (0) | 2024.02.02 |
스프링 데이터 JPA - Entity 그래프 (0) | 2024.02.02 |
스프링 데이터 JPA - 벌크성 수정 쿼리 (0) | 2024.02.02 |
스프링 데이터 JPA - 페이징, 슬라이스 (0) | 2024.02.01 |