JpaRepository를 상속하지 않는 이유 라는 최범균님의 영상을 보고 포스팅해봅니다.
extends JpaRepository
public interface MemberRepository extends JpaRepository<Member, Long> {
}
Jpa를 학습한 이후로 한 번도 JpaRepository 이외의 인터페이스를 확장해서 사용해본 적이 없었는데요,
최근 최범균님의 JPA 영상을 보다가 Repository 상속을 선호한다는 말씀을 접하게 되었습니다.
왜 그러한지에 대해서도 간명하게 친절하게 설명해주셔서 큰 도움이 되었습니다.
팀 프로젝트 동료들에게도 이 내용에 대해 공유해보았는데요,
모두들 공감해주어서 프로젝트에서도 Repository 상속을 사용하게 되었습니다.
그럼 왜 JpaRepository가 아닌 Repository 상속을 선택하게 되었는지,
어떤 차이가 있는지 알아보겠습니다.
1. 필요한 메시지만 개방한다
객체지향 관점에서 메시지는 협력의 수단입니다.
어떤 객체가 어떠한 메시지를 받을 수 있는지는 그 객체의 타입을 의미하기도 하고,
각각의 메시지는 각각의 책임을 의미하며, 이들이 하나로 모여 역할을 이루게 됩니다.
그런 관점에서 볼 때, 어떤 메시지가 외부로 개방되는지,
외부에서 해당 객체에게 메시지를 보낼 때 어떤 메시지를 보낼 수 있는지는
객체지향 세상에서 꽤나 중요한 문제입니다.
아래 두 경우를 비교해볼까요?
JpaRepository를 상속하게 되면 이미 구현되어 있는 수많은 메시지가 외부에 개방됩니다.
주로 사용하게 될 서비스 레이어에서 해당 리포지토리에 개방된 메시지 자동완성 도움을 받았을 때,
이 리포지토리의 책임들이 무엇이고 역할이 무엇인지 파악할 수 없게 됩니다.
물론 리포지토리니까 당연히 객체들을 컬렉션처럼 보관해주는 역할이겠거니 하고 유추할 순 있지만,
해당 비즈니스 로직에 있어서 이 리포지토리가 어떤 메시지들의 묶음으로 어떤 역할을 갖는지는 알 수 없습니다.
Repository를 상속하게 되면 기본으로 구현되는 메서드가 전혀 없습니다.
사용할 메서드들을 인터페이스 내부에 선언해두어야 하고, 선언된 메서드에 대해서만 외부에 개방됩니다.
서비스 레이어에서 사용할 때, 어떤 메시지를 보낼 수 있는지 한눈에 파악할 수 있게 되고,
비즈니스 로직에서 유의미한 책임들이 무엇이 있는지 파악할 수 있게 됩니다.
이처럼 필요한 메시지만 외부에 개방하는 일은
함께 일하는 동료 개발자들에게 "이 리포지토리는 이런 역할이야" 라고 메시지를 보낼 수 있다는 점에서
상당히 매력적이라고 생각합니다.
2. 명세로서의 인터페이스
인터페이스에는 여러 측면이 있지만, 명세로서의 측면도 빼놓을 수 없습니다.
이러이러한 조건을 충족한다면, 이 타입이라고 인정해줄 수 있다 라던가,
내부 구현은 자유롭게 하되, 이러이러한 조건에 대해서는 필수적으로 구현해야 합니다 라고 제약한다던가
하는 식으로 인터페이스는 명세이자 규약으로써 역할을 하기도 합니다.
명세의 측면에서 JpaRepository와 Repository의 차이를 살펴보겠습니다.
JpaRepository를 상속할 경우, 총 4개의 인터페이스를 상속하는 결과가 만들어지고,
이로 인해 수많은 명세들이 함께 포함됩니다.
그러나 이들은 모두 MemberRepository에 명시되지는 않습니다.
물론 필요한 것만 MemberRepository에 직접 메서드를 작성해둘 수도 있긴 합니다.
그러나 명시되지 않은 메서드들도 외부에 노출된다는 점,
구현체를 만들 때 명시되지 않았더라도 모든 메서드를 구현해야 한다는 점은 여전히 문제로 남게 됩니다.
Repository를 상속할 경우, 완전히 독립적인 명세로서 역할을 할 수 있게 됩니다.
MemberRepository 인터페이스에 선언되어 있는 메서드들만이 명세의 전부입니다.
따라서 이 구현체의 메서드들이 모두 MemberRepository의 책임이고
개방해야 할 메시지들임을 알 수 있으며,
이를 구현하려는 구현체들이 어떤 것을 구현해야 하는지 명세로서 온전한 역할을 할 수 있게 됩니다.
이처럼 인터페이스를 명세라는 관점에서 바라볼 때에도 Repository를 상속하는 것이 매력적으로 느껴졌습니다.
3. 테스트 더블 용이성
테스트 더블의 경우, 대역을 만들기 위한 비용에서도 차이가 크게 발생합니다.
JpaRepository를 상속할 경우, 구현해야 할 메서드가 정말 많아지게 됩니다.
물론 내부를 다 구현하지 않아도 되겠지만 어쨌든 불필요한 코드 라인이 발생하게 되고,
테스트 시점에서도 사용하지 않는 메서드들, 구현되지 않은 메서드들이 외부에 노출됩니다.
Repository를 상속할 경우, 꼭 필요한 메서드만 구현하면 됩니다.
외부에 개방된 모든 메서드들은 비즈니스 로직에서 유의미한 책임들임을 유추할 수 있게 됩니다.
테스트 시점에서도 어떤 메서드를 사용해야 할지 선택이 쉬워집니다.
이처럼 테스트 더블을 이용한 단위 테스트에서도 차이가 발생하게 됩니다.
물론 JPA가 자동으로 구현해주는 메서드들, 쿼리들을 사용할 텐데,
이에 대한 단위 테스트가 필요하냐 라는 생각이 들기도 합니다.
최범균님의 영상에서는 CQRS, 명령 모델의 단위 테스트,
조회 모델에서 명령 메서드 분리 등의 개념이 추가적으로 나오는데요,
이 내용들은 아직 학습되지 않은 내용이라서 테스트 더블을 이용한
단위 테스트의 필요성에 대해 아직 크게 공감하기 어려운 것으로 생각하고 있습니다.
JpaRepository 상속 계층도
JpaRepository는 CrudRepository, PagingAndSortingRepository 등을 상속합니다.
CrudRepository에는 CRUD 관련 메서드들이 선언되어 있고,
PagingAndSortingRepository에는 Sort 객체를 매개변수로 받는 페이징 처리 메서드가 선언되어 있고,
JpaRepository에는 추가적인 편의 메서드들이 많이 선언되어 있습니다.
필요한 최소한의 메서드들만 사용할 수 있게 이처럼 계층을 두어 분리해둔 것으로 이해했는데요,
Repository를 상속해서 정말 필요한 것만 선언해두는 것이 역시 가장 좋은 방법이라 느껴집니다.
혹시 한 번도 살펴본 적 없으셨다면, JpaRepository의 상속 계층 및 선언된 메서드들을 보시면
흥미로울 것 같다는 생각입니다.
결론
- JpaRepository 보다는 Repository를 상속해서 사용
- 필요한 메시지만 개방함으로써 객체의 책임과 역할을 분명히 드러냄
- 인터페이스의 명세로서의 기능을 충분히 누릴 수 있음
- 필요한 메서드만 선언했기에 테스트 더블 생성 시 비용이 절감됨
- 선배님들의 경험과 노하우를 전수받는 것이 정말 학습과 성장에 큰 도움이 됩니다.. ;ㅅ; 👍
학습 출처
[QA] JpaRepository를 상속하지 않은 이유
'JPA & QueryDSL' 카테고리의 다른 글
줍줍 메시지 조회 API 동적 쿼리 리팩터링 (0) | 2022.07.25 |
---|