DTO는 어디서 변환하는 것이 좋을까?

eunsiver·2023년 4월 30일
0

Spring boot 구현

목록 보기
11/12
post-custom-banner

마이크로서비스로 개발하는 강의를 보는데 DTO를 Mapper해서 사용했다.
나도 전에는 Service 계층에서 DTO를 Entity로 변환해서 사용하였는데 어떤 계층에서 변환을 하는 것이 좋을지, DTO를 왜 사용하는지 다시 정리해보고자 한다.

DTO 사용 이유?

DTO = Data Transfer Object
즉, 데이터를 전송하는 객체

벨덩에서는 "They are flat data structures that contain no business logic"이라고 정의했다.

또한 메서드 호출 수를 줄이기 위해 프로세스 간에 데이터를 전달하는 개체이다.
DTO는 원격 호출 수를 줄이는 데 도움이 되므로 원격 호출이 있는 시스템에서 유용하다.
즉, 필요한 모든 데이터를 그룹화하고 API와의 상호작용을 최적화하는 단일 요청으로 서버에 보낸다.

요청 시 DTO를 사용하는 이유?

  • @RequestParam으로 데이터를 일일히 받을 필요 없이 객체 하나로 한꺼번에 받을 수 있다.
  • Bean Validation, Controller에서 검증 기능을 분리할 수 있다.
  • 엔티티 내부를 캡슐화할 수 있다.(뜬금없는 곳에서 엔티티의 값이 변경되지 않도록 한다.)

응답 시 DTO를 사용하는 이유?

  • 화면에 필요한(api 스펙에 맞는) 데이터만 보낼 수 있다.
  • 순환참조를 예방할 수 있다.
  • 엔티티 내부를 캡슐화 할 수 있다.

그렇다면 DTO는 어디서 Entity로 변환해야 하는지?

DTO를 어디까지 사용하고 어디에서 변환할지가 중요한 것이 아니라 도메일 모델 보호가 핵심이라는 것!!!

1. Controller에서 DTO를 Entity로 변환하고, Service에 Entity 객체를 파라미터로 넘기자.

  • 서비스 계층 입장에서는 특정 DTO에 얽매이지 않고, 엔티티에만 의존하기 때문에 유연하고, 재사용성이 높아진다.
  • 서비스 레이어에는 DTO가 아닌 도메인을 받아야 여러 컨트롤러에서 해당 서비스를 사용할 수 있다.

그러나

  • 하나의 DTO만으로 완전한(값이 다 들어있는) 엔티티를 만들지 못하는 경우가 대부분이다.
    • 예를 들어, 게시글 등록을 위한 DTO의 경우, 게시글 ID 값이 아직 부여되지 않은 상태이다.
    • 여러 DTO를 모아 하나의 엔티티를 만들어야 하는 경우도 빈번하다.
    • 여러 DTO를 모아 복잡한 엔티티를 만드는 것 자체가 비즈니스 로직일 확률이 매우 높다.
  • 파라미터로 받는 Entity는 영속된 상태가 아니라는 것도 조심해야 한다.

https://www.inflearn.com/questions/53023

2. Controller에서 받은 DTO를 Service에 그대로 넘기고, Service에서 Entity로 변환하자

책이나 강의를 보면서 만드는 프로젝트는 대부분 이 방법을 사용했었다.

  • 컨트롤러는 엔티티를 알지 못하고 오직 DTO에 대해서만 알고 있도록 만들수 있다.
  • 그러나 Controller와 Sevice 간의 결합도가 높아지는 문제가 생긴다.

https://stackoverflow.com/questions/16866102/using-dto-to-transfer-data-between-service-layer-and-ui-layer/16872129#16872129

또한 마틴 파울러는 서비스 레이어에서 DTO로 변환하는것을 제시한다.

https://martinfowler.com/eaaCatalog/serviceLayer.html

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.

이러한 의견도 있다고 한다!!

서비스 레이어에는 DTO가 아닌 도메인을 받아야 여러 컨트롤러에서 해당 서비스를 사용할 수 있다.
하지만, 실무에서는 보통 여러 종류의 컨트롤러에서 한 서비스를 사용하기보다는 한 종류의 컨트롤러에서 서비스를 사용하기에 엄격하게 DTO의 진입을 막을 필요는 없다고 한다.

3. 서비스 DTO와 Mapper를 별도로 만들자

서비스 요청, 응답 시 서비스 계층의 메서드가 사용하는 서비스 DTO를 별도로 만들고, DTO와 서비스 DTO를 매핑하는 Mapper를 끼워넣는 방법이 있다고 한다.

Mapper를 사용하면 RequestDTO가 변경되더라도 ServiceRequest 객체는 변하지 않기 때문에 Mapper만 수정해주면 서비스 계층에는 아무런 영향을 미치지 않는다는 것이다.

즉, 매퍼가 중간다리 역할을 해주면서 컨트롤러와 서비스 계층이 완전히 분리되는 효과를 얻을 수 있다.

https://techblog.woowahan.com/2711/


결론

서비스 계층에서 DTO를 그대로 파라미터로 받아 내부에서 엔티티로 변환하는 방법을 기본으로 사용하다가 프로젝트 규모가 커지고 컨트롤러와 서비스 계층의 분리, 모듈화가 필요하다고 느낄 때 Mapper와 서비스 DTO를 만들어 3번으로 넘어가는 것이 좋을 것 같다.

프로젝트 규모에 따라 유동적으로 선택하자.

정해진 답은 없으며 해당 상황에 맞는 적절한 선택지를 가져가는 것이 중요하다.

참고 링크들에 보면 이런 결론이 나와있다.


추가

추가> 핵심 비즈니스 로직을 가지고 있는 서비스 로직과, 화면에 맞춘 읽기 전용 서비스 로직을 별도로 분리해서 설계하는 것이 중요!

https://www.baeldung.com/java-dto-pattern
벨덩의 DTO Pattern에서 Common mistakes에 관한 내용이 있다.

  1. 매 상황마다 DTO를 새로 만드는 것을 지양해라.
  • 기존의 DTO를 재사용하는 것과 새로 만드는 것의 trade-off를 고려해야 한다.
  1. DTO 내부에 비즈니스 로직을 넣지 마라.

참고
https://sedangdang.tistory.com/296
https://catsbi.oopy.io/ecfb5269-a531-4687-b41f-55f80c90acea

profile
Let's study!
post-custom-banner

0개의 댓글