애플리케이션에서 엔티티를 다루다 보면 등록일, 수정일은 거의 필수적으로 필요하다.
“이 데이터가 언제 만들어졌는지”, “마지막으로 누가 수정했는지”는 유지보수와 이력 관리에서 중요한 정보이기 때문이다.
따라서 엔티티에 기본적으로 등록일(createdDate), 수정일(lastModifiedDate), 등록자(createdBy), 수정자(lastModifiedBy) 같은 정보를 추적할 수 있어야 한다.
이를 어떻게 구현할 수 있을까?
1. 순수 JPA로 Auditing 구현하기
순수 JPA에서도 엔티티 생명주기 이벤트(@PrePersist, @PreUpdate)를 활용하면 간단히 등록일과 수정일을 기록할 수 있다.
@MappedSuperclass
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createDate;
private LocalDateTime updateDate;
@PrePersist
public void prePersist() {
createDate = LocalDateTime.now();
updateDate = LocalDateTime.now();
}
@PreUpdate
public void preUpdate() {
updateDate = LocalDateTime.now();
}
}
이제 엔티티가 JpaBaseEntity를 상속하면 저장 시점과 수정 시점에 값이 자동으로 들어간다.
테스트 코드
@Test
public void jpaEventBaseEntity() throws Exception {
//given
Member member = new Member("member1");
memberRepository.save(member); // @PrePersist 호출
Thread.sleep(100);
member.setUsername("member2");
em.flush(); // @PreUpdate 호출
em.clear();
//when
Member findMember = memberRepository.findById(member.getId()).get();
//then
System.out.println("createdDate = " + findMember.getCreatedDate());
System.out.println("updatedDate = " + findMember.getUpdatedDate());
}
이 방식으로도 기본적인 시간 정보는 기록할 수 있다.
하지만 등록자, 수정자까지 관리하려면 코드가 점점 복잡해지고, 매번 공통 엔티티를 만들어야 한다는 불편이 있다.
2. 스프링 데이터 JPA Auditing
스프링 데이터 JPA는 이런 반복 작업을 간단히 처리할 수 있도록 Auditing 기능을 제공한다.
설정
- 스프링 부트 메인 클래스에 @EnableJpaAuditing 추가
- 엔티티에 @EntityListeners(AuditingEntityListener.class) 적용
제공 어노테이션
- @CreatedDate → 등록일
- @LastModifiedDate → 수정일
- @CreatedBy → 등록자
- @LastModifiedBy → 수정자
등록일·수정일 자동화
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {
//등록일 수정일
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
//등록자 수정자
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
여기서 등록자와 수정자는 AuditorAware 빈을 통해 주입한다.
@EnableJpaAuditing
@SpringBootApplication
public class DataJpaApplication {
public static void main(String[] args) {
SpringApplication.run(DataJpaApplication.class, args);
}
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of(UUID.randomUUID().toString());
}
}
실무에서는 UUID 대신 세션 정보나 스프링 시큐리티 로그인 사용자 정보를 넣어준다.
3. Base 클래스 분리 전략
실무에서는 대부분 등록일, 수정일은 필요하지만, 등록자, 수정자는 필요하지 않은 경우도 많다.
그래서 보통 공통 엔티티를 두 단계로 분리한다.
public class BaseTimeEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
public class BaseEntity extends BaseTimeEntity {
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
}
- 단순히 시간만 필요한 경우: BaseTimeEntity 상속
- 사용자까지 필요한 경우: BaseEntity 상속
마무리하며
엔티티에 등록일, 수정일, 등록자, 수정자를 남기는 건 선택이 아니라 사실상 필수에 가깝다.
순수 JPA로도 @PrePersist, @PreUpdate를 활용해 구현할 수 있지만,
스프링 데이터 JPA가 제공하는 Auditing 기능을 쓰면 훨씬 간결하고 직관적으로 관리할 수 있다.
상황에 따라 단순 시간만 기록하거나, 사용자 정보까지 추적할 수 있도록 Base 엔티티를 적절히 분리해 상속하는 게 실무에서 가장 중요하다고 전해진다.
감사합니다.
'스프링 데이터 JPA' 카테고리의 다른 글
| [스프링] 스프링 Data Jpa(7) 사용자 정의 리포지토리 구현 (1) | 2025.08.26 |
|---|---|
| [스프링] 스프링 Data JPA(6) @EntityGraph (0) | 2025.08.21 |
| [스프링] 스프링 Data JPA(5) 벌크성 수정쿼리 (0) | 2025.08.21 |
| [스프링] 스프링 Data JPA(4) JPA 페이징과 정렬 (0) | 2025.08.21 |
| [스프링] 스프링 Data JPA(3) @Query, 리포지토리 메소드에 쿼리 정의하기 (0) | 2025.08.21 |
