[JPA] AuditingEntityListener 사용 시 주의점

김형진·2023년 7월 18일
0

BaseEntity + AuditingEntityListener

대다수의 엔티티가 사용하는
createdDate, modifiedDate, createdBy, lastModifiedBy 필드는
BaseEntity 추상클래스를 만들어 상속받아 사용하는 것이 일반적이다.

JPA에서는 AuditingEntityListener를 사용하여 엔티티가 생성되거나 수정될 때 위와 같은 필드들의 값을 자동으로 채워주는 기능을 제공한다.

TimeBaseEntity 와 UserBaseEntity

@MappedSuperclass
@Getter
@EntityListeners(AuditingEntityListener.class)
public abstract class TimeBaseEntity {

    @CreatedDate
    private LocalDateTime createdDate;
    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}
@MappedSuperclass
@Getter
public abstract class UserBaseEntity extends TimeBaseEntity{

    @CreatedBy
    private String createUser;
    @LastModifiedBy
    private String lastModifyingUser;
}

네 개의 필드를 한 번에 가진 BaseEntity를 만들어도 되겠지만, 내가 개발 중인 어플리케이션의 경우 createUser, lastModifyingUser 가 필수로 들어가지 않는 엔티티들도 있기 때문에 TimeBaseEntity와 UserEntity를 분리하였으며

다이아몬드 문제를 발생시키지 않기 위해 다중상속이 불가하며 UserBaseEntity를 사용하는 경우 TimeBaseEntity의 필드도 필수로 들어가는 점 때문에 UserBaseEntity가 필요한 경우 TimeBaseEntity를 상속하여 네 개의 필드에 대한 관리를 한 번에 할 수 있게 하였다.

그리고 TimeBaseEntity에 엔티티 리스너 설정을 하여 JPA를 통해 네 개의 필드들이 자동으로 관리될 수 있도록 하였다.

AuditingEntityListener 와 AuditorAware의 구현

엔티티가 생성되거나 수정될 때 엔티티가 DB에 반영되기 이전에 이 두 메소드가 호출이 되고, 두 메소드는 내부적으로 무조건getAuditor라는 메소드를 호출하게 된다.
그리고 그 구현은 아래와 같은데, 엔티티 생성 및 수정한 주체의 정보를 얻기 위한 것으로 개발자가 직접 구현해야 한다.

 
public class AuditorAwareImpl implements AuditorAware<String> {

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

        if (Objects.isNull(authentication)|| !authentication.isAuthenticated()) {
            return Optional.empty();
        }

        UserDetails user = (UserDetails) authentication.getPrincipal();
        return Optional.of(user.getUsername());
    }
}

문제상황 - User 엔티티에서는 이벤트 리스너 사용불가

User 엔티티에서도 JPA를 이용하여 관리하기 위해 BaseEntity를 상속받아 사용하였다. 단, UserEntity가 생성되는 시점에서는 세션정보가 없으며 다른 누군가에 의해 생성된다는 개념이 없기 때문에 UserBaseEntity가 아닌 TimeBaseEnity만 상속받도록 하였다.

그러나 문제는, JPA에서는 @CreatedBy, @LastModifiedBy 가 아닌 @CreatedDate, @LastModifiedDate 만 사용하는 상황에서도 무조건 getCurrentAuditor를 호출하여 현재 로그인된 사용자의 정보를 찾고자 한다는 것이다.

SecurityContext에서는 당연히 현재 로그인한 사용자의 정보를 찾을 수 없고, 그 결과 getPrincipal메소드 호출 시 "anonymous"라는 String이 리턴되어 이후 로직에서 에러가 발생하였다.

해결책?

getCurrentAuditor에서 위와 같은 상황을 처리하는 로직을 추가할 수도 있겠지만
단 하나의 상황만을 위해 구현을 복잡하게 하거나 이후에 혹시 발생할 수 있는 문제를 생각하여 User엔티티에서는 엔티티 리스너를 사용하지 않고 createdDate라는 컬럼을 따로 두었다.

profile
히히

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

정말 유익한 글이었습니다.

답글 달기