RESTFul API 개발에서 DTO(Data Transfer Object)는 클라이언트와 서버 간의 데이터 교환을 위한 핵심 요소.
API가 클라이언트가 보낸 Request를 받을 때 RequestDTO를 사용하고, 결과 값을 내려줄 때 ResponseDTO를 사용한다.
-> 문제는 API스펙을 고도화할수록 DTO가 너무 많아진다는 것.
또한 요청에 따라서 Request Body가 다르고, Response Body도 다르다.
-> 그래서 API 스펙을 많이 뽑을수록 DTO 패키지가 지저분해진다.
프로젝트가 어느정도 크기가 커지면 로직 안에서 동작하는 클래스들은 domain별로 관리하는게 좋다. (ex. 사용자, 주문, 상품 등) 별로 DTO를 그룹화
-> domain 별로 class 정리
장점 :
com.example.dto
├── user
│ ├── UserRequestDTO
│ └── UserResponseDTO
├── order
│ ├── OrderRequestDTO
│ └── OrderResponseDTO
└── product
├── ProductRequestDTO
└── ProductResponseDTO
여기에 더해서 더 간결해질 방법이 있다.
방법 : 하나의 클래스 안에 Request와 Response DTO를 내부 정적 클래스로 정의한다.
장점 :
public class UserDTO {
public static class Request {
// Request 관련 필드 및 메서드
}
public static class Response {
// Response 관련 필드 및 메서드
}
}
Domain 분리 + inner static class 결합
com.example.dto
├── user
│ └── UserDTO.java (내부에 Request, Response 등의 Static Class 포함)
├── order
│ └── OrderDTO.java (내부에 Request, Response 등의 Static Class 포함)
└── product
└── ProductDTO.java (내부에 Request, Response 등의 Static Class 포함)
이러한 방식의 장점 :
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
import jakarta.validation.constraints.*;
import java.time.LocalDateTime;
public class UserDTO {
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class Request {
@NotBlank(message = "이름은 필수입니다.")
@Size(max = 50, message = "이름은 50자를 초과할 수 없습니다.")
private String name;
@NotBlank(message = "이메일은 필수입니다.")
@Email(message = "유효한 이메일 주소를 입력해주세요.")
private String email;
@NotBlank(message = "비밀번호는 필수입니다.")
@Size(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하여야 합니다.")
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[@#$%^&+=]).*$",
message = "비밀번호는 숫자, 문자, 특수문자를 포함해야 합니다.")
private String password;
@NotNull(message = "생년월일은 필수입니다.")
@Past(message = "생년월일은 과거 날짜여야 합니다.")
private LocalDateTime birthDate;
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public static class Response {
private Long id;
private String name;
private String email;
private LocalDateTime birthDate;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
@JsonIgnore
private String password;
public static Response from(User user) {
return Response.builder()
.id(user.getId())
.name(user.getName())
.email(user.getEmail())
.birthDate(user.getBirthDate())
.createdAt(user.getCreatedAt())
.updatedAt(user.getUpdatedAt())
.build();
}
}
}