중첩 DTO vs Flat DTO

김현정·2025년 5월 28일

Spring Boot로 REST API를 개발하면 ResponseDto부분에서 필요한 부분을 리스트 형식으로 가져오는지
어떻게 가져올지 고민을 하는데 이번에는 중첩 DTO를 사용해보았다.

1. 중첩 DTO (Nested DTO) 방식

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TrainerReservationResponseDto {
    private Long reservationId;
    private LocalDate reservationDate;
    private LocalTime reservationTime;
    private ReservationStatus status;
    
    private GymInfo gym;
    private UserInfo user;

    @Getter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class GymInfo {
        private Long gymId;
        private String name;
        private String address;
        private String number;
    }

    @Getter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public static class UserInfo {
        private Long userId;
        private String name;
        private String email;
        private String phone;
    }
}

JSON 응답 예시:

{
  "reservationId": 1,
  "reservationDate": "2025-06-01",
  "reservationTime": "15:00",
  "status": "CONFIRMED",
  "gym": {
    "gymId": 1,
    "name": "피트니스센터",
    "address": "서울시 강남구",
    "number": "02-1234-5678"
  },
  "user": {
    "userId": 1,
    "name": "홍길동",
    "email": "hong@example.com",
    "phone": "010-1234-5678"
  }
}

2. Flat DTO 방식

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TrainerReservationFlatDto {
    private Long reservationId;
    private LocalDate reservationDate;
    private LocalTime reservationTime;
    private ReservationStatus status;
    
    // Gym 정보
    private Long gymId;
    private String gymName;
    private String gymAddress;
    private String gymNumber;
    
    // User 정보
    private Long userId;
    private String userName;
    private String userEmail;
    private String userPhone;
}

JSON 응답 예시:

{
  "reservationId": 1,
  "reservationDate": "2025-06-01",
  "reservationTime": "15:00",
  "status": "CONFIRMED",
  "gymId": 1,
  "gymName": "피트니스센터",
  "gymAddress": "서울시 강남구",
  "gymNumber": "02-1234-5678",
  "userId": 1,
  "userName": "홍길동",
  "userEmail": "hong@example.com",
  "userPhone": "010-1234-5678"
}

비교 분석

구분중첩 DTOFlat DTO
가독성논리적 구조 명확필드가 많아지면 복잡
타입 안전성강함 (내부 클래스)약함 (필드명 혼재)
재사용성높음 (GymInfo 재사용 가능)낮음
JSON 크기약간 큼 (중첩 구조)작음
파싱 복잡도중간낮음
확장성좋음나쁨

중첩 DTO의 장점

  1. 명확한 의미적 그룹핑
// 체육관 정보만 필요할 때
GymInfo gymInfo = reservation.getGym();
String gymName = gymInfo.getName();
  1. 뛰어난 재사용성
// 다른 DTO에서도 동일한 구조 재사용
public class GymDetailResponseDto {
    private GymInfo gym;  // 재사용!
    private List<TrainerInfo> trainers;
}
  1. 타입 안전성 보장
// 컴파일 타임에 오류 검출
reservation.getGym().getName();  // ✅ 안전
reservation.getGymName();        // ❌ 오타 가능성

4.쉬운 확장성

public static class GymInfo {
    private Long gymId;
    private String name;
    private String address;
    private String number;
    // 새 필드 추가 시 여기만 수정하면 됨
    private String description;
    private List<String> facilities;
}

중첩 DTO의 단점

  1. JSON 크기 증가
// 중첩: 더 많은 브래킷과 키
{"gym": {"gymId": 1, "name": "..."}}

// 평면: 더 간결
{"gymId": 1, "gymName": "..."}
  1. 프론트엔드 접근 복잡도
// 중첩: 한 단계 더 접근
const gymName = reservation.gym.name;

// 평면: 직접 접근
const gymName = reservation.gymName;

상황에 따른 선택

중첩 DTO를 선택

✅ 관련 필드가 많고 논리적 그룹핑이 가능할 때
✅ 코드 재사용성이 중요할 때
✅ 도메인 구조가 복잡할 때
✅ 장기적 확장 가능성이 높을 때
✅ 타입 안정성이 중요한 프로젝트

Flat DTO를 선택

✅ 필드 수가 적을 때
✅ 성능이 매우 중요할 때 (모바일 앱)
✅ 단순한 CRUD 작업
✅ 레거시 시스템과의 호환성이 필요할 때

0개의 댓글