[데브코스] Spring Boot REST API 실습 (9강) - PostDto를 record로 변경하기

zuno·2025년 12월 20일

이번 글은 데브코스 Spring Boot REST API 실습 9강 내용을 정리한 글이다.
9강에서는 기존에 사용하던 PostDto 클래스를 Java record로 변경하고,
DTO를 더 간결하고 명확하게 표현하는 방법을 다룬다.

또한 프론트엔드 개발자의 요구사항 변경을
엔티티가 아닌 DTO에서만 처리해야 하는 이유를 다시 한 번 확인한다.


1️⃣ 9강의 핵심 주제

9강에서 다룬 핵심은 다음 세 가지다.

  • DTO를 classrecord로 변경
  • 프론트엔드 요구사항에 따른 필드명 변경을 DTO에서만 처리
  • Stream 변환 코드를 메서드 레퍼런스로 개선

2️⃣ 실습 1 — PostDto를 record로 변경

📌 첫 번째 커밋

PostDto를 record로 변경

기존 PostDto는 일반 클래스 형태였다.

@Getter
public class PostDto {
    private int id;
    private LocalDateTime createdDate;
    private LocalDateTime modifiedDate;
    private String subject;
    private String body;
}

9강에서는 이를 Java record로 변경한다.

변경 후 PostDto

public record PostDto(
        int id,
        LocalDateTime createdDate,
        LocalDateTime modifiedDate,
        String subject,
        String body
) {
    public PostDto(Post post) {
        this(
                post.getId(),
                post.getCreateDate(),
                post.getModifyDate(),
                post.getTitle(),
                post.getContent()
        );
    }
}

record를 사용하는 이유

  • 불변 객체(immutable)로 설계 가능
  • getter, equals, hashCode, toString 자동 생성
  • DTO의 목적(데이터 전달)에 가장 적합한 형태
  • 코드 양이 줄어들고 가독성이 좋아짐

👉 DTO는 상태를 변경할 필요가 없기 때문에
record는 DTO에 매우 잘 어울리는 선택이다.


3️⃣ 실습 2 — 프론트엔드 요구사항 반영 (DTO에서만)

📌 두 번째 커밋

프론트 개발자 요구:
createdDate → createDate
subject → title
body → content

프론트엔드 개발자의 요청에 따라
API 응답 필드명이 변경되었다.

중요한 점은 엔티티는 전혀 수정하지 않았다는 것이다.

변경 후 PostDto

public record PostDto(
        int id,
        LocalDateTime createDate,
        LocalDateTime modifyDate,
        String title,
        String content
) {
    public PostDto(Post post) {
        this(
                post.getId(),
                post.getCreateDate(),
                post.getModifyDate(),
                post.getTitle(),
                post.getContent()
        );
    }
}

이 실습의 핵심 메시지는 명확하다.

프론트엔드와의 API 계약은 DTO에서만 관리한다.
엔티티는 내부 도메인 모델로 유지한다.


4️⃣ 실습 3 — Stream 변환 코드 개선

📌 세 번째 커밋

.map(post -> new PostDto(post)) → .map(PostDto::new)

기존 컨트롤러 코드는 다음과 같았다.

return items.stream()
        .map(post -> new PostDto(post))
        .toList();

이를 메서드 레퍼런스로 개선한다.

return items.stream()
        .map(PostDto::new)
        .toList();

메서드 레퍼런스를 사용하는 이유

  • 코드가 더 간결해짐
  • “Post → PostDto 변환” 의도가 명확해짐
  • record의 생성자와 잘 어울림

5️⃣ 9강에서 강조한 설계 포인트

9강은 단순한 문법 변경 강의가 아니다.
강의에서 반복해서 강조한 설계 관점은 다음과 같다.

  • DTO는 API 응답 전용 객체
  • 엔티티는 비즈니스 로직과 DB 모델
  • 프론트 요구사항 변경은 DTO에서만 반영
  • record는 DTO의 목적에 가장 적합한 형태

🔚 9강 정리

  • PostDto를 class에서 record로 변경했다
  • DTO를 불변 객체로 설계했다
  • 프론트엔드 요구사항 변경을 DTO에서만 처리했다
  • Stream 변환 코드를 메서드 레퍼런스로 개선했다
  • 엔티티와 API 응답을 명확히 분리하는 구조를 완성했다

💡 한 줄 요약

9강은 DTO를 record로 개선하면서,
API 응답과 도메인 모델을 분리하는 설계를 완성하는 강의였다.



🔍 보충 정리 — 왜 PostDto를 record로 변경했을까?

9강에서는 PostDto를 일반 클래스에서 record로 변경했다.
이는 단순한 문법 변경이 아니라, DTO의 역할을 더 명확히 표현하기 위한 선택이다.


1️⃣ record란 무엇인가?

record는 Java 16부터 도입된 문법으로,
데이터 전달만을 목적으로 하는 클래스(DTO)를 간결하게 표현하기 위해 만들어졌다.

Java 공식 정의를 한 문장으로 요약하면 다음과 같다.

record는 불변(immutable) 데이터 캐리어를 만들기 위한 클래스이다.


2️⃣ record 기본 문법

public record PostDto(
    int id,
    String title
) {
}

이 한 줄만 작성해도 Java는 다음을 자동으로 생성해준다.

  • 모든 필드를 받는 생성자
  • getter 메서드 (id(), title())
  • equals(), hashCode()
  • toString()
  • 모든 필드는 final (불변)

3️⃣ record의 getter와 Jackson 직렬화

record의 getter는 일반 클래스와 이름이 조금 다르다.

  • class: getId()
  • record: id()

하지만 Jackson은 두 형태를 모두 정상적인 getter로 인식한다.
따라서 record를 사용해도 JSON 직렬화에는 전혀 문제가 없다.


4️⃣ record에 생성자 추가하기

record도 생성자를 가질 수 있으며,
실무에서는 엔티티 → DTO 변환용 생성자를 자주 사용한다.

public record PostDto(
    int id,
    String title
) {
    public PostDto(Post post) {
        this(
            post.getId(),
            post.getTitle()
        );
    }
}

이 방식은
엔티티와 API 응답 객체를 분리하는 가장 깔끔한 패턴이다.


5️⃣ class + Lombok vs record

구분class + @Getterrecord
코드 길이매우 짧음
불변성직접 관리기본 불변
setter 존재 가능
equals/hashCodeLombok 의존기본 제공
DTO 의도 표현보통매우 명확

DTO는 상태 변경이 필요 없는 객체이기 때문에
record가 구조적으로 더 잘 어울린다.


6️⃣ 왜 DTO에는 record가 잘 어울릴까?

9강에서 record를 사용한 이유는 다음과 같다.

  • DTO는 데이터 전달만이 목적
  • API 응답 객체는 불변이 자연스러움
  • 프론트엔드 요구사항 변경을 DTO에서만 처리 가능
  • 엔티티와 역할이 명확히 분리됨

즉, record는
“이 클래스는 DTO다”라는 의도를 코드 자체로 표현해준다.


⚠️ record가 어울리지 않는 경우

record는 만능이 아니다.

  • 값 변경이 잦은 객체
  • setter가 필요한 객체
  • 복잡한 비즈니스 로직을 포함한 클래스

👉 이런 경우에는 일반 class가 더 적합하다.


💡 보충 한 줄 요약

record는 DTO의 목적(데이터 전달)을 가장 명확하게 드러내는 문법이며,
9강은 DTO 설계를 한 단계 더 깔끔하게 완성하는 강의였다.

0개의 댓글