Spring/JPA

[Spring/JPA] JPA Auditing

오잎 클로버 2023. 2. 17. 11:19
728x90

JPA Auditing

INSERT, UPDATE, DELETE 의 SQL 를 추적하여, 영속 상태인 엔티티의 엔티티 버전 관리와 관련된 이벤트들을
  추적 및 로깅하는 기능이다.

순수 JPA 에는 Auditing (추적)할 수 없다. (javax(현재는 jakarta)에 직접적으로 명시되어있지 않다.)

하지만 엔티티 라이프 사이클을 사용하여 구현할 수 있다.

(엔티티 라이프 사이클은 다음 포스트에서 자세히 다루겠다.)

 

@PrePersist, @PreUpdate, @PreRemove

@Entity
@Getter
@Builder
@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Column(nullable = false)
    private String name;

    @Setter(AccessLevel.PROTECTED)
    @Column(nullable = false, updatable = false)
    private LocalDate createAt;
    
    @Builder.Default
    @OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST, orphanRemoval = true)
    private List<Todo> todoList = new ArrayList<>();

    @PrePersist
    public void onPrePersist() { ... }

    @PreUpdate
    public void onPreUpdate() { ... }

    @PreRemove
    public void onPreRemove() { ... }
    
    ...
}

위와 같이 메소드들을 정의하여, 각각 @PrePersist, @PreUpdate, @PreRemove 가 발생할 경우, 해당 메소드를
  사용하도 할 수 있다.

여기서 한 가지 유의해야할 점은 해당 메소드는 반드시 void 를 return 해야하며, 인자는 없어야한다. 또, static 여서는
  안되고, public 이어야한다.

그렇다면 Member 를 JPA 를 통해 저장할 때 자동으로 생성 시간을 저장하기 위해서는 다음과 같이 정의하면 된다.

(물론, DDL 에서부터 DEFAULT 로 시간을 지정해주는 방법도 있지만, 그 외에 특정 값 등을 넣어야하는 특수 상황이
  있을 수 도 있다.)

@PrePersist
public void onPrePersist() {
    setCreateAt(LocalDate.now());
}

setter 를 통해 주입을 해주는 방식이다. 이때, 현재 엔티티는 createAt 를 updatable = false, 즉 수정하지 못하게 하였기에 

  .setter 를 protected 로 지정하였다.

그 후, 엔티티 클래스에 @EntityListeners 어노테이션에 리스닝 클래스를 작성해주면 된다.

@EntityListeners(AuditingListener.class)
public class Member {
    ...
}

public class AuditingListener {

    @PrePersist
    @PreUpdate
    @PreRemove
    public void preInvoke(Object target) { ... }

}

추가로 기본으로 제공해주는 클래스역시 없기에 직접 구현해야한다.

이렇듯 Hibernate 와 같은 JPA 구현체 없이 순수 JPA 명세만을 가지고 구현한다는 것은 매우 어렵고 번거로운 작업이다.

 

Spring Data JPA 에서 Auditing

※ Hibernate 의 envers 라이브러리 통해 Spring Data JPA 없이도 Auditing 이 가능하나, 스프링을 중점적으로 공부하고자       제외하였습니다.

우선 Spring Data JPA(이하 Spring JPA)에서 Entity Auditing 을 하기 위해서는 @Configuration 클래스에                                @EnableJpaAuditing 어노테이션을 반드시 필요로 합니다. 그리고 @EntityListeners 어노테이션을 엔티티 클래스에
  추가한다. (순수 JPA에서 구현할 때와 달리, @Pre~ 인 어노테이션을 굳이 사용하지 않아도 됩니다.)

@EntityListeners 에는 AuditingEntityListener.class 로 설정합니다. 해당 클래스는 Auditing 기능을 포함한
    리스너 클래스이다.

앞서 설명한 @Pre~ 어노테이션 대신 Spring 에서 제공하는 어노테이션들을 사용하여 미리 필요한 기능을 구현할 수 있다. (만일 제공하지 않을 경우, 순수 JPA 에서 했던 것처럼 구현하면 됩니다.)

 

@CreatedDate, @LastModifedDate

최초 생성 날짜, 최근 수정 날짜를 특정 칼럼에 넣도록 제공해주는 스프링 Data 어노테이션들이다.

매우 편리하지만, 엔티티에 공통으로 들어가야할 때에는 번거로움이 발생할 수 있는 데, 이를 해결하는 방법은 @MappedSuperClass 를 사용하는 방법이다. 이는 JPA Entity 클래스들이 해당 추상 클래스를 상속할 경우, 해당 추상 클래스에 있는 칼럼 그대로 추가된다는 점이 있다. 자세한 내용은 해당 글을 참고하자.

참고로 트랜잭션 커밋 시점에서 플러시가 호출될 때, 하이버네이트가 자동으로 시간 값을 추가해주는 원리이기에
  실수로라도 insertable = false 하면 예외가 발생하니 주의하자.

 

@CreateBy, @LastModifedBy

이 둘 어노테이션들은 JavaDoc 을 읽어보면 나오듯이 보안과 관련된, 즉 Spring Security 를 사용할 때 사용하는
  어노테이션들이다. AuditorAware 인터페이스를 사용하여 현재 보안 주체(접근한 주체)를 반환하게 하여,
  이를 통해 auditorAwareRef 로 설정하여, 적용할 수 있다.

@CreateBy

  • 해당 엔티티가 생성될 때, 생성하는 사람(보안 주체)이 누구인지를 삽입한다.

@LastModifiedBy

  • 해당 엔티티가 수정될 때, 수정하는 사람(보안 주체)가 누구인지를 삽입한다.

 

 

이상입니다.


Reference


https://www.baeldung.com/database-auditing-jpa