구현

[구현] BaseTimeEntity로 불필요한 코드 줄이기 (JPA)

키태 2023. 11. 8. 10:01
728x90

JPA를 사용해서 엔티티에 생성되어있는 created_at과 updated_at의 중복 코드를 줄여보도록 하자

내가 진행하고 있는 프로젝트에서 기존의 Entity들은 아래 코드처럼 컬럼이 정의되어 있었다.

@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt

 

그리고 엔티티들이 생성될 때, 나는 각각 생성자에 LocalDateTime.now()를 통해 DB에 저장시켜줬다.

@Builder
    public Answer(String content, Integer emotion, LocalDate date, User user, Boolean isPublic, Boolean isPremium, Boolean isSpare){
        this.content = content;
        this.emotion = emotion;
        this.date = date;
        this.isPublic = isPublic;
        this.isPremium = isPremium;
        this.createdAt = LocalDateTime.now();
        this.user = user;
        this.isBlind = false;
        this.likeCount = 0;
        this.isSpare = isSpare;
    }

 

이렇게 코드를 짜면 무엇이 문제일까? 

 

개발자도 사람이다보니, 각 생성자마다 this.createdAt를 붙이는 과정에서 실수가 날 수 있고 엔티티가 여러개인 상황에서는 '중복 코드'가 생길 수 밖에 없다. 이는 객체지향적으로 코드를 짜지 않는 것이고 불필요한 코드를 남발하는 개발인 것이다.

 

이를 위해 JPA에서는 모든 Entity의 상위 클래스에서 createdAt, updatedAt를 자동으로 관리해주는 역할을 제공해주고 있다.

 

우선, 메인 클래스에서 @EnableJpaAuditng이라는 어노테이션을 붙여주도록 하자.

@SpringBootApplication
@EnableScheduling
@EnableJpaAuditing
public class ServerApplication {

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

	@PostConstruct
	public void setTime() {
		TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));
	}
}

 

나는 DB에 UST로 저장되는 것이 아닌, KST로 저장받고 싶어 @PostConstruct를 통해 전역 타임존을 서울 시간으로 맞췄다. @EnableJpaAuditing을 붙여줌으로써 이제 JPA에서 자동으로 Auditing을 관리해주는 것이다.

 

그리고 이제 엔티티들을 상위에서 관리해줄 추상 클래스를 만들어주도록 하자.

@Getter
@MappedSuperclass // 추후에 BaseTimeEntity를 상속한 엔티티들을 아래 필드들을 컬럼으로 인식
@EntityListeners(AuditingEntityListener.class)  // Auditing(자동으로 값 매핑) 기능 추가
public abstract class BaseTimeEntity {

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

 

위 코드를 보면 추상클래스로 BaseTimeEntity를 만들어줬다.

처음보는 어노테이션들이 있는데 하나씩 보도록 해보자

 

@MappedSuperClass : 앞서 말한대로 우리는 기존에 사용되고 있던 엔티티들을 상위에서 시간들을 자동으로 관리해준다고 했었다. 그러면 예를 들어 Answer 클래스에서 기존에 사용되고 있던 created_at, updated_at 같은 컬럼이 남아있으면 관리가 힘들 수도 있겠다. 그래서 우리는 Answer 클래스에서 created_at, updated_at을 없애고 BaseTimeEntity에서 이들을 정의해주며 상위에서 관리한다는 것이다. 즉, @MappedSuperClass는 추후 BaseTimeEntity를 상속한 엔티티들(ex.. Answer)을 아래 필드들(created_at, updated_at)을 컬럼으로 인식하라 라는 의미이다. 이 어노테이션을 붙임으로써 상속한 모든 클래스에서 불필요한 컬럼 정의 코드를 없앨 수 있다.

 

@EntityListeners(AuditingEntityListener.class) : @EntityListeners 어노테이션은 특정 엔티티에 대한 이벤트 리스너 클래스를 등록하는데 사용된다. AuditingEntityListener 클래스는 주로 Jpa 엔티티에 대한 Auditing 정보를 자동으로 처리하는데 사용된다. 이 클래스를 사용하면 엔티티가 생성 또는 업데이트 될 때 자동으로 갱신이 가능하다. 즉, 이 어노테이션을 사용함으로써 모든 클래스에서 컬럼 생성 및 수정에 사용되었던 (ex.. this.createdAt = LocalDateTime.now()) 같은 코드들을 없앨 수 있다는 것이다.

 

@CreatedDate, @LastModifiedDate : 이 어노테이션은 이름으로 알 수 있듯이, 엔티티가 생성, 마지막으로 수정되는 시간을 의미한다. 

 

이제 모든 학습이 끝났다. 어떻게 코드가 줄어드는지 보도록 하자.

방금 Answer 클래스를 보도록 하자.

@Table(name = "answer")
@Entity
@Getter
@NoArgsConstructor
public class Answer extends BaseTimeEntity {
	// Codes
}

 

기존의 Answer 클래스와 다르게 BaseTimeEntity를 상속(extends)하는 모습이다. 또 코드에는 안나와있지만 기존에 정의되어있던

@Column(name = "created_at", nullable = false)
private LocalDateTime createdAt

 

같은 코드도 삭제했다. 또

@Builder
    public Answer(String content, Integer emotion, LocalDate date, User user, Boolean isPublic, Boolean isPremium, Boolean isSpare){
        this.content = content;
        this.emotion = emotion;
        this.date = date;
        this.isPublic = isPublic;
        this.isPremium = isPremium;
//        this.createdAt = LocalDateTime.now();
        this.user = user;
        this.isBlind = false;
        this.likeCount = 0;
        this.isSpare = isSpare;
    }

 

기존 코드와는 다르게 더이상 createdAt을 수동으로 관리하지 않는다. 코드로는 못보여주고 있지만 updated_at도 마찬가지이다. 엔티티를 수정하는 비즈니스 로직에 쓰이면 매우 유용할 것이다.

 

지금은 Answer 클래스 하나만 예시로 보여줬지만, 실제로 내가 하고 있는 프로젝트에서는 상당히 많은 엔티티들이 created_at과 updated_at이 쓰이고 있다. 이들 모두 BaseTimeEntity를 통해 상속받게하고 관리해주며 이득을 볼 것이다.

728x90