JPA/Spring Data JPA

[SpringDataJPA] Auditing

IT록흐 2023. 7. 20. 21:18
반응형

등록날짜, 등록자, 수정날짜, 수정자 데이터는 유지보수에 중요한 요소이다.

 

그러므로 엔티티는 위 요소를 담고 있어야 한다. 그러나 모든 엔티티에 이를 동일하게 생성한다면 엄천난 중복코드를 만들어 내게 된다. 그래서 JPA는 @MappedSuperclass를 제공하여 문제를 해결한다. @MappedSuperclass가 선언된 클래스에 공통속성을 생성하고 엔티티가 이를 상속하면 DB에 레코드가 생성될 때, 공통속성의 컬럼이 생성된다. 

 

 

JPA가 제공하는 Auditing 

@MappedSuperclass
@Getter
public class JpaBaseEntity {

    @Column(updatable = false) //수정 불가
    private LocalDateTime createdDate;
    private LocalDateTime updatedDate;
    
    @PrePersist //콜백 메소드로 초기화
    public void perPersist(){
        LocalDateTime now = LocalDateTime.now();
        createdDate = now;
        updatedDate = now;
    }

    @PreUpdate //콜백 메소드로 초기화
    public void preUpdate(){
        updatedDate = LocalDateTime.now();
    }
}

 

 

코드를 보면, @PrePersist, @PreUpdate 어노테이션을 확인할 수 있다. 이는 콜백 메소드 어노테이션이다.  콜백메소드는 엔티티의 생명주기 중 이벤트 발생시, 처리할 코드를 작성하는 메소드이다. 

 

 

 

엔티티는 여러 상태가 있고 여러 이벤트로 상태가 변화한다. 생명주기 중 이벤트가 발생할 때, 개발자가 이벤트 전후에 관여할 수 있는 환경을 어노테이션 형태로 제공한다. @PrePersist, @PreUpdate 등으로 선언된 메소드는 반환타입은 void여야 하고 이벤트 전후로 호출되어 실행된다. 

 

이런 콜백 어노테이션을 활용하면 개발자는 Auditing을 구현할 수 있다. @PrePersist는 엔티티를 영속화하기 전에 호출되는 콜백 메소드를 의미한다. 영속화되는 시점의 시간을 등록일시, 수정일시로 등록한다. 

 

출처

 

@PreUpdate는 엔티티를 데이터를 변경하기 전에 호출되는 콜백메소드이다. 구글링한 결과, 실제 UPDATE문이 실행되기 전에 시작된다고 한다. JPA가 제공하는 어노테이션을 활용하면 Auditing을 구현할 수 있지만 콜백메소드를 직접 구현해야하는 번거로움이 있다. 스프링데이터JPA는 Auditing을 간단한 코드로 구현한다. 

 

 

SpringDataJPA가 제공하는 Auditing 

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

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;

    @CreatedDate
    @Column(updatable = true)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime updatedDate;
}

 

 

JPA는 콜백메서드를 제공하지만 Auditing 작업은 개발자가 직접 코드로 작성해야 했다. 스프링데이터JPA는 어노테이션으로 Auditing 기능을 지원한다.

 

@CreatedBy : 엔티티 등록시, 등록자가 자동으로 주입된다.

@LastModifiedBy : 엔티티 수정시, 수정자가 자동으로 주입된다.

@CreatedDate : 엔티티 등록시, 등록일자가 자동으로 주입된다.

@LastModifiedDate : 엔티티 수정시, 수정일자가 자동으로 주입된다. 

 

Auditing 어노테이션을 사용하려면 몇 가지 설정이 필요하다. 

 

 

1. @EntityListeners(AuditingEntityListener.class)

 

엔티티에 Auditing에 특화된 엔티티리스너를 등록해야 한다. 

 

 

 

 

AuditingEntityListener 클래스는 등록자, 수정자, 등록일자, 수정일자를 생성하여 어노테이션 형태로 제공하는 엔티티리스너이다. 등록일자와 수정일자는 시간을 제공하는 객체로 데이터를 생성할 수 있지만 등록자, 수정자는 어플리케이션마다 다르다. 그래서 개발자가 등록자, 수정자를 제공하는 로직을 담고 있는 AuditorAware 객체를 Bean으로 등록해야 AuditingHandler가 Bean으로 등록된 AuditorAware 객체를 주입받아 사용할 수 있다. 

 

 

2. AuditorAware Bean으로 등록하기 

@EnableJpaAuditing 
@SpringBootApplication
public class DataJpaApplication {

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

	@Bean //생성자 수정자 생성하는 Bean
	public AuditorAware<String> auditorProvider(){
		return ()-> Optional.of(UUID.randomUUID().toString()); // 랜덤 생성하여 반환하기
	}

}

 

원래는 Config 클래스를 만들어 Bean으로 등록해야 하지만 간단한 프로젝트이니 부트 클래스에서 Bean으로 등록해보겠다.  AuditorAware 인터페이스는 메소드가 하나 뿐인 함수형 인터페이스이다. 함수형 인터페이스는 데이터 전달이 아닌 '로직' 전달이 목적이다. 람다식으로 생성자, 수정자를 생성하는 로직을 표현하고 이를 메소드 그릇에 담아 전달한다. 그러면 AuditingEntityListener는 수정자, 생성자 생성 로직을 사용하여 데이터를 생성해 @CreatedBy, @LastModifiedBy로 선언된 변수에 주입할 수 있다.

 

 

3. @EnableJpaAuditing  

 

@CreatedBy, @LastModifiedBy, @CreatedDate, @LastModifiedDate 어노테이션은  엄밀히 말하면 SpringData 영역에서 제공하는 어노테이션이다.  JPA 프레임워크는 위 어노테이션을 알지 못한다. JPA 프레임워크가 어노테이션을 인식하려면 어노테이션을 등록해주어야 한다. 이는 @EnableJpaAuditing  어노테이션으로 가능하다. 

 

@EnableJpaAuditing 

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(JpaAuditingRegistrar.class) // Auditing 어노테이션 등록 클래스
public @interface EnableJpaAuditing {
	// 중략....
}

 

JpaAuditingRegistrar 클래스는 스프링데이터JPA에서 제공하는 Auditing 기능이 사용가능하도록 환경을 조성한다. @EnableJpaAuditing  어노테이션으로 Auditing의 어노테이션 및 엔티티 생성및 수정시간 추적이 가능해지면 Auditing 데이터 생성이 가능해진다.

 

@EnableJpaAuditing 
@SpringBootApplication
public class DataJpaApplication {

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

	@Bean //생성자 수정자 생성하는 Bean
	public AuditorAware<String> auditorProvider(){
		return ()-> Optional.of(UUID.randomUUID().toString()); // 랜덤 생성하여 반환하기
	}

}

 

위 코드처럼 부트 클래스에 @EnableJpaAuditing를 선언해도 된다. 그러나 프로젝트 규모가 크다면 Config파일로 분리하는 것이 좋다. 

 

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorAware")
public class JpaConfig {
    @Bean
    public AuditorAware<String> auditorAware() {
        return ()-> Optional.of(UUID.randomUUID().toString()); 
    }
}

 

Auditing 전용 컨피그 클래스를 만들어 따로 관리할 수 있다. @EnableJpaAuditing운  auditorAwareRef 속성을 가지고 있어 Bean으로 생성된 AuditorAware 객체의 Bean이름을 저장할 수 있다. 

 

앞서 말했듯, 등록날짜, 등록자, 수정날짜, 수정자는 유지보수에 정말 중요한 요소이다. JPA는 개발자가 Auditing 코드를 작성 할 수 있도록 콜백 메서드를 제공하지만 Auditing 기능을 직접 제공하지는 않는다. Spring Data 영역에서 제공하는 어노테이션을 활용하면 Auditing에 필요한 데이터를 주입할 수 있다. @EnableJpaAuditing 어노테이션으로 스프링데이터JPA가 Auditing 기능을 사용할 수 있도록 활성화 하고 Auditing이 적용될 클래스에 AuditingEntityListener 클래스를 등록하면 생명주기 이벤트 발생시, 원하는 Auditor데이터를 주입할 수 있다. 

 

 

 

+ 번외로 

 

등록일자, 수정일자는 거의 모든 엔티티에 포함되지만 등록자, 수정자는 포함되는 곳이 있고 아닌 곳이 있다. 그러므로 등록자, 수정자는 따로 클래스를 분리하여 등록일자, 수정일자를 상속하는 구조로 구성하는 것이 좋다. 

 

등록자, 수정자 클래스

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity extends BaseTimeEntity {  // 등록일자, 수정일자 클래스 상속

    @CreatedBy
    @Column(updatable = false)
    private String createdBy;

    @LastModifiedBy
    private String lastModifiedBy;
    

}

 

등록일자, 수정일자 클래스

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseTimeEntity {
    @CreatedDate
    @Column(updatable = true)
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime updatedDate;
}

 

 

+ 추가 번외로

 

어노테이션으로 엔티티 하나하나에 AuditorEntityListenener 등록이 번거롭다면 스프링부트의 경우,  META-INF/orm.xml 파일에 아래와 같이 등록하면 된다.

 

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_2.xsd" version="2.2">
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="org.springframework.data.jpa.domain.support.AuditingEntityListener"/>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
</entity-mappings>

 

 


 

 

참고자료

 

Chapter 6. Entity listeners and Callback methods

@PostPersistExecuted after the entity manager persist operation is actually executed or cascaded. This call is invoked after the database INSERT is executed.

docs.jboss.org

 

Implement an entity listener in our SpringBoot application

Some times we need to carry out certain actions before saving a record into the database. In this post, we are going to see how to...

solocoding.dev

 

Enable auditing using Spring Data JPA

Auditing of entities in an application is a crucial part to store information about the updated time and authors of changes made to the…

medium.com

 

 

실전! 스프링 데이터 JPA - 인프런 | 강의

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다.

www.inflearn.com

 

반응형