Home > Troubleshooting > πŸ”[Troubleshooting] πŸš€ DTO 쀑첩 섀계

πŸ”[Troubleshooting] πŸš€ DTO 쀑첩 섀계
Troubleshooting Backend Development Spring Boot

πŸš€ DTO 쀑첩 섀계!

🎯 λͺ©ν‘œ

학생 정보 응닡 μ‹œ 전곡 정보λ₯Ό 쀑첩 객체둜 λ°˜ν™˜ν•˜λ„λ‘ DTO ꡬ쑰λ₯Ό κ°œμ„ ν•©λ‹ˆλ‹€.

λ³€κ²½ λͺ©ν‘œ JSON ꡬ쑰

{
    "name": "Minseong Kang",
    "admissionYear": 2010,
    "major_info": {
        "id": 1,
        "majorNumber": "31513162120518",
        "name": "Computer"
    }
}

πŸ“Š ꡬ쑰 섀계

κΈ°μ‘΄ ꡬ쑰 vs κ°œμ„  ꡬ쑰

ꡬ뢄 κΈ°μ‘΄ κ°œμ„ 
응닡 ν•„λ“œ id, studentId, name, major name, admissionYear, major_info
전곡 ν‘œν˜„ String νƒ€μž… 쀑첩 객체 (MajorInfoDto)
정보 깊이 단일 레벨 2레벨 쀑첩 ꡬ쑰

πŸ”¨ κ΅¬ν˜„ 단계

Step 1: 전곡 정보 DTO 생성

μƒˆλ‘œμš΄ MajorInfoDto 클래슀λ₯Ό μƒμ„±ν•˜μ—¬ 전곡 상세 정보λ₯Ό λ‹΄μŠ΅λ‹ˆλ‹€.

package com.kobe.schoolmanagement.dto.response;

import com.kobe.schoolmanagement.domain.entity.Major;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class MajorInfoDto {

    private Long id;
    private String majorNumber;
    private String name;

    /**
     * Major μ—”ν‹°ν‹°λ₯Ό MajorInfoDto둜 λ³€ν™˜
     * 
     * @param major λ³€ν™˜ν•  Major μ—”ν‹°ν‹°
     * @return MajorInfoDto 객체
     */
    public static MajorInfoDto fromEntity(Major major) {
        return MajorInfoDto.builder()
                .id(major.getId())
                .majorNumber(major.getMajorNumber())
                .name(major.getName())
                .build();
    }
}

πŸ’‘ 핡심 포인트

  • 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ νŒ¨ν„΄ μ‚¬μš© (fromEntity)
  • μ—”ν‹°ν‹°λ₯Ό DTO둜 λ³€ν™˜ν•˜λŠ” μ±…μž„μ„ DTO에 μœ„μž„
  • λΆˆλ³€ 객체 섀계 (Getter만 제곡)

Step 2: 학생 응닡 DTO μˆ˜μ •

κΈ°μ‘΄ StudentResponseDtoλ₯Ό μˆ˜μ •ν•˜μ—¬ 쀑첩 ꡬ쑰λ₯Ό λ°˜μ˜ν•©λ‹ˆλ‹€.

Before

@Getter
@Builder
public class StudentResponseDto {
    private Long id;
    private String studentId;
    private String name;
    private String major;  // ❌ λ‹¨μˆœ λ¬Έμžμ—΄
    
    // ...
}

After

package com.kobe.schoolmanagement.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.kobe.schoolmanagement.domain.entity.Student;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class StudentResponseDto {

    private String name;
    private int admissionYear;

    @JsonProperty("major_info")  // βœ… JSON ν‚€λ₯Ό μŠ€λ„€μ΄ν¬ μΌ€μ΄μŠ€λ‘œ λ³€ν™˜
    private MajorInfoDto majorInfo;

    /**
     * Student μ—”ν‹°ν‹°λ₯Ό StudentResponseDto둜 λ³€ν™˜
     * 
     * @param student λ³€ν™˜ν•  Student μ—”ν‹°ν‹°
     * @return StudentResponseDto 객체
     */
    public static StudentResponseDto fromEntity(Student student) {
        return StudentResponseDto.builder()
                .name(student.getName())
                .admissionYear(student.getAdmissionYear())
                .majorInfo(MajorInfoDto.fromEntity(student.getMajor()))  // βœ… 쀑첩 λ³€ν™˜
                .build();
    }
}

πŸ’‘ μ£Όμš” 변경사항

  1. λΆˆν•„μš”ν•œ ν•„λ“œ 제거: id, studentId μ‚­μ œ
  2. ν•„λ“œ νƒ€μž… λ³€κ²½: String major β†’ MajorInfoDto majorInfo
  3. JSON ν•„λ“œλͺ… λ§€ν•‘: @JsonProperty둜 μŠ€λ„€μ΄ν¬ μΌ€μ΄μŠ€ 적용
  4. 쀑첩 λ³€ν™˜ 둜직: fromEntity λ©”μ„œλ“œ 체인 ν™œμš©

πŸ”„ 데이터 λ³€ν™˜ 흐름

Student Entity
    β”œβ”€β”€ name: "Minseong Kang"
    β”œβ”€β”€ admissionYear: 2010
    └── major: Major Entity
             β”œβ”€β”€ id: 1
             β”œβ”€β”€ majorNumber: "31513162120518"
             └── name: "Computer"
        ↓
StudentResponseDto.fromEntity(student)
        ↓
    MajorInfoDto.fromEntity(student.getMajor())
        ↓
StudentResponseDto
    β”œβ”€β”€ name: "Minseong Kang"
    β”œβ”€β”€ admissionYear: 2010
    └── majorInfo: MajorInfoDto
             β”œβ”€β”€ id: 1
             β”œβ”€β”€ majorNumber: "31513162120518"
             └── name: "Computer"
        ↓
JSON Response (μžλ™ 직렬화)

βœ… μ˜ˆμƒ κ²°κ³Ό

API 응닡 μ˜ˆμ‹œ

{
    "name": "Minseong Kang",
    "admissionYear": 2010,
    "major_info": {
        "id": 1,
        "majorNumber": "31513162120518",
        "name": "Computer"
    }
}

πŸ“Œ 섀계 원칙

1. 계측 뢄리

  • Entity ↔️ DTO λ³€ν™˜ λ‘œμ§μ„ DTO에 μΊ‘μŠν™”
  • ControllerλŠ” λ³€ν™˜ λ‘œμ§μ„ μ•Œ ν•„μš” μ—†μŒ

2. λͺ…ν™•ν•œ 넀이밍

| ν•­λͺ© | Java μ½”λ“œ | JSON ν‚€ |
|β€”β€”|———–|β€”β€”β€”|
| 전곡 정보 | majorInfo (카멜 μΌ€μ΄μŠ€) | major_info (μŠ€λ„€μ΄ν¬ μΌ€μ΄μŠ€) |

3. μž¬μ‚¬μš©μ„±

  • MajorInfoDtoλŠ” λ‹€λ₯Έ 응닡 DTOμ—μ„œλ„ μž¬μ‚¬μš© κ°€λŠ₯
  • 전곡 정보 ν‘œν˜„ 방식이 일관됨

4. ν™•μž₯μ„±

// ν–₯ν›„ μΆ”κ°€ 정보가 ν•„μš”ν•  λ•Œ
@JsonProperty("major_info")
private MajorInfoDto majorInfo;

@JsonProperty("course_info")  // βœ… λ™μΌν•œ νŒ¨ν„΄μœΌλ‘œ ν™•μž₯
private CourseInfoDto courseInfo;

πŸš€ 적용 방법

Controllerμ—μ„œ μ‚¬μš©

@PostMapping
public ResponseEntity<StudentResponseDto> createStudent(
    @RequestBody StudentRequestDto request
) {
    Student student = studentService.createStudent(request);
    
    // fromEntity λ©”μ„œλ“œλ‘œ κ°„λ‹¨νžˆ λ³€ν™˜
    return ResponseEntity.ok(
        StudentResponseDto.fromEntity(student)
    );
}

μΆ”κ°€ μž‘μ—… λΆˆν•„μš”

  • Service 계측 μˆ˜μ • ν•„μš” μ—†μŒ
  • Repository 계측 μˆ˜μ • ν•„μš” μ—†μŒ
  • DTO λ³€ν™˜ 둜직만 μˆ˜μ •ν•˜λ©΄ 끝!