Programming/Refactoring

필요한 파라미터만 사용하기! (DTO를 남용하지 말자.)

JaeHoney 2023. 5. 24. 08:41

Controller - Service - Repository 패턴

아래는 전통적인 계층형 아키텍처(Layered Architecture)이다.

DTO 형태로 요청을 받고 Controller, Service, Repository 등이 메시지를 주고받을 때도 DTO를 사용한다.

 

그러면 되는 걸까?!

아키텍처

Layered Architecture 관점으로 봤을 때 Controller는 Service를 알아야 하는 것이 맞다. 그렇지만 호출되는 Service가 Controller를 알아야 할 필요는 없다.

 

각 계층은 단지 하위의 있는 계층을 사용하면 된다.

Controller에서 DTO를 자신의 Needs로 사용한다면 Service가 DTO를 통해 간접적으로 웹 계층에 의존하게 된다.

  • 개념적 의존이 역류하게 된다.
  • 결과적으로 DTO를 통해 모든 계층이 강하게 결합될 수 있다.

사용하지 않는 필드

Controller에서 필요한 Request, Response와 Service에서 필요한 Request, Response의 스펙이 엄연히 다를 수 있다.

 

예를 들어보자. Controller에 입력으로 들어오는 객체는 User에 관한 정보는 사용하지 않을 것이다. 하지만 Service에서는 유저의 정보가 반드시 입력으로 들어와야 한다.

@PutMapping("/{no}")
public ResponseEntity<MailboxDto.Response> modifyMailbox(
        @AuthenticationPrincipal SecurityUser user,
        @Validated @RequestBody MailboxDto.Request updateDto,
        @PathVariable String no) {
    MailboxDto.Response dto = mailboxService.updatePersonalMailbox(user, updateDto, no);
    return ResponseEntity.ok(dto);
}

위 코드 처럼 Controller에서 사용했던 DTO와 유저 정보를 따로 Service에게 인자로 넘길 수 있다.

 

이렇게 작성된 코드는 요구사항이 많아지면서 수정에 대한 변화가 예측하지 못한 곳으로 전파될 수 있다. 결과적으로 코드가 변화에 취약하게 되어서 새로운 요구사항을 반영하기 어렵게 하고, 리팩토링을 꺼려하게 만든다.

 

Clean Code에서는 가장 이상적인 메서드 파라미터는 0개, 많아야 1개가 적합하다고 말한다.

  • 백기선님 코드 리뷰에서도 의미가 전혀 다른 것이 아니라면 완성된 객체 1개를 넘기는 게 객체 지향적으로 적합하다고 한다.
@PutMapping("/{no}")
public ResponseEntity<MailboxDto.Response> modifyMailbox(
        @AuthenticationPrincipal SecurityUser user,
        @Validated @RequestBody ModifyMailboxCommand modifyMailboxCommand,
        @PathVariable String no ) {
    ModifyMailboxRequest request = new ModifyMailboxRequest(
        no,
        user.getId(),
        modifyMailboxCommand.name(),
        // ...
    )
    ModifiedMailboxInfo info = mailboxService.updatePersonalMailbox(request);
    
    ModifyMailboxResponse response = new ModifyMailboxResponse(
        info.no(),
        info.name(),
        // ...
    )
    return ResponseEntity.ok(response);
}

 

정리하면 Controller에서 유저 정보 등을 조합하거나 필요하지 않은 것을 제외한 Service에 맞는 Request 객체를 만들어서 Service에 넘겨 주어야 한다.

클린 아키텍처

클린 아키텍처에서도 각자의 클래스가 자신이 필요한 파라미터를 받고, 결과를 반환해야 한다고 말한다.

  • 그리고 각자의 계층 관점에서의 유효성 검증을 해야 한다.

 

그래서 Controller에 맞는 Request, Response 객체가 필요하고 UseCase에 맞는 Request, Response 객체가 필요하다.

 

이런 작업은 처음에는 공수가 많이 들겠지만 유지보수하는 동안에는 분명히 빛을 발할 것이다.

참고