자바 백엔드 입문 48편 — JPA Auditing @CreatedDate @LastModifiedDate

2026-05-17자바 백엔드 입문

자바 백엔드 입문 48편. 모든 Entity에 생성·수정 시각·작성자를 자동으로 박는 JPA Auditing 표준 패턴. BaseEntity 상속까지 풀어쓴 학습 노트.

📚 자바 백엔드 입문 · 48편 — JPA Auditing @CreatedDate @LastModifiedDate

이 글은 자바 백엔드 입문 시리즈 59편 중 48편이에요. 회사 시스템 모든 테이블에 박히는 created_at·updated_at 컬럼 — JPA Auditing이 자동으로 박아주는 표준 패턴.

Auditing이란

JPA Auditing = "엔티티가 생성·수정될 때 시각·작성자를 자동으로 기록하는 기능". 회사 시스템 표준 — 모든 테이블에 created_at·updated_at 컬럼이 박혀 있어요.

수동으로 하면.

order.setCreatedAt(LocalDateTime.now());
orderRepo.save(order);
// ... 수정 시
order.setUpdatedAt(LocalDateTime.now());
orderRepo.save(order);

매번 반복. 매번 깜빡할 위험. Auditing이 자동 처리해줘요.

4가지 Auditing 어노테이션

어노테이션 자동 입력
@CreatedDate 생성 시각
@LastModifiedDate 마지막 수정 시각
@CreatedBy 생성한 사용자
@LastModifiedBy 마지막 수정한 사용자

처음 두 개가 가장 자주. 사용자 추적이 필요하면 뒤 두 개도.

설정 — 3단계

1. @EnableJpaAuditing 활성화

@SpringBootApplication
@EnableJpaAuditing                              // ← 한 줄
public class MyApp { ... }

2. BaseEntity 만들기

모든 Entity가 공통으로 가질 부모 클래스.

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

    @CreatedDate
    @Column(updatable = false)                   // 생성 후 변경 X
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

@MappedSuperclass = "이 클래스는 테이블이 아니라 부모". 자식 Entity가 상속하면 필드를 같이 가져감.

@EntityListeners(AuditingEntityListener.class) = "이 Entity의 변화에 Auditing 리스너 박아".

3. Entity 상속

@Entity
@Table(name = "orders")
@Getter @Setter @NoArgsConstructor
public class Order extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private int amount;
    private String status;
    // createdAt·updatedAt 자동 상속
}

Order 가 자동으로 createdAt·updatedAt 필드 가짐. 모든 INSERT·UPDATE에 자동 박힘.

동작 확인

Order order = new Order();
order.setAmount(10000);
orderRepo.save(order);
// → INSERT 시 createdAt 자동 박힘

order.setAmount(20000);
orderRepo.save(order);
// → UPDATE 시 updatedAt 자동 박힘 (createdAt은 변경 X)

매번 손으로 시각 박을 일 없음.

@CreatedBy·@LastModifiedBy — 사용자 추적

"누가 만들고 누가 수정했는지" 도 자동 박으려면.

AuditorAware 구현

Spring에 "현재 사용자는 누군가" 알려주는 Bean.

@Component
public class SecurityAuditorAware implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        return Optional.ofNullable(SecurityContextHolder.getContext())
                .map(SecurityContext::getAuthentication)
                .filter(Authentication::isAuthenticated)
                .map(Authentication::getName);
    }
}

(Spring Security 사용 시 — 45편에서 자세히)

@EnableJpaAuditing에 연결

@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "securityAuditorAware")
public class MyApp { ... }

BaseEntity 확장

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

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

    @LastModifiedDate
    private LocalDateTime updatedAt;

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

    @LastModifiedBy
    private String updatedBy;
}

INSERT 시 createdBy = 현재 사용자 이메일, UPDATE 시 updatedBy = 수정자 자동 박힘.

자주 보강하는 컬럼들

회사 시스템에서 BaseEntity에 더 박는 필드들.

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

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

    @LastModifiedDate
    private LocalDateTime updatedAt;

    @Column(nullable = false)
    private boolean deleted = false;             // 소프트 삭제

    @Version
    private Long version;                          // 낙관적 락
}
  • deleted (Soft Delete) = 실제 DELETE 안 하고 플래그만. "휴지통" 패턴
  • @Version = 동시 수정 충돌 감지 (낙관적 락)

이 4가지 박은 BaseEntity가 회사 표준 BaseEntity. 모든 Entity가 상속.

시각 타입 — LocalDateTime vs Instant vs ZonedDateTime

@CreatedDate 필드 타입 선택.

타입 의미 권장
LocalDateTime 시간대 없음 단일 국가 서비스
Instant UTC 타임스탬프 글로벌 서비스 권장
ZonedDateTime 시간대 명시 시간대 정보 필요 시
Long (epoch ms) 숫자 가장 단순

한국 단일 서비스 = LocalDateTime 표준. 글로벌·다국적 = Instant.

💡 BaseEntity가 회사 코드의 핵심

모든 Entity가 같은 BaseEntity를 상속하는 게 한국 회사 표준. 시간·작성자·소프트 삭제·낙관적 락 — 4가지가 공통이라 한 곳에서 관리. 새 Entity 추가 시 extends BaseEntity 한 줄로 끝.

한 줄 정리 — @EnableJpaAuditing + BaseEntity(@MappedSuperclass + @EntityListeners) + @CreatedDate·@LastModifiedDate 세 단계로 모든 Entity 자동 Auditing. 사용자 추적은 AuditorAware 추가.

시험 직전 한 번 더 — Auditing 입문자가 매번 헷갈리는 것

  • JPA Auditing = 생성·수정 시각·작성자 자동 박기
  • 활성화 = @EnableJpaAuditing 메인 클래스
  • 4가지 어노테이션 = @CreatedDate·@LastModifiedDate·@CreatedBy·@LastModifiedBy
  • BaseEntity = 공통 필드 부모 클래스
  • @MappedSuperclass = 테이블 아닌 부모 (필드 상속용)
  • @EntityListeners(AuditingEntityListener.class) = Auditing 리스너 등록
  • 자식 Entity는 extends BaseEntity 한 줄
  • @Column(updatable = false) = 생성 후 변경 X (createdAt)
  • 사용자 추적 = AuditorAware<String> 구현 Bean
  • @EnableJpaAuditing(auditorAwareRef = "...") 로 연결
  • Spring Security 사용 시 = SecurityContextHolder 에서 사용자 꺼냄
  • BaseEntity 자주 박는 필드 = createdAt·updatedAt·createdBy·updatedBy·deleted·version
  • 소프트 삭제 = deleted 플래그로 실제 DELETE 회피 (휴지통)
  • @Version = 낙관적 락 (동시 수정 충돌 감지)
  • 시각 타입 = LocalDateTime (한국), Instant (글로벌)
  • 한국 회사 = 모든 Entity가 BaseEntity 상속이 표준
  • 새 Entity 추가 시 = extends BaseEntity 한 줄로 자동 적용
  • 마이그레이션 시 = created_at·updated_at 컬럼 추가 자동
  • Auditing은 JPA save 시점에 동작 (영속화)
  • 직접 SQL UPDATE는 Auditing 안 먹힘 (@Modifying 쿼리도)

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!