본문 바로가기

개발/Spring&JPA

[JPA] JPA에서 Spring Data의 Audit 기능 적용하기


안녕하세요😎 백엔드 개발자 제임스입니다 :)
오늘은 Spring Data에서 제공하는 Audit 기능에 대해서 알아보도록 하겠습니다. 먼저 audit의 사전적 의미를 안다면 기능을 쉽게 이해할 수 있습니다.

(사전적 의미) Audit : 심사, 감사, 회계 감사

Audit는 '심사'라는 사전적 의미를 갖습니다. 가령 '조직에 문제가 발생했을 때, 외부 기관에서 감사를 진행한다' 할 때와 같이 사용됩니다.
Spring Data 에서 제공하는 Audit 기능은 Database에 값이 변경되었을 때 [누가, 언제] 변경했는지 감사하는 기능입니다. 사실 이것은 사전적 의미입니다. 정확하게는 어떤 역할을 갖으며, 기능 사용은 어떻게 해야 할까요?
이제 자세하게 알아보도록 하겠습니다. :)

1. JPA에서 Spring Data의 Audit 기능 적용 방법


앞서 우리는 Audit를 사전적 의미로 '감사'라고 이해했습니다. 즉, Spring Data JPA가 엔티티의 변화를 추적하는 것인데요. 정확하게는 누가 언제 데이터를 변화시켰는지 추적하는 것입니다. 여기서 변화란, 생성 또는 수정을 의미합니다.
JPA를 사용하면 Entity가 테이블의 역할을 수행합니다. 즉, 필드가 테이블의 Column이 되는 것이죠. 그렇다면 엔티티의 변화를 필드에 어떻게 적용시켜야 할까요?
아래 코드를 보도록 하겠습니다.

// Entity 가 생성되어 db에 저장될 때 시간이 자동으로 저장
@CreatedDate
@Column(updatable = false)
private LocalDateTime createDate;

// 조회한 Entity 의 값을 변경할 때 시간이 자동으로 저장
@LastModifiedDate
@Column
private LocalDateTime editDate;

// Entity를 생성한 User의 ID
@CreatedBy
private Long createdId;

// Entity를 수정한 User ID
@LastModifiedBy
private Long modifiedId;

코드를 보면, Entity를 생성할 때 작성해야 할 중요한 필드(Column)가 있습니다. 보다시피 생성 시간, 수정 시간, 생성자 ID, 수정자 ID를 나타내는데요. 각각의 필드 위에는 @createdDate, @LastModifiedDate, @CreateBy, @LastModifiedBy 애너테이션이 선언된 것을 볼 수 있습니다. 해당 애너테이션들이 Spring Data에서 제공하는 Audit 기능이라 할 수 있습니다. 따라서 직접 값을 할당하지 않아도, 데이터 추가 또는 수정 작업이 이루어지면, 이를 자동으로 추적하여 해당 필드에 저장할 수 있게 되는 것입니다.

@CreatedDate (org.springframework.data) 데이터 생성 날짜 자동 저장 어노테이션
@LastModifiedDate (org.springframework.data) 데이터 수정 날짜 자동 저장 어노테이션
@CreatedBy (org.springframework.data) 데이터 생성자 자동 저장 어노테이션
@LastModifiedBy (org.springframework.data) 데이터 수정자 자동 저장 어노테이션

2. JPA Auditing


이제 Audit를 통해 Data를 유용하게 추적할 수 있다는 것은 알았습니다. 하지만 위 내용대로 필드에 애너테이션만 선언해서는 에러가 발생할 것입니다. 몇 가지 더 추가해야할 코드가 있습니다.
여기서 짚고 갈 부분이 또 있습니다. 만약 해당 필드(Column)들을 모든 Entitiy에 적용시킨다면 어떨까요?
가령 필드(Column)가 많아진다면, 중복되는 코드를 작성하는 것이 쉽지 않을 것입니다. 이때 사용하는 것이 Auditing입니다. 방법은 AOP(Aspect-oriented Programming)처럼 구현하는 것인데요. 이제 위 문제를 해결할 방법과 구현 방법에 대해서 알아보도록 하겠습니다.


1) JPA Auditing 활성화하기

import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
public class ServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }

}

@EnableJpaAuditing

Configuration 애너테이션을 통해 JPA 에서 auditing 을 가능하게 하는 애너테이션입니다. 즉, 데이터의 변화를 추적한 값들을 필드에 적용하기 위해 사용합니다.

2) 모든 Entity에 Audit 필드를 적용하기 위한 클래스 생성

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class Auditable {

    // Entity 가 생성되어 db에 저장될 때 시간이 자동으로 저장
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime regDate;

    // 조회한 Entity 의 값을 변경할 때 시간이 자동으로 저장
    @LastModifiedDate
    @Column
    private LocalDateTime editDate;

    // Entity를 생성한 User의 ID
    @CreatedBy
    private Long createdId;

    // Entity를 수정한 User ID
    @LastModifiedBy
    private Long modifiedId;
}
클래스에 @Getter, @MappedSuperclass, @EntityListeners(AuditingEntityListener.class) 애너테이션을 적용합니다.

@Getter
audit를 적용한 entity의 데이터를 사용할 때 해당 getter를 통해 사용할 수 있습니다.
@MappedSuperclass
해당 애너테이션을 부모 클래스에 정의함으로써, 상속받는 Entity에 공통으로 필드를 제공합니다.
@EntityListeners(javax.persistence)
Entity를 DB에 적용하기 이전 또는 이후에 커스텀 콜백을 요청할 수 있는 애너테이션입니다. 여기서 커스텀 콜백이란 단어가 어려울 수 있습니다.

하이버네이트 문서

먼저 콜백(Callback)은 하이버네이트 문서를 참고하면, 특정 엔티티 생명 주기 이벤트에 대한 알림을 수신하는 재호출 메서드입니다. 다시 말하면, 엔티티와 관련하여 특정한 이벤트가 발생할 시점에 동작하는 메서드입니다. 콜백 메서드는 하이버네이트에 정의되어 있기도 하지만, 직접 구현도 가능합니다. 이렇게 직접 구현한 콜백 메서드가 커스텀 콜백이 됩니다. 아래 이미지는 하이버네이트에서 지원하는 콜백 메서드입니다.

다시 본론으로 돌아오겠습니다.
즉, @EntityListeners(javax.persistence)는 JPA Entity에서 이벤트가 발생할 때마다 특정 로직을 실행시키는 것입니다.
우리가 사용한 @EntityListeners에는 AuditingEntityListener.class가 인자로 사용된 것을 볼 수 있습니다. 이는 스프링에서 이미 커스텀 콜백 메서드를 구현해놓은 EntityListner입니다. 해당 클래스 내부로 들어가면 아래와 같이 @PrePersist와 @PreUpdate가 선언된 메서드가 있습니다. 즉, 이렇게 구현된 메서드 덕분에 Entity에 데이터 변화가 발생할 때, 해당하는 Column에 값을 적용시킬 수 있는 것입니다.

3) Entity에 적용시키기

마지막으로 @MappedSuperclass가 적용한 클래스를 모든 엔티티에 상속받게 합니다. 이제 데이터를 추가하거나 수정하면자동으로[생성 시간, 수정 시간, 생성자, 수정자] 컬럼이 생성되고, 값도 저장되는 것을 볼 수 있습니다.

지금까지 JPA를 사용할 때, Audit 기능을 사용하는 방법에 대해서 알아보았습니다.
사실 적용 방법은 그렇게 어렵지 않습니다. 애너테이션 몇 개 추가하고, 클래스를 상속시켜주는 정도입니다. 하지만 저는 해당 기능을 사용하는 이유어떻게 적용되는지 자세하게 알 필요가 있다고 느꼈습니다. 따라서 이렇게 포스팅 글이 길어졌는데요. JPA는 참 알면 알수록, 재밌고 어려운 기술인 것 같습니다.

반응형