TravelMate-KU / TravelMate-Server

0 stars 1 forks source link

DTO <-> Entity 변환에 대한 고찰 #27

Open jaeuk520 opened 3 months ago

jaeuk520 commented 3 months ago

다음과 같은 코드를 보자

UserProduct userProduct = new UserProduct(
                request.getOneTakeAmount(),
                request.getTotalAmount(),
                request.getNotification(),
                request.getProductName(),
                user,
                request.getCompanyName(),
                request.getImageUrl()
        );
userProductRepository.save(userProduct);

위 코드는 service 단의 코드이며 request는 DTO이다.

서비스 단에서 DTOEntity 로 변환시켜 DB에 insert 하기 위해서 위와 같은 코드가 작성되었다.

로직 자체는 문제가 없지만 다음과 같은 생각이 들었다.

  1. 엔티티의 필드가 늘어날수록 서비스 단의 가독성이 매우 저하된다.


그렇다면 DTO -> Entity 변환 메소드를 만들어 사용해보는 것은?


DTO 내부에 정적 팩토리 메소드, 빌더 패턴 등을 사용하여 toEntity(), from() 와 같은 DTO <-> Entity 변환 메소드를 만들어서 사용하는 일은 사실 이미 흔하다.


하지만 DTOEntity사이에 의존관계가 생겨도 되는 것일까?


결론부터 말하자면 DTOEntity를 알고 의존하는 것은 크게 상관이 없다고 한다.

하지만 역인 EntityDTO에 의존하는 것은 매우 좋지 않다. DTO에 있는 value1 field 이름이 변경되면 Entity 에도 변경이 발생하기 때문에 자신의 책임의 여부와 관계 없는 코드로 인해 변경이 발생하는 것이다.

따라서 위의 코드를 DTO 안에 정적 메소드를 사용하는 코드로 변경하면 아래와 같이 서비스 단의 코드가 더 깔끔해지게 된다.

UserProduct userProduct = request.toEntity(user);
userProductRepository.save(userProduct);

위에서 제시한 방법은 DTO를 "수동으로" Entity로 변환하는 방법이다.

그런데 "Model Mapper"와 같이 "자동으로" DTO를 Entity로 변환해주는 라이브러리가 존재한다고 하는데...?


Mapper를 사용하는 것에 대한 의견은 김영한 님의 답변을 인용했다.

안녕하세요. bk님

우아한형제들의 의견이라기 보다는 제가 실무에서 다양하게 경험한 개인적인 의견을 드리겠습니다^^

저도 주위 개발자 분들과 이것가지고 옥신각신? 하는데요. ㅎㅎ

결국 코드량을 줄여준다는 장점이 있지요.

그럼 제가 생각하는 단점을 쭉 늘어보겠습니다.

  1. 모델이 단순하면 상관이 없는데, 매핑해야 하는 모델이 복잡하거나 서로 차이가 많이 나면... 머리속으로 생각을 좀 많이 해야 합니다. 이게 어느정도 복> 잡해지면 생각하시는 시간 때문에 비용이 더 들어가더라구요.

  2. 직접 수동으로 할 때는 컴파일 시점에 오류를 잡을 수 있는데, 이건 실행을 해봐야 오류를 찾을 수 있습니다.

  3. ModelMapper는 동시성 성능 이슈가 있습니다. 수천 TPS의 리엑티브 모델에서는 이 부분이 명확하게 병목으로 나왔습니다. 물론 수천 TPS가 안되> 는 상황에서는 상관이 없습니다. (MapStruct는 모르겠네요)

결국 장단점이 있는데요. 저는 사용을 안합니다. ㅎㅎ(사용하다가 사용을 안하게 되었으니, 언젠가는 바뀔지도 모르겠습니다.)

복잡한 실무에서 엔티티를 DTO로 변경하는게 이상적으로 딱딱 맞아 떨어지는 경우만 있는 것도 아니고, 수동으로 작업하면 결국 컴파일 시점에 오류를 잡> 을 수 있다는 장점도 있구요. 그리고 무엇보다! 수동으로 해도 손까락만 약간 힘들지 몇분 안걸립니다. ㅋㅋ

주위에 개발 잘하시는 분들 중에 정말 선호하는 분들도 있고, 정말 싫어 하는 분들도 있습니다. ㅎㅎ

그리고! 아쉬워하셨던 엔티티와 DTO 사용과 관련된 부분은 조만간 오픈하는 활용편2탄에서 깊이있게 다룰 예정입니다. 기대해주세요^^

감사합니다.

david-parkk commented 3 months ago
jaeuk520 commented 3 months ago

변환은 service 단에서 일어나는것이 더 일반적이라고 알고 있습니다. 사실 추상화를 기준으로 생각해보면 가장 하위 레이어인 repository에서 변환을 해 주는 것이 가장 깔끔하긴 하지만 repository에서 변환 작업이 발생할 경우 해당 쿼리메서드를 재사용하기 힘들다는 단점이 있어요. (DB에서 조회해온 결과값을 엔터티 갹체로 받지 않고 바로 DTO 객체로 받기 때문)

따라서 일반적으로는 서비스에서 변환해주는게 맞는거같아요:)

jaeuk520 commented 3 months ago

DTO를 서비스 단에서 반환해주는 더 중요한 이유가 있어서 공유하고자 합니다.

실제 운영 서버에서는 OSIV를 false로 해두고 사용하는 경우가 많은데 해당 경우 영속성 컨텍스트의 생존범위와 트랜잭션의 범위를 일치시켜주는 것이 좋기 때문에 서비스 클래스 위에 @Transactional 을 붙여 서비스부터 트랜잭션을 먹여 사용합니다.

따라서 OSIV가 false이며 entity가 반환되어야 하는 경우에는 service 단에서 처리하여 controller에서 지연로딩 할 일이 없도록 해야합니다.