
소프트웨어 개발에서 DTO(Data Transfer Object)는 데이터를 구조화하여 계층 간에 전달하는 데 중요한 역할을 합니다. 하지만 DTO를 적절히 설계하지 않으면, 코드 유지보수성과 확장성이 저하될 수 있습니다.

AS-IS 구조에서는 다음과 같은 문제가 발생할 수 있습니다:
Controller와 Service 간 강한 의존성
DTO 변환의 책임 불명확
확장성 저하
[Servlet] → [ControllerRequest] → [Service] → [Entity] → [Repository]
1. Controller
@PostMapping("/api/v1/parking-tickets/new")
public ApiResponse<ParkingTicketResponse> createParkingTicket(@Valid @RequestBody ParkingTicketCreateRequest request) {
return ApiResponse.ok(parkingService.createParkingTicket(request));
}
ParkingTicketCreateRequest를 Service로 바로 전달합니다.2. Controller용 DTO
@NoArgsConstructor
@Getter
public class ParkingTicketCreateRequest {
@NotNull(message = "차량 번호는 필수입니다.")
private String vehicleNumber;
@NotNull(message = "입차 시간은 필수입니다.")
private LocalDateTime entryTime;
@Builder
public ParkingTicketCreateRequest(String vehicleNumber, LocalDateTime entryTime) {
this.vehicleNumber = vehicleNumber;
this.entryTime = entryTime;
}
}

TO-BE 구조는 다음과 같은 방식으로 문제를 해결합니다:
계층 간 독립성 확보
DTO 변환 책임 명확화
확장성과 유지보수성 향상
[Servlet] → [ControllerRequest] → [ServiceRequest] → [Service] → [Entity] → [Repository]
1. Controller
@PostMapping("/api/v1/parking-tickets/new")
public ApiResponse<ParkingTicketResponse> createParkingTicket(@Valid @RequestBody ParkingTicketCreateRequest request) {
return ApiResponse.ok(parkingService.createParkingTicket(request.toServiceRequest()));
}
2. Controller용 DTO
@NoArgsConstructor
@Getter
public class ParkingTicketCreateRequest {
@NotNull(message = "차량 번호는 필수입니다.")
private String vehicleNumber;
@NotNull(message = "입차 시간은 필수입니다.")
private LocalDateTime entryTime;
@Builder
public ParkingTicketCreateRequest(String vehicleNumber, LocalDateTime entryTime) {
this.vehicleNumber = vehicleNumber;
this.entryTime = entryTime;
}
public ParkingTicketCreateServiceRequest toServiceRequest() {
return ParkingTicketCreateServiceRequest.builder()
.vehicleNumber(vehicleNumber)
.entryTime(entryTime)
.build();
}
}
3. Service용 DTO
@Getter
public class ParkingTicketCreateServiceRequest {
private String vehicleNumber;
private LocalDateTime entryTime;
@Builder
public ParkingTicketCreateServiceRequest(String vehicleNumber, LocalDateTime entryTime) {
this.vehicleNumber = vehicleNumber;
this.entryTime = entryTime;
}
public ParkingTicket toEntity(String ticketNumber) {
return ParkingTicket.builder()
.ticketNumber(ticketNumber)
.vehicleNumber(vehicleNumber)
.entryTime(entryTime)
.build();
}
}
AS-IS 구조에서는 Controller와 Service 간 강한 결합과 DTO 변환 책임의 불명확성이 문제였습니다. TO-BE 구조에서는 이를 해결하기 위해 DTO를 분리하고 각 계층의 책임을 명확히 정의했습니다.
📌 이 글은 TDD 강의를 학습한 내용을 바탕으로 재구성하였습니다. 문제가 되는 부분이 있다면 수정하겠습니다.