DTO를 왜 써?(Entity는 외않되?)라는 글을 완성하면서 DTO가 무엇인지, 왜 사용하는지, DTO와 Entity의 차이점에 대해 알게 되었다.
나 그럼 DTO 완벽 이해한 거잖아?

이제는 DTO는 어디 계층까지 사용할 것인가? 어디 계층에서 Entity 객체로 변환될 것인가? 이 의문들을 해결해 보자.

웹 애플리케이션에선 책임 분리, 모듈화, 유지보수성 및 확장성 향상 등의 이유로 계층 구조를 사용한다.

계층 간 데이터 전달을 위해서 DTO 혹은 Entity를 사용할 텐데, 어디 계층까지 DTO를 사용할 것인지, 사용 범위를 정해줄 필요가 있다.
위의 그림에서 클라이언트와 컨트롤러 사이의 데이터는 DTO를 통해 전달된다고 생각할 수 있겠다. 또한, 리포지토리는 Entity 객체로 DB에 직접 전달 한다는 것도 알고 있다.
그럼 리포지토리 계층에서 DTO를 Entity로 변환하여 DB와 상호작용 하면 될까?

리포지토리 구현체는 영속성 계층(Persistence Layer)에 속한다.
정확하게 얘기하면 리포지토리 인터페이스는 도메인 계층에, 구현체는 영속성 계층에 속한다고 한다.
그리고 영속성 계층에서는 DTO의 사용을 지양한다.
컨트롤러 계층에서 변환이 이루어진다고 생각해보자.
@RestController
@RequestMapping("/api/users")
public class UserController {
// 중략
@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody UserDTO userDTO) {
// DTO를 엔티티로 변환
User user = new User();
user.setUsername(userDTO.getUsername());
user.setEmail(userDTO.getEmail());
user.setPassword(userDTO.getPassword());
// 서비스 계층에 엔티티를 넘겨 처리
userService.registerUser(user);
return ResponseEntity.ok("User registered successfully");
}
}
회원가입 컨트롤러의 예시이다. UserDTO를 RequestBody로 받아 User Entity로 변환하고, Entity 객체를 userService로 넘겨주는 것을 확인 할 수 있다.
컨트롤러 계층에서 DTO, Entity 변환을 하게 된다면
서비스 계층에서 변환이 이루어진다고 생각해보자.
@Service
public class UserService {
// 중략
public User createUser(UserDTO userDTO) {
// DTO를 엔티티로 변환
User user = new User();
user.setUsername(userDTO.getUsername());
user.setEmail(userDTO.getEmail());
user.setPassword(userDTO.getPassword());
// 변환된 엔티티를 저장
return userRepository.save(user);
}
}
회원가입 서비스의 예시이다. UserDTO를 파라미터로 받아 User Entity로 변환하고, userRepository를 통해 DB에 저장하는 것을 확인 할 수 있다.
서비스 계층에서 DTO, Entity 변환을 하게 된다면
그래서 컨트롤러냐고, 서비스냐고.

A Service Layer defines an application's boundary [Cockburn PloP] and its set of available operations from the perspective of interfacing client layers. It encapsulates the application's business logic, controlling transactions and coor-dinating responses in the implementation of its operations.
-Martin Fowler
마틴 파울러는 'Service Layer'란 어플리케이션의 경계를 정의하고 비즈니스 로직 등 도메인을 캡슐화하는 역할이라고 정의한다. 즉, Entity를 컨트롤러 계층에 노출시키면 안 된다는 것이다.
이러한 관점으로 보았을 때는 DTO, Entity 변환은 서비스 계층에서 이루어지는 것이 맞다고 볼 수 있다.
서비스 계층의 모듈화가 꼭 필요한 상황이 되거나, 서비스 로직과 컨트롤러 로직에서 필요로 하는 인자가 달라지는 경우에는 서비스용 DTO를 따로 분리하는 식으로 문제를 해결할 수 있다.
이 글에 대한 필자의 최종 결론이다.

구글에서 찾아볼 수 있는 사진과 조금 다르다 (Service ↔ Repository). 정말 많이 찾아보고 고민해 보았는데 이렇게 표현하는 것이 맞다고 생각한다.