프로그래밍/Spring

[Spring] spring-data-jpa Auditing 사용하기

Auditing 

테이블을 설계할 때, 공통적으로 들어가는 컬럼이있는데, 바로

생성일자,수정일자,생성자,수정자 이다.

거의 모든 테이블에 들어가있고 정렬, 필터, 또는 검증? 도 할 수 있는 꽤나 중요한 정보들이다.

하지만 서비스를 요청을 하거나 응답을 줄 때, 필요한 정보가 아니기도하다. 서비스에는 영향이없지만 필요하고 중요한? 데이터를

spring-data-jpa 모듈에서 자동으로 넣을 수 있도록 제공을해준다.

해당 기능이 바로 Auditing이다. 

 


사용하기

1. Main App 클래스에 @EnableJpaAuditing 추가하기

@EnableJpaAuditing
@SpringBootApplication
public class Application {

2. 엔티티 클래스 위에 @EntityListeners (AuditingEntityListener.class) 추가

@Getter
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class BaseEntity {
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    @CreatedDate
    @Column(nullable = false, updatable = false)
    protected LocalDateTime createdDateTime; // 생성일시
    
    @CreatedBy
    @Column(nullable = false, updatable = false, length = 100)
    protected String createdBy; // 생성자
    
    @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
    @LastModifiedDate
    @Column(nullable = false)
    protected LocalDateTime modifiedDateTime; // 수정일시
    
    @LastModifiedBy
    @Column(nullable = false, length = 100)
    protected String modifiedBy; // 수정자

3. AuditorAware 구현체 정의하기

@CreatedBy(생성자), @ModifiedBy(수정자)의 경우 별도의 구현체를 통해 정보를 가져와야합니다.

해당 과정에서 Spring Security 설정이 필요합니다. 

 

저의 경우에는 Jwt 인증 방식으로 구현했기에 토큰 검증을 하는 Filter에서 검증 후 SecurityContextHolder에 인증정보를 넣어줍니다.

@Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String accessToken = jwtProvider.resolveToken(request);

        if (accessToken != null && jwtProvider.validateAccessToken(accessToken)) {
            log.debug("access user");
            // check access token
            AbstractAuthenticationToken auth = (AbstractAuthenticationToken) jwtProvider.getAuthenticationByAccessToken(accessToken);
            
            //인증정보 set
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        filterChain.doFilter(request, response);
    }

저장된 인증정보에서 Id값을 가져와 반환합니다.

@Component
public class AuditingConfig implements AuditorAware<String>{

    @Override
    public Optional<String> getCurrentAuditor() {
        //SecurityContextHolder에 저장된 인증정보  가져오기
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //인증이 안되있거나 익명인 경우 빈값 반환
        if (null == authentication || !authentication.isAuthenticated() || "anonymousUser".equals(authentication.getPrincipal())) {
            return Optional.empty();
        }
        //인증된 유저 정보 가지요기
        UserDetails user = (UserDetails) authentication.getPrincipal();
        //유저 Id 반환
        return Optional.ofNullable(user.getUsername());
    }
}

※ @MappedSuperclass

  • 일반적으로, 상속 관계 매핑 전략에서 부모 클래스와 자식 클래스 모두 DB 테이블과 매핑을 한다.
  • 이와 달리, 부모 클래스를 상속받는 자식클래스에게
    🔑 매핑 정보 속성만 제공하고 싶을때 이 어노테이션을 사용하면 된다.
  • 엔티티 종류에 상관없이 공통으로 가지고 있어야 하는 정보가 있다면 ( ex. 생성시간, 수정시간 등 ) 공통 클래스로 추출하고 이를 상속받는 방식으로 구현할 때 사용 한다.
    DB 테이블과는 상관없다. 아래에 보면 DB는 매핑 정보 다 따로 쓰고 있다. 객체의 입장이다.
  • 그러나 엔티티는 엔티티만 상속받을 수 있기 때문에 엔티티가 아닌 클래스를 상속받기 위해서 @MappedSuperclass 를 사용한다.


Reference

728x90