Spring Data JPA Auditing으로 엔티티의 생성/수정 시각 자동으로 기록

@Hudi· August 26, 2022 · 2 min read

Auditing?

Audit은 사전적 의미로 감사하다, 심사하다 등의 의미를 가지고 있다. Spring Data JPA에서는 Auditing이라는 기능을 제공한다. 이를 사용하여 엔티티가 생성되고, 변경되는 그 시점을 감지하여 생성시각, 수정시각, 생성한 사람, 수정한 사람을 기록할 수 있다.

특히 서비스를 운영할 때 데이터가 생성되고, 수정한 일자를 기록하고 트래킹하는 것은 중요하다. 이번 포스팅에서는 Spring Data JPA Auditing을 사용해서 엔티티의 생성시각, 수정시각을 자동으로 기록하는 방법에 대해서 알아본다.

@EnableJpaAuditing

@SpringBootApplication
@EnableJpaAuditing
public class SampleApplication {

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

우선 @EnableJpaAuditing 어노테이션을 사용하여, Auditing을 활성화 해야한다. Application 클래스에 붙이거나, @Configuration 어노테이션이 사용된 클래스에 붙이면 된다.

엔티티 코드 작성

Post 엔티티

@Getter
@NoArgsConstructor
@EntityListeners(AuditingEntityListener.class)
@Entity
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String content;

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

    public Post(final String title, final String content) {
        this.title = title;
        this.content = content;
    }
}

@Getter@NoArgsConstructor 는 lombok에서 제공하는 어노테이션이다.

@EntityListeners

우선 Auditing을 적용할 엔티티 클래스에 @EntityListeners 어노테이션을 적용해야한다. 해당 어노테이션은 엔티티의 변화를 감지하여 엔티티와 매핑된 테이블의 데이터를 조작한다.

이 어노테이션의 파라미터에 이벤트 리스너를 넣어줘야하는데, 여기에 AuditingEntityListener 클래스를 넣어준다. 이 클래스는 Spring Data JPA에서 제공하는 이벤트 리스너로 엔티티의 영속, 수정 이벤트를 감지하는 역할을 한다.

@CreateDate

생성일을 기록하기 위해 LocalDateTime 타입의 필드에 @CreateDate 를 적용한다. 또한 생성일자는 수정되어서는 안되므로 @Column(updatable = false) 를 적용한다. 이렇게 적용하면, 엔티티가 생성됨을 감지하고 그 시점을 createdAt 필드에 기록한다.

@LastModifiedDate

수정일을 기록하기 위해 LocalDateTime 타입의 필드에 @LastModifiedDate 를 적용한다. 이렇게 적용하면, 엔티티가 수정됨을 감지하고 그 시점을 updatedAt 필드에 기록한다.

사실 Auditing 적용은 이것이 끝이다. 직접 엔티티를 생성하고, 조회해보면 createAtupdatedAt에 시각이 잘 들어가는 것을 확인할 수 있다.

BaseEntity로 분리하기

사실 생성시각과 수정시각은 대부분의 엔티티에서 사용되는 필드일 것이다. 따라서 이를 별개의 엔티티 클래스로 분리하고, 다른 엔티티에서 상속을 받아 사용하면 많은 중복 코드를 없앨 수 있을 것이다.

BaseEntity

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

    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

@EntityListners 어노테이션, createdAt, updatedAt 필드를 Post 클래스와 똑같이 가져온다.

@MappedSuperclass 어노테이션은 공통 매핑 정보가 필요할 때 부모 클래스에 선언된 필드를 상속받는 클래스에서 그대로 사용할 때 사용한다. 이때, 부모 클래스에 대한 테이블은 별도로 생성되지 않는다.

Post

@Getter
@NoArgsConstructor
@Entity
public class Post extends BaseEntity {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    private String content;

    public Post(final String title, final String content) {
        this.title = title;
        this.content = content;
    }
}

@EntityListeners 어노테이션과, createdAt, updatedAt 필드를 제거한다. 그리고 방금 만든 BaseEntity 클래스를 상속받는다.

이렇게되면 중복코드를 없애면서, 여러 엔티티에서 생성시각과 수정시각을 쉽게 추적할 수 있다.

@Hudi
꾸준히, 의미있는 학습을 기록하기 위한 공간입니다.