1. 현재 코드의 문제점

문제 1: Entity에서 시간을 직접 설정

@Builder
public Post(String title, String content, String author) {
    this.title = title;
    this.content = content;
    this.author = author;
    this.createdAt = LocalDateTime.now();    // 직접 설정
    this.updatedAt = LocalDateTime.now();    // 직접 설정
}

public void update(String title, String content) {
    this.title = title;
    this.content = content;
    this.updatedAt = LocalDateTime.now();    // 직접 설정
}

모든 Entity에서 LocalDateTime.now()를 직접 호출합니다. Comment Entity에도, 나중에 User Entity에도 같은 코드가 반복됩니다. 실수로 빠뜨리면 null이 들어갑니다.

문제 2: 변환 로직의 위치가 혼재

// PostService에서 직접 Builder 호출
Post post = Post.builder()
        .title(request.getTitle())
        .content(request.getContent())
        .author(request.getAuthor())
        .build();

// PostListResponse에서 from() 메서드
PostListResponse.from(post);

// PostDetailResponse에서도 from() 메서드
PostDetailResponse.from(post);

Request DTO → Entity 변환은 서비스에서 직접 하고, Entity → Response DTO 변환은 DTO의 from()에서 합니다. 일관성이 없습니다.

문제 3: 중복 코드

// PostService.getPost()
Post post = postRepository.findById(id)
        .orElseThrow(() -> new RuntimeException("게시글을 찾을 수 없습니다: " + id));

// PostService.updatePost()
Post post = postRepository.findById(id)
        .orElseThrow(() -> new RuntimeException("게시글을 찾을 수 없습니다: " + id));

// PostService.deletePost()
Post post = postRepository.findById(id)
        .orElseThrow(() -> new RuntimeException("게시글을 찾을 수 없습니다: " + id));

같은 조회 + 예외 처리 코드가 3번 반복됩니다.

이번 수업에서 이 문제들을 전부 해결합니다.


2. JPA Auditing : 생성일/수정일 자동화

JPA Auditing을 쓰면 createdAt, updatedAt자동으로 관리해줍니다. LocalDateTime.now()를 직접 호출할 필요가 없습니다.

Step 1: Auditing 활성화

메인 클래스에 @EnableJpaAuditing을 추가합니다:

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

Step 2: BaseEntity 만들기

모든 Entity가 상속할 공통 Entity를 만듭니다:

package com.myname.board.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;

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

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

    @LastModifiedDate
    private LocalDateTime updatedAt;
}