쉬었음 코리아
취업/잡담Spring JPA에서 생성일과 수정일을 공통 엔티티로 관리하는 방법
쉬었음 청년
85
0

Spring Boot와 JPA로 게시판이나 커뮤니티 서비스를 만들다 보면 거의 모든 엔티티에 공통적으로 필요한 값이 있다.

대표적인 것이 바로 생성일수정일이다.

게시글이 언제 작성되었는지, 댓글이 언제 등록되었는지, 회원이 언제 가입했는지 같은 정보는 서비스 운영에서 매우 기본적이지만 중요한 데이터다.


처음에는 각 엔티티마다 createdAt, modifiedAt 필드를 직접 작성할 수도 있다.

하지만 엔티티가 많아질수록 같은 코드가 계속 반복된다.

예를 들어 게시글 엔티티에도 생성일과 수정일이 필요하고, 댓글 엔티티에도 필요하며, 회원 엔티티에도 필요할 수 있다.


이런 공통 필드는 별도의 부모 클래스로 분리해서 관리하는 것이 더 깔끔하다.

이번 글에서는 JPA Auditing을 활용해 생성일과 수정일을 자동으로 관리하는 방법을 정리해보려고 한다.

생성일과 수정일이 필요한 이유

커뮤니티 서비스를 예로 들어보자.

게시글 목록에는 보통 작성 시간이 표시된다.

제목: JPA Auditing 정리 작성자: 익명 작성일: 2026.06.08

댓글 역시 언제 작성되었는지 알아야 한다.

엔티티마다 반복하면 생기는 문제

만약 모든 엔티티에 아래 필드를 직접 작성한다고 생각해보자.

private LocalDateTime createdAt; private LocalDateTime modifiedAt;

게시글 엔티티에도 넣고, 댓글 엔티티에도 넣고, 회원 엔티티에도 넣게 된다.

처음에는 큰 문제가 없어 보이지만, 프로젝트가 커질수록 중복이 늘어난다.

public class PostEntity { private LocalDateTime createdAt; private LocalDateTime modifiedAt; } public class CommentEntity { private LocalDateTime createdAt; private LocalDateTime modifiedAt; } public class UserEntity { private LocalDateTime createdAt; private LocalDateTime modifiedAt; }

이렇게 같은 필드가 여러 엔티티에 반복되면 코드 관리가 불편해진다.

그래서 생성일과 수정일처럼 여러 엔티티에서 공통으로 사용하는 필드는 부모 클래스로 분리하는 것이 좋다.

실제 코드 예시

내 프로젝트에서는 생성일과 수정일을 관리하기 위해 CreateTimeEntity라는 공통 클래스를 만들었다.

크리에이트 타임.png

public class PostEntity extends CreateTimeEntity { // 게시글 관련 필드 }

PostEntity에는 직접 createdAt, modifiedAt을 선언하지 않아도 해당 필드가 포함된다.

즉, 게시글 테이블에는 다음과 같은 컬럼이 함께 들어가게 된다.

id title content created_at modified_at

여기서 중요한 점은 CreateTimeEntity라는 테이블이 따로 생성되는 것이 아니라는 점이다.

CreateTimeEntity의 필드가 이 클래스를 상속받는 엔티티의 테이블에 포함되는 구조다.

@MappedSuperclass의 역할

먼저 가장 중요한 어노테이션은 @MappedSuperclass다.

@MappedSuperclass

@MappedSuperclass는 이 클래스가 독립적인 엔티티는 아니지만, 자식 엔티티에게 매핑 정보를 물려줄 수 있다는 의미다.

쉽게 말하면 다음과 같다.

CreateTimeEntity는 테이블로 만들지 않는다. 대신 이 클래스를 상속받는 엔티티에게 필드만 물려준다.

예를 들어 PostEntity가 CreateTimeEntity를 상속하면, 게시글 테이블에 created_at, modified_at 컬럼이 포함된다.

CommentEntity가 상속하면 댓글 테이블에도 동일하게 생성일과 수정일 컬럼이 포함된다.

이 방식의 장점은 명확하다.

공통 필드는 한 곳에서 관리하고, 실제 엔티티는 필요한 기능에 집중할 수 있다.

@EntityListeners와 Auditing

다음으로 볼 부분은 @EntityListeners다.

@EntityListeners(AuditingEntityListener.class)

이 설정은 JPA Auditing 기능을 사용하기 위한 설정이다.

직접 코드로 작성한다면 이런 식의 처리가 필요할 수 있다.

post.setCreatedAt(LocalDateTime.now()); post.setModifiedAt(LocalDateTime.now());

하지만 엔티티가 많아질수록 이런 코드를 매번 작성하는 것은 번거롭다.

또 실수로 어떤 엔티티에서는 시간을 넣고, 어떤 엔티티에서는 빼먹는 문제가 생길 수도 있다.

JPA Auditing을 사용하면 이런 반복 작업을 줄일 수 있다.

엔티티 저장 → createdAt 자동 입력 엔티티 수정 → modifiedAt 자동 변경

이렇게 시간이 자동으로 관리되기 때문에 서비스 로직은 게시글 저장, 댓글 저장 같은 핵심 기능에 더 집중할 수 있다.

@CreatedDate의 의미

@CreatedDate는 엔티티가 처음 생성될 때의 시간을 자동으로 넣어준다.

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

여기서 createdAt은 작성일을 의미한다.

게시글을 처음 작성한 시간이 2026.06.08 14:00이라면, 이후 게시글을 수정하더라도 작성일은 그대로 유지되어야 한다.

그래서 createdAt에는 다음 설정을 함께 사용한다.

@Column(updatable = false, nullable = false)

updatable = false는 한 번 저장된 이후에는 이 컬럼을 수정하지 않겠다는 뜻이다.

즉, 게시글을 수정하더라도 작성일은 바뀌지 않는다.

처음 작성 시간: 14:00 게시글 수정 시간: 15:20 createdAt = 14:00

작성일은 데이터가 처음 만들어진 시점을 의미하므로, 수정될 필요가 없다.

@LastModifiedDate의 의미

@LastModifiedDate는 엔티티가 마지막으로 수정된 시간을 자동으로 넣어준다.

@LastModifiedDate @Column(nullable = false) private LocalDateTime modifiedAt;

게시글이 처음 저장될 때는 createdAt과 modifiedAt이 같은 시간일 수 있다.

createdAt = 14:00 modifiedAt = 14:00

이후 게시글을 수정하면 createdAt은 그대로 유지되고, modifiedAt만 변경된다.

createdAt = 14:00 modifiedAt = 15:20

이렇게 하면 데이터가 처음 만들어진 시간과 마지막으로 변경된 시간을 구분해서 관리할 수 있다.

실제 서비스에서는 이 정보가 생각보다 중요하다.

예를 들어 게시글이 언제 처음 작성되었는지뿐만 아니라, 최근에 수정된 게시글인지 확인해야 할 때도 있기 때문이다.

LocalDateTime을 사용한 이유

생성일과 수정일 타입으로는 LocalDateTime을 사용했다.

private LocalDateTime createdAt; private LocalDateTime modifiedAt;

LocalDateTime은 날짜와 시간을 함께 표현할 수 있는 타입이다.

예를 들어 다음과 같은 값을 담을 수 있다.

2026-06-08T14:30:00

게시글이나 댓글의 작성 시각을 저장하기에는 적절한 타입이다.

다만 실제 운영 환경에서는 서버 시간대와 데이터베이스 시간대가 일관되게 관리되어야 한다.

예를 들어 서버는 UTC 기준으로 저장하고, 화면에서는 한국 시간으로 변환해서 보여주는 방식도 많이 사용된다.

중요한 것은 프로젝트 전체에서 시간 저장 기준을 일관되게 정하는 것이다.

JPA Auditing을 사용하려면 추가 설정이 필요하다

위 코드만 작성했다고 해서 Auditing이 바로 동작하는 것은 아니다.

Spring Boot 프로젝트에서는 보통 설정 클래스나 메인 애플리케이션 클래스에 @EnableJpaAuditing을 추가해야 한다.

@EnableJpaAuditing @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

이 설정이 있어야 @CreatedDate, @LastModifiedDate 같은 Auditing 어노테이션이 동작한다.

이 구조의 장점

이 방식의 가장 큰 장점은 중복 제거다.

생성일과 수정일은 여러 엔티티에서 공통으로 사용된다.

이 필드를 매번 직접 작성하면 코드가 반복되고, 수정이 필요할 때 여러 파일을 고쳐야 한다.

하지만 공통 클래스로 분리하면 한 곳에서 관리할 수 있다.

또 다른 장점은 실수를 줄일 수 있다는 점이다.

직접 시간을 넣는 방식에서는 어떤 저장 로직에서는 createdAt을 넣고, 어떤 저장 로직에서는 빼먹을 수도 있다.

하지만 Auditing을 사용하면 JPA가 자동으로 처리해주기 때문에 이런 실수를 줄일 수 있다.

마지막으로 엔티티 코드가 더 깔끔해진다.

게시글 엔티티는 게시글과 관련된 필드에 집중하고, 댓글 엔티티는 댓글과 관련된 필드에 집중할 수 있다.

생성일과 수정일 같은 공통 관심사는 부모 클래스로 분리하는 것이 구조적으로 더 자연스럽다.

주의할 점

다만 몇 가지 주의할 점도 있다.

첫 번째로, @EnableJpaAuditing 설정을 빼먹으면 @CreatedDate, @LastModifiedDate가 동작하지 않을 수 있다.

두 번째로, createdAt과 modifiedAt에 nullable = false를 설정했다면 저장 시점에 값이 반드시 들어가야 한다.

Auditing 설정이 제대로 되어 있지 않으면 저장 과정에서 오류가 발생할 수 있다.

세 번째로, 시간대 기준을 명확히 정해야 한다.

운영 서버, 데이터베이스, 프론트엔드 화면에서 시간이 다르게 보이면 사용자 입장에서는 혼란스러울 수 있다.

따라서 실제 서비스에서는 시간 저장 기준과 화면 표시 기준을 미리 정해두는 것이 좋다.

정리

JPA에서 생성일과 수정일을 여러 엔티티에서 공통으로 사용해야 한다면, 공통 부모 클래스로 분리하는 방식이 유용하다.

@MappedSuperclass @EntityListeners(AuditingEntityListener.class) public class CreateTimeEntity { @CreatedDate @Column(updatable = false, nullable = false) private LocalDateTime createdAt; @LastModifiedDate @Column(nullable = false) private LocalDateTime modifiedAt; }

결국 좋은 엔티티 설계는 단순히 테이블을 만드는 것이 아니라, 반복되는 구조를 어떻게 정리하고 유지보수하기 쉽게 만들 것인지까지 함께 고민하는 과정이라고 볼 수 있다.

0
0

댓글 0

댓글 불러오는 중...

실시간 인기글