티스토리 뷰

반응형

본 필자는 회사에서 일하면서 고객의 정보를 변경하면 수정 이력을 나타내는 기능과 수정 했던 사람을 추적해야 하는 기능을 구현해야 했다.

기존에는 직접 업데이트가 될 때 마다 전 후 비교를 통해 시간을 업데이트 시키는 방식으로 복잡하게 비즈니스 로직이 수행되었는데 필자는 이걸 좀 더 수월하게 이용하기 위해서 JPA Auditing 기술을 이용하고자 했다.

JPA Auditing 이란?

데이터베이스에서 누가 수정하고 언제 수정되었냐는 기록은 매우 중요할 수 있는 데이터이다. JPA에서는 이러한 기능을 추적할 수 있는 기능을 제공하고 있는데 그것이 바로 JPA Auditing 기술이다.
이 기술을 사용하면 자동으로 JPA에서 자동으로 생성이나 수정 발생 시 시간을 매핑하여 데이터베이스에 넣어준다.

사용해보자!

보통 요즘은 BaseTimeEntity를 이용하여 상속을 통해 모든 Entity에 해당 속성들을 넣어준다. 그렇다면 코드를 보자

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {

    @CreatedDate
    @Column(name = "create_at", updatable = false)
    private LocalDateTime createAt;

    @LastModifiedDate
    @Column(name = "update_at")
    private LocalDateTime updateAt;

    @CreatedBy
    @Column(name = "created_by", updatable = false)
    private String createdBy;

    @LastModifiedBy
    @Column(name = "updated_by")
    private String updatedBy;

    public LocalDateTime createAt() { return createAt; }

    public LocalDateTime updateAt() { return updateAt; }

    public String createdBy() { return createdBy; }

    public String updatedBy() { return updatedBy; }
}
  1. @EntityListeners : Entity를 DB에 적용하기 이전, 이후에 커스텀 콜백을 적용하기 위한 클래스
  2. class AuditingEntityListener : 영속화 및 업데이트 시 Auditing 정보를 캡처하는 클래스
  3. @CreatedDate : 데이터 생성 날짜를 자동으로 저장해주는 어노테이션
  4. @LastModifiedDate : 데이터 수정 날짜를 자동으로 저장해주는 어노테이션
  5. @CreatedBy : 데이터 생성 시 생성한 사용자 저장
  6. @LastModifiedBy : 데이터 업데이트 시 업데이트한 사용자 저장

물론 원래는 생성/업데이트 시간과 생성한 사용자에 대한 정보는 따로 분리해야 하지만 편의상 하나로 묶었다.

이제 상속을 해줄 BaseTimeEntity에 대한 내부 작성이 끝났다. 이제 사용하기 위해 활성화를 시켜보자

@EnableJpaAuditing // 활성화를 위한 어노테이션
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
  • JPA Auditing 기술을 사용하기 위해선 꼭 해당 어노테이션을 Application 클래스에 적용하여 활성화를 시켜줘야한다.

자 그럼 우린 개발자기 때문에 테스트를 통해 실제 결과를 확인해봐야 한다. 

해당 엔티티는 게시판 엔티티다 현재 BaseEntity를 상속받아 사용한다. 또한 CreatedBy와 lastModifiedBy 정보를 저장하기위해 AuditorAware를 시큐리티에서 사용자 정보를 가져와 이벤트 발생 시 저장하는 클래스를 정의하였다.

@Configuration
public class SpringSecurityAuditorAware implements AuditorAware<Long> {
    @Override
    public Optional<Long> getCurrentAuditor() {
        return Optional.ofNullable(SecurityContextHolder.getContext())
                .map(SecurityContext::getAuthentication)
                .map(authentication -> {
                    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
                    boolean isUser = authorities.contains(new SimpleGrantedAuthority("USER"));
                    CustomUserDetails principal = (CustomUserDetails) authentication.getPrincipal();

                    return isUser ? principal.getId() : null;
                });
    }
}

각 CreatedBy와 LastModifiedBy에는 사용자의 식별값(PK)이 들어갈 예정이다. 그럼 테스트 결과를 보자

@Test
@DisplayName("게시판 정보를 수정하고 수정 날짜와 수정자를 기록한다.")
@WithAuthUser
void update() {
    // given
    BoardUpdateRequest request =
            BoardUpdateRequest.of(Title.from("수정된 게시판 제목입니다."), Content.from("안녕하세요 수정된 게시판 내용입니다."));

    // when
    boardService.update(request.toEntity(), 1L, 1L);
    Board board = boardRepository.findById(1L).orElseThrow(
            () -> new BoardNotFoundException(ErrorCode.BOARD_NOT_FOUND));

    // then
    assertThat(board.getTitle()).isEqualTo(Title.from("수정된 게시판 제목입니다."));
    assertThat(board.getContent()).isEqualTo(Content.from("안녕하세요 수정된 게시판 내용입니다."));
    assertThat(board.getLastModifiedBy()).isEqualTo(1L);
    assertThat(board.getLastModifiedTime()).isAfter(board.getCreateTime());
}

다음 코드로 테스트를 진행할 예정이다. 

테스트는 잘 성공한다!!!

쿼리도 잘 나가는 것을 알 수 있다. !! 

 

DB도 결과적으로 잘 나오는 것을 알 수 있다!!!

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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 31
글 보관함