Spring/강의

[📕 JPA 심화] 5. SpringData JPA 심화

가지코딩 2025. 6. 26. 15:31

📕 목차

  1. 좀 더 멋지게 쿼리 생성하기 (QueryDSL)
  2. 테이블 객체 방명록 설정하기 (Auditing)
  3. 필요한 부분만 갱신하기 (Dynamic Insert/Update)

❤️ 학습 목표

  • QueryDSL을 사용하여 타입 안전 쿼리를 작성하는 방법을 배웁니다. 복잡한 조건과 정교한 쿼리를 쉽게 구성할 수 있는 기법을 소개합니다.
  • Auditing을 사용하여 엔티티의 생성 및 수정 시간을 자동으로 관리하는 방법을 배웁니다. Spring Data JPA에서 제공하는 Auditing 기능을 통해 데이터의 일관성과 추적성을 확보합니다.
  • Projection을 사용하여 필요한 데이터만 선택적으로 조회하는 방법을 배웁니다. 과도한 데이터 로드를 방지하고 성능을 최적화하는 기법을 학습합니다.

1. 좀 더 멋지게 쿼리 생성하기 (QueryDSL)

QueryDSL

  • 타입 안전한 쿼리를 자바 코드로 작성할 수 있게 도와주는 프레임워크
  • 문자열 기반 JPQL의 단점을 보완 (오타, 런타임 오류 등)

 

Q 클래스

  • QueryDSL이 작동하기 위한 기반이 되는 클래스
  • Entity 기반으로 자동 생성되는 쿼리 전용 클래스
  • 내부에 엔티티의 필드를 포함하고 있어, 객체처럼 접근 가능: QUser user = QUser.user;

 

JPAQueryFactory

  • QueryDSL 쿼리를 생성하고 실행하는 핵심 클래스
  • 내부적으로 EntityManager를 사용
  • selectFrom(), where(), fetch() 등 메서드 제공

 

JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QUser user = QUser.user;

List<User> result = queryFactory
    .selectFrom(user)
    .where(user.username.eq("teasun"))
    .fetch();

2. 테이블 객체 방명록 설정하기 (Auditing)

Auditing

  • 엔티티의 생성자/수정자, 생성일/수정일을 자동으로 기록해주는 기능
  • ex: “누가 언제 만들고, 언제 수정했는가”를 자동으로 관리

 

Auditing 적용 방법

 

1. @EnableJpaAuditing 설정

  • 메인 클래스에 추가
@EnableJpaAuditing(auditorAwareRef = "userAuditorAware")
@SpringBootApplication
public class Application { ... }

 

2. 엔티티에 Auditing 설정

  • 공통 속성을 가지는 부모 엔티티에 설정
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class TimeStamp {

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime modifiedAt;

    @CreatedBy
    @ManyToOne
    private User createdBy;

    @LastModifiedBy
    @ManyToOne
    private User modifiedBy;
}

 

 

3. AuditorAware 구현

  • 인증된 사용자를 반환하는 로직 구현
  • @CreatedBy, @LastModifiedBy는 AuditorAware 구현이 반드시 필요함
  • @CreatedDate, @LastModifiedDate는 자동으로 동작함
@Service
public class UserAuditorAware implements AuditorAware<User> {

    @Override
    public Optional<User> getCurrentAuditor() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        if (auth == null || !auth.isAuthenticated()) {
            return Optional.empty();
        }

        return Optional.of(((UserDetailsImpl) auth.getPrincipal()).getUser());
    }
}

 

 

4. SecurityContextHolder에 인증 정보 넣기 (예: JwtFilter)

public void setAuthentication(String username) {
    SecurityContext context = SecurityContextHolder.createEmptyContext();
    Authentication auth = jwtUtil.createAuthentication(username);
    context.setAuthentication(auth);
    SecurityContextHolder.setContext(context);
}

 

 

 

전체 흐름 요약

  • 사용자 로그인 후 → SecurityContextHolder에 인증 정보 저장
  • 엔티티 저장 시 → AuditorAware가 현재 사용자 반환
  • @CreatedBy, @LastModifiedBy에 사용자 정보 자동 저장
  • @CreatedDate, @LastModifiedDate는 자동 시간 기록

3. 필요한 부분만 갱신하기 (Dynamic Insert/Update)

@DynamicInsert

  • 정의: INSERT 시 null 값인 필드를 쿼리에서 제외
  • 사용 위치: 엔티티 클래스에 @DynamicInsert 어노테이션 추가
  • 효과: 기본값이 DB에 설정된 컬럼에 null이 덮어씌워지는 것을 방지
  • 쿼리 최적화: 불필요한 칼럼 제외로 SQL 간결화 및 성능 향상
@DynamicInsert
@Entity
public class User { ... }

 

 

@DynamicUpdate

  • 정의: UPDATE 시 변경된 필드만 쿼리로 생성
  • 사용 위치: 엔티티 클래스에 @DynamicUpdate 어노테이션 추가
  • 효과: 변경되지 않은 필드에 대한 불필요한 업데이트를 방지
  • 쿼리 최적화: 업데이트 대상 컬럼이 적을수록 효과 증가
@DynamicUpdate
@Entity
public class User { ... }

 

 

* 두 어노테이션 모두 Hibernate 전용 기능이며, 필드 수가 많을수록 성능 차이가 커진다. 특히 DB 기본값을 활용하거나 변경 감지 기반 최적화가 중요한 경우 유용하다.

 

 

 


👏 👏 👏  JPA 심화 강의 완강  👏 👏 👏