1. 사용자 정의 리 포지토리란
- Spring Data JPA는 인터페이스만 정의하면 구현체를 자동으로 생성해주는 강력한 기능을 제공합니다.
- 하지만 때로는 복잡한 쿼리나 특별한 로직이 필요할 때가 있습니다.
- 사용자 정의 리포지토리는 Spring Data JPA의 기본 기능을 확장하여 개발자가 직접 구현한 메서드를 추가할 수 있게 해줍니다.
정보
실무에서는 주로 QueryDSL이나 Spring JDBC Template과 함께 사용할 때 사용자 정의 리포지토리 기능을 자주 활용합니다.
1.1 사용자 정의 리포지토리가 필요한 경우
- 복잡한 동적 쿼리 작성이 필요할 때
- JPQL로 표현하기 어려운 네이티브 쿼리를 사용해야 할 때
- 여러 엔티티를 조인하여 DTO로 조회해야 할 때
- 성능 최적화를 위한 특별한 쿼리 로직이 필요할 때
2. 기본 사용자 정의 리포지토리 구현
2.1 사용자 정의 인터페이스 정의
public interface MemberRepositoryCustom {
List<MemberTeamDto> search(MemberSearchCondition condition);
}
- 먼저 직접 구현할 메서드를 정의하는 인터페이스를 만듭니다.
- 이 인터페이스를 프래그먼트 인터페이스라고 합니다.
2.2 사용자 정의 인터페이스 구현 클래스 작성
- 구현 클래스의 이름은 반드시 특정 명명 규칙을 따라야 합니다.
리포지토리 인터페이스 이름 + Impl또는사용자 정의 인터페이스 이름 + Impl@Enable<StoreModule>Repositories(repositoryImplementationPostfix = …)를 설정하여 저장소별 접미사를 커스터마이징할 수 있습니다.
@RequiredArgsConstructor
public class MemberRepositoryImpl implements MemberRepositoryCustom {
private final JPAQueryFactory queryFactory;
@Override
public List<MemberTeamDto> search(MemberSearchCondition condition) {
return queryFactory
.select(new QMemberTeamDto(
member.id,
member.username,
member.age,
team.id,
team.name))
.from(member)
.leftJoin(member.team, team)
.where(
usernameEq(condition.getUsername()),
teamNameEq(condition.getTeamName()),
ageGoe(condition.getAgeGoe()),
ageLoe(condition.getAgeLoe())
)
.fetch();
}
private BooleanExpression usernameEq(String username) {
return isEmpty(username) ? null : member.username.eq(username);
}
private BooleanExpression teamNameEq(String teamName) {
return isEmpty(teamName) ? null : team.name.eq(teamName);
}
private BooleanExpression ageGoe(Integer ageGoe) {
return ageGoe == null ? null : member.age.goe(ageGoe);
}
private BooleanExpression ageLoe(Integer ageLoe) {
return ageLoe == null ? null : member.age.loe(ageLoe);
}
}
2.3 메인 리포지토리 인터페이스에서 상속
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {
List<Member> findByUsername(String username);
}
- 리포지토리 인터페이스가 프래그먼트 인터페이스를 확장하도록 합니다.
- JpaRepository 인터페이스와 프래그먼트 인터페이스를 확장하면 CRUD와 커스텀 기능이 결합되어 클라이언트에서 사용할 수 있게 됩니다.
3. Fragment 기반 고급 구현
3.1 Fragment란
- Spring Data는 Fragment라는 개념을 통해 리포지토리를 구성합니다.
- Fragment는 기본 리포지토리, 기능적 측면(QueryDSL 등), 사용자 정의 인터페이스와 구현체를 포함합니다.
- 여러 Fragment를 조합하여 하나의 완전한 리포지토리를 만들 수 있습니다.