Spring/Spring Data

[Spring Data] JPA의 EnableJpaAuditing으로 Entity 필드 관리

jundyu 2024. 11. 3. 20:24
Spring Data

 

들어가며

반려동물 쇼핑몰 프로젝트에서 JPA를 이용한 Entity 클래스 생성을 도맡았습니다. 프로젝트의 규모가 생각보다 커져서 테이블이 10개 넘게 나와서 Entity의 필드 정의하는데 시간이 꽤 걸렸고, 끝나고 나니 중복되는 필드들이 많았습니다. 중복되는 필드에 대해 하나의 글로벌 클래스에서 처리할 수 있는 방법이 없을까 고민하다가 추상 클래스를 만들고 상속 받도록 했습니다. 그 중에 생성 일시 필드는 객체가 생성될 때 자동으로 값이 할당되면 좋을 것 같아서 이리저리 방법을 찾아봤습니다.
 
결국, Github에서 다른 사람들의 Repository 중 JPA의 EnableJpaAuditing Annotation으로 Auditing 기능을 활성화해서 Entity의 생명 주기를 자동으로 관리하도록 만든 것을 발견해 저도 이 방식을 적용해봤습니다. 각각의 Annotation에 대한 간략한 설명과 예시 코드를 통해 이번 주제에 대해 설명하겠습니다. 또한, 이번 글은 Entity의 Life Cycle에 대한 사전 지식이 있어야 이해하기 쉬울 것 같아 아래에 관련 글 참고바랍니다.

(링크 추가 예정)

 


 

@EnalbeJpaAuditing

일단 자동으로 Entity의 값을 추가하려면 EnableJpaAuditing 어노테이션을 통해 Auditing 기능을 활성화 해야합니다. EnableJpaAuditing 어노테이션은 JPA의 Auditing 기능을 활성화 시켜주는데, 조금 더 와닿게 설명하자면 Entity의 생명 주기 이벤트(PrePersist)를 활용하여 자동으로 생성일시, 수정일시 등을 관리할 수 있도록 합니다. @CreatedDate@LastModifiedDate 등의 Annotation으로 생성, 수정 일시를 자동으로 저장할 수 있지만 저는 생성 시각만 필요해서 @CreatedDate만 사용했습니다.

R : EnableJpaAuditing의 표준 라이브러리 L : Application에서 Auditing 기능 활성화

@interface로 EnableJpaAuditing Annotation의 인터페이스를 정의했습니다. 그리고 Application 클래스의 레벨에서 해당 Annotation을 적용한 모습입니다. 여기서 눈 여겨서 봐야할 곳은 메타 Annotation인 @Target@Retention입니다. @Target을 통해서 Annotation을 사용할 레벨을 정해주고, @Retention을 통해 Annotation의 지속 시간을 정해줍니다.
 

1. @Target({ElementType.TYPE})

표준 라이브러리에는 @TargetElementType.TYPE으로 정의되어 있습니다. TYPE은 class, interface, enum에 사용할 수 있는 속성입니다. @EnableJpaAuditing@TargetTYPE이기 때문에 메인 클래스(PetpickApplication)에 적용해줬습니다. 이렇게 메인 클래스에 @EnableJpaAuditing를 적용하면 Auditing 기능을 전역에서 사용할 수 있도록 AuditingEntityListener를 자동으로 등록합니다.
 
 참고로 자주 사용되는 @Target의 값은 다음과 같습니다.

타입대상
ANNOTATION_TYPE어노테이션
CONSTRUCTOR생성자
FIELD멤버 변수, ENUM 상수
LOCAL_VARIABLE지역 변수
METHOD메서드
PACKAGE패키지
PARAMETER매개변수
TYPEclass, interface, enum

 

2. @Retention({RetentionPolicy.RUNTIME})

@Retention은 유효 기간을 정하는 메타 어노테이션입니다. 언제까지 Annotation이 유지되고, 어디에서 접근을 할 수 있는지 지정합니다. 표준 라이브러리에서 @EnableJpaAuditing@Retention을 RUNTIME이라고 지정한 이유는 애플리케이션 실행 후 Entity가 DB에 저장될 때마다 @CreatedDate 같은 어노테이션이 실행되야 하기 때문입니다.
 
아래는 @Retention의 값으로 사용되는 enum 타입인 RetentionPolicy의 값들입니다.

ENUM 값설명사용 예시
SOURCE소스코드에만 유지되고, 컴파일 시 제거@Override
CLASS바이트 코드에는 포함되지만, 런타임에 로드 X바이트코드 분석
RUNTIME런타임까지 유지되고 리플렉션으로 접근 가능@Autowired

 
 

@EntityListeners(AuditingEntityListener.class)

Entity Listener는 JPA에서 엔티티의 생명 주기 이벤트를 감지하고 특정 로직을 실행할 수 있게 해주는 콜백 메서드를 정의한 클래스입니다. @MappedSuperclass로 BaseTime을 상속 받는 Entity가 컬럼을 추가로 받게 되고, 객체가 DB에 저장될 때 @CreatedDate가 붙어 있는 필드에 자동으로 현재 시각을 저장해줍니다.
 
아래는 실제 프로젝트에 사용했던 생성일시를 자동으로 생성해주는 BaseEntity 클래스의 코드입니다.

@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTime {
    @CreatedDate
    @Column(name = "create_at",updatable = false)
    private LocalDateTime createAt;

}

 

1. @MappedSuperclass

JPA의 @MappedSuperclass 어노테이션은 해당 클래스를 상속 받는 모든 클래스에서 BaseTime 클래스에 정의된 필드를 상속 받게 됩니다. @MappedSuperclass로 선언된 클래스는 자체적으로 Entity의 속성을 가지지 않기 때문에 Table이 생성되지도 않고 쿼리의 영향을 받지도 않습니다.
 

2. @CreatedDate

BaseTime 클래스를 상속 받은 Entity는 BaseTime에 정의된 필드까지 컬럼에 추가한다고 했습니다. 따라서 createAt이라는 LocalDateTime을 가지게 됩니다. createAt 필드 위에 @CreatedDate Annotation이 추가된 것을 볼 수 있는데 AuditingEntityListener는 Entity가 DB에 저장될 때 @CreatedDate가 붙어 있는 컬럼에 값을 자동으로 할당합니다. 즉, createAt이 최초로 저장되는 시점에 AuditingEntityListener@PrePersist 이벤트를 감지하고 @CreatedDate 어노테이션이 붙은 필드에 현재 시각을 할당하게 됩니다.
 

3. @LastModifiedDate

추가로 생성일 말고 수정일에 관한 어노테이션도 있습니다. @LastModifiedDate을 사용하면 AuditingEntityListener@PreUpdate 라는 생명주기 이벤트를 감지해서 엔티티의 필드가 수정될 때마다 현재 시각을 자동으로 할당합니다. @PreUpdate는 수정 이벤트이지만 최초엔 @PrePersist 이벤트와 동시에 발생해 최초 값이 할당됩니다.
 
 

.
.
.

 

마치며

Spring Data JPA의 기능을 또 하나 알게 되었습니다! 저는 단순히 특정 도구의 사용법을 아는 것이 아니라 왜 사용했는지, 그리고 어떻게 동작하는지를 아는게 더 중요하다고 생각합니다. 이번 프로젝트의 기간이 너무 짧아서 이 원칙을 유지하기는 힘들었지만 프로젝트의 마무리 단계인 지금부터 천천히 새로 배운 내용들을 정리해나갈 생각입니다.
 

'Spring > Spring Data' 카테고리의 다른 글

[Spring Data] JPA를 활용해 pagination 구현하기  (1) 2024.11.15