| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- docker
- Spring Data JPA
- DLQ
- Spring
- redis
- 쿠버네티스
- 컨테이너
- 서블릿 컨테이너
- JdbcTemplate
- DI
- Routing Key
- @ComponentScan
- mybatis
- AWS
- kafka
- 지연 로딩
- JPQL
- securitycontextholderfilter
- Web
- MSA
- JWT
- 스프링 부트
- CORS
- 페이징
- Spring Container
- Dead Letter Queue
- docker compose
- JPA
- dockerhub
- @Transactional
- Today
- Total
look-forest
확장 기능 본문
자동 생성되는 쿼리 대신, 쿼리를 직접 구현하고 싶은 경우엔 어떻게 할까?
- 간단한 쿼리는 @Query와 JPQL로 정의.
- 복잡한 SQL이 필요한 경우 네이티브 쿼리 사용.
- 동적 쿼리에는 QueryDSL이 유용.
- 고급 로직이나 복잡한 비즈니스 요구사항은 Custom Repository로 처리.
사용자 정의 리포지토리 구현
스프링 데이터 JPA 리포지토리는 인터페이스만 정의하고 구현체는 스프링이 자동 생성한다.
그런데 스프링 데이터 JPA가 제공하는 인터페이스를 직접 구현하면 구현해야 할 기능이 너무 많다.
실무에서는 주로 QueryDSL이나 SpringJdbcTemplate을 함께 사용할 때 사용자 정의 리포지토리 기능 자주 사용한다.
Spring data JPA 인터페이스의 모든 메소드가 아니라 특정 메서드만 직접 구현하고 싶다면 다음과 같이 진행하면 된다.
=> JpaRepository와 커스텀Repository(구현체 등록 필)를 둘 다 상속 받는다.
즉, 특정 메소드만 따로 인터페이스를 정의해서 구현하고, 그 인터페이스를 확장하면 된다.
1. 사용자 정의 인터페이스 생성 (MemberRepositoryCustom)
public interface MemberRepositoryCustom {
List<Member> findMemberCustom();
}
2. 사용자 정의 인터페이스 구현 클래스 생성 (MemberRepositoryCustomImpl)
- 규칙: [ 사용자 정의 인터페이스 명 + Impl ] or [ 리포지토리 인터페이스 이름 + Impl ]
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final EntityManager em;
@Override
public List<Member> findMemberCustom() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
3. 사용자 정의 인터페이스 상속
스프링 데이터 JPA가 MemberRepositoryCustom Impl 을 인식해서 그 클래스의 구현 메소드도 사용 가능하게 된다.
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom{
}
★참고
항상 사용자 정의 리포지토리가 필요한 것은 아니다. 그냥 임의의 리포지토리를 만들어도 된다.
가령 MemberQueryRepository를 인터페이스가 아닌 클래스로 만들고 스프링 빈으로 등록해서 그냥 같이 사용해도 된다.
물론 이 경우 스프링 데이터 JPA와는 아무런 관계 없이 별도로 동작한다.
커스텀 리포지토리까지 만들어서 한 리포지토리에 모두 때려넣는 것이 옳다고만은 볼 수 없다.
중요 비즈니스 로직과 화면/API 를 위한 복잡한 로직을 구분하는 것도 가독성에도 좋고, 그 둘은 변경 주기가 다르기 때문에 명확히 분리하는 것이 깔끔하다.
Auditing(감사)
엔티티를 생성, 변경할 때 변경한 사람과 시간을 추적하고 싶으면?
JPA에서 @MappedSupperClass와 @PrePersist, @PreUpdate를 사용할 수 있지만,
스프링 데이터 JPA는 좀 더 직관적이고 간단하게 제공한다. (@CreatedBy, @LastModifiedDate 등)
설정
- @EnableJpaAuditing : 자동 타임스탬프, 사용자 추적 기록
- AuditorAware : 감사 정보를 자동으로 관리하기 위한 인터페이스
- @EntityListeners(AuditingEntityListener.class) : 엔티티 생명 주기 이벤트에 대한 콜백을 처리하는 리스너 지정



※ 참고
저장 시점에 등록일, 등록자는 물론이고, 수정일, 수정자도 같은 데이터가 저장된다. 데이터가 중복 저장되는 것 같지만,
이렇게 해두면 변경 컬럼만 확인해도 마지막에 업데이트한 유저를 확인할 수 있으므로 유지보수 관점에서 편리하다.
이렇게 하지 않으면 변경 컬럼이 null 일때 등록 컬럼을 또 찾아야 한다.
WEB 확장
도메인 클래스 컨버터
HTTP 파라미터로 넘어온 Id로 엔티티 객체를 찾아서 자동으로 바인딩해준다!

# 주의할 점
도메인 클래스 컨버터로 엔티티를 파라미터로 받으면, 이 엔티티는 단순 조회용으로만 사용해야 한다.
트랜잭션이 없는 범위에서 엔티티를 조회했으므로, 엔티티를 변경해도 DB에 반영되지 않는다.
페이징과 정렬
스프링 데이터가 제공하는 페이징과 정렬 기능을 스프링 MVC에서 편리하게 사용할 수 있다.
단순히 Page, Pageable 인터페이스를 파라미터로하면 된다.
/members?page=0&size=3&sort=id,desc&sort=username,desc
위와 같이 요청을 보내면, 스프링 MVC가 자동으로 PageRequest 객체 생성해서 파라미터로 전달한다.

페이지 사이즈
기본 20인데, 변경하고 싶다면 아래와 같이 설정하면 된다


페이징 정보가 둘 이상
@Qualifier 에 접두사명 추가하면 요청 파라미터의 접두사로 구분이 가능하다.

Page 내용을 DTO로 변환하기
엔티티를 API로 노출하면 다양한 문제가 발생한다. 그래서 엔티티를 꼭 DTO로 변환해서 반환해야 한다.
Page는 map() 을 지원해서 내부 데이터를 다른 것으로 변경할 수 있다.
Page<MemberDto> pageDto = page.map(MemberDto::new);
Page를 1부터 시작하기
- Pageable, Page를 파라미터와 응답 값으로 사용히지 않고, 직접 클래스를 만들어서 처리하는 방법
- spring.data.web.pageable.one-indexed-parameters 를 true 로 설정하는 방법
(이 방법은 web에서 page 파라미터를 -1 처리 할 뿐이라, 페이지 관련 메타 정보는 0부터 시작해서 안 맞으니 주의)
참고 자료 & 이미지 출처
실전! 스프링 데이터 JPA (김영한 님)
'JPA > Spring data JPA' 카테고리의 다른 글
| 스프링 데이터 JPA 분석 (0) | 2024.10.20 |
|---|---|
| 다양한 편의 기능 (0) | 2024.10.19 |
| 공통 인터페이스 기능 (0) | 2024.10.19 |