해결된 질문
작성
·
821
2
안녕하세요 강의 잘 듣고 있습니다.
강의를 듣는 도중에 궁금한 점이 있어서 이렇게 질문 드립니다.
컨트롤러의 return타입은 entity를 캡슐화 하기 위해 DTO를 사용하고, 각 Layer 별로 데이터를 주고 받을 때 DTO 또는 Entity를 상황에 맞게 사용하고있습니다.
또한 Repository에서는(Service -> Repository) Entity를 넘겨주어서 사용하도록 하고 있습니다. 이유는 DTO는 Getter,Setter로 가변이지만 Entity는 불변객체이기에 영속 계층에서는 가변 객체를 파라미터로 넘겨준다면 위험하다고 생각해서입니다.
그런데 강의에서는 DTO를 Repository 까지 전달하시는 것을 보고 DTO가 영속 계층까지 가는것에 문제가 없는가 궁금하여 질문드립니다.
추가로 AEntity의 Repository에서 BEntity를 파라미터로 받아도 설계상?으로 괜찮은지도 궁금합니다.
class MemberRepository{
//파라미터로 DTO 받기
List<Member> findMember(MemberDto memberDto){
...
}
//파라미터로 다른 Entity 받기
Member doSomething(Team team){
...
}
}
답변 1
1
안녕하세요. 이하진님, 공식 서포터즈 y2gcoder입니다.
그런데 강의에서는 DTO를 Repository 까지 전달하시는 것을 보고 DTO가 영속 계층까지 가는것에 문제가 없는가 궁금하여 질문드립니다.
먼저 DTO를 영속성 레이어에서 사용해도 되는지에 대해 질문주셨습니다. 결론을 말씀드리자면 어떤 계층에서든 DTO를 사용하는 것은 상관없습니다. DTO를 사용할 때 고려해야 하는 제일 중요한 요소는 해당 DTO의 위치와 해당 DTO를 사용함으로서 패키지(레이어) 간의 의존관계에 순환이 생기지 않는지를 체크하는 것이라고 생각합니다. 이에 대해서는 해당 링크를 타고 가셔서 모두 살펴보시길 추천드립니다. :)
추가로 AEntity의 Repository에서 BEntity를 파라미터로 받아도 설계상?으로 괜찮은지도 궁금합니다.
이 부분은 크게 상관없을 것 같습니다. 주로 연관관계가 있는 엔티티의 Repository에서 이러한 문제가 생길 것 같습니다. 연관된 엔티티의 식별자를 받아 Repository 안에서 연관된 엔티티(혹은 프록시)를 조회해와야 할 것 같습니다. 이 부분은 직접 요구사항과 두 가지 방법을 비교해보고 합당하다고 생각하는 방식으로 해보시는 게 좋을 것 같습니다.
감사합니다.
먼저 좋은 질문해주셔서 감사합니다! 서포터즈지만 영한님의 강의를 수강하고 있는 수강생 및 초보 개발자의 입장에서의 제 개인적인 의견이라고 생각해주시고 들어주시면 감사하겠습니다.
첫번째 질문에 대해서는 제가 DTO의 범위를 이름 그대로 데이터 전송의 목적을 위한 객체로만 생각해서 답변을 드렸습니다. 하지만 질문자분께서는 우테코 리뷰어분께서 말씀하셨던 예처럼 엔티티로 변환할 목적으로 만든 DTO를 언급하셨던 것 같습니다. 저도 실무에서 개발할 때를 생각해보면, 조회 로직에서의 검색 조건을 위한 DTO를 제외하면 기본 타입이나, 엔티티를 Repository에 주로 넘겼던 것 같습니다. repository는 리포지토리에서 생성한 객체나 엔티티에만 의존하도록 저도 개발하고 있습니다. 혼동을 드려 죄송합니다.
그리고 두 번 째 DTO와 엔티티의 변환에서는 해당 답변을 모두 읽어보았습니다.
첫번째 답변에서 서비스 계층에서는 엔티티에만 의존하도록 하는 것이 이상적이라고 하셨고, 하지만 엔티티만으로는 비즈니스 요구사항을 다 충족하지 못할 때도 있다는 현실적인 부분에서의 고려도 필요하다는 답변이었습니다.
두번째 답변에서는 해당 답변의 질문자분께서 짠 코드에서 XXService, XXDto, XX Entity에 모두 의존하고 있는 점이 잘못되었다고 말씀하고 계십니다. 그리고 그 다음에 "차라리~"로 시작하는 부분을 보시면 XXService.findById(id)
의 반환 타입을 엔티티로 하여 컨트롤러에서 DTO에 대한 의존성을 제거하는 방법을 먼저 설명하시고 그 뒤에 엔티티에 대한 의존성을 제거하는 방법을 설명하고 계십니다.
두번째 질문글의 답변을 봤을 때, XXService.findById(id)의 반환 타입이 XXService에서 생성한 다른 DTO라는 점에서 해당 DTO에 의존성이 생긴다고 말씀하신 것 같습니다. 해당 말씀을 예시로 들어주신 코드
(혹시 예시로 들어주신 코드가 어떤 경우인지 말씀해주실 수 있으실까요? requestDto를 AEntity 타입으로 변환하셨고, AService의 파라미터가 없는 serviceMethod() 메서드를 사용해서 또 AEntity 타입을 반환하고 계신데, 결과적으로 ResponseDto에서 사용하는 것은 서비스 메서드에서 받은 엔티티만 사용하고 계십니다. )
에 그대로 적용하면 해당 컨트롤러 메서드는 AEntity와 AService에 의존하고 있다고 보실 수 있지 않을까 합니다. 번외로 컨트롤러에서 사용하는 요청용 DTO와 응답용 DTO는 컨트롤러에서 생성했기 때문에, 컨트롤러와 동일한 패키지에서 생성합니다. 그러면 의존관계가 순환하는 것을 막기 위해 둘 다 서비스로 보내지 않는 것이 맞다고 생각합니다.
그리고 두 답변 + 첫번째 질문의 리뷰어 분께서도 말씀하셨고, 선배 개발자분들이 흔히 하시는 말씀처럼 은탄환은 없다고 생각합니다. 그저 내가 구현해야 할 요구사항에서 비용과 트레이드 오프를 잘 따져서 제일 적합한 방법을 적용하시는 것이 좋다고 생각합니다.
아 아래 질문은 제가 패키지 구조를 생각안하고 질문을 했네요. 죄송합니다.
패키지 구조가
controller
AController.java
RequestDto.java
ResponseDto.java
servic
AService.java
entity
AEntity.java
이렇게 되면 AController는 AEntity와 AService에만 의존하게 되고
AService는 AEntity에만 의존하게 된다는 말씀이시죠?
무슨 말씀이신지 정확하게 알았습니다. 궁금한 점이 정확히 해결되었습니다. 정말 감사합니다.
답변 감사합니다 ^^ . 서포터즈님의 의견을 들으니 길이 보이는 것 같습니다.
두번째 질문에서 예시로 든 코드는 대충 상황만 만들다보니 이상해졌네요.
public ResponseDto controllerMethod(RequestDto requestDto){
AEntity entity1 = requestDto.toEntity();
AEntity entity2 = aService.serviceMethod(entity1);
return new ResponseDto(entity2);
}
Controller -> (DTO to Entity) -> Service(param Entity) -> Repository
Controller <- (Entity to DTO) <- Service(return Entity) <- Repository
이러한 상황을 표현하고 싶었습니다.
해당 코드에서 DTO, Entity, Service 세개에 의존한다고 생각한 이유는 다른게 아니라 해당 클래스에서 import 된 클래스가 DTO, Entity, Service이기 때문입니다.(import를 보고 의존성 판단)
그런데 서포터즈님과 영한님께서 답변을 달아주신것을 다시 읽어보면 의존의 기준이 제가 생각한 import가 아닌 해당 객체생성을 어디에서 했느냐로 판단하시는거 맞나요..?
그런데 객체생성의 위치를 기준으로 본다 하더라도 entity1은 requestDto에서 만들어집니다. 그렇다면 DTO와 의존성이 있는거 아닌가요..? entity1과 entity2는 각각 dto와 service에서 만들어져서 사용되고 있습니다.
객체 생성위치 기준 의존성 판단하자면 -> RequestDto와 Aservice, AEntity에 의존하며 ResponseDto에는 의존하지 않는다. 라고 생각됩니다.
제가 잘못이해하고 있는 부분이 어디일까요? 의존성의 판단이 어떤 기준으로 되는지 잘 모르겠습니다.
답변 달아주셔서 감사합니다.
다시 올려주신 코드를 보니 service는 엔티티에만 의존하도록 코드가 변경된 것을 잘 이해할 수 있었습니다.
그런데 서포터즈님과 영한님께서 답변을 달아주신것을 다시 읽어보면 의존의 기준이 제가 생각한 import가 아닌 해당 객체생성을 어디에서 했느냐로 판단하시는거 맞나요..?
의존의 기준을 개인적으로 엄격하게 보면 저는 처음에 이하진님께서 말씀해주셨던 것처럼 컨트롤러가 RequestDto, ResponseDto, AEntity, AService에 모두 의존하고 있다고 말씀드리고 싶습니다.
두번째 질문글의 답변을 봤을 때, XXService.findById(id)의 반환 타입이 XXService에서 생성한 다른 DTO라는 점에서 해당 DTO에 의존성이 생긴다고 말씀하신 것 같습니다. 해당 말씀을 예시로 들어주신 코드에 그대로 적용하면 해당 컨트롤러 메서드는 AEntity와 AService에 의존하고 있다고 보실 수 있지 않을까 합니다.
다만 이 답변은 두 번째 답변글과 연관지어 이해해주시면 감사하겠습니다. 두 번째 답변글에서는 AEntity, AService, 그리고 AService에서 만들어 반환값으로 보내는 XXDto에 의존하고 있는데, 이러면 너무 많은 곳에 다 의존하기 때문에 하나라도 의존을 없애라는 취지로 말씀을 하신 것으로 보입니다. 위의 댓글에서 제가 그렇게 생각했다는 것을 명료히 보여드리지 못해 죄송합니다.
그리고 RequestDto와 ResponseDto는 컨트롤러에서 사용하기 위해 만든 것이기 때문에 의존하고 있는 것이 당연하다고 생각합니다. 대신 제가 윗 댓글에서 말씀드리고 싶었던 것은 의존관계에 순환 사이클이 생기는 것을 막기 위해 이 컨트롤러에서 사용하기 위해 만든 DTO들을 서비스로 보내지 말자는 것이었습니다.
그런데 객체생성의 위치를 기준으로 본다 하더라도 entity1은 requestDto에서 만들어집니다. 그렇다면 DTO와 의존성이 있는거 아닐까요..? entity1과 entity2는 각각 dto와 service에서 만들어져서 사용되고 있습니다.
그리고 해당 부분은 RequestDto가 AEntity에 의존하고 있는 것이라고 이해해주셔야 합니다. RequestDto 내의 메서드에서 AEntity를 만들어 반환해주고 있기 때문입니다. 의존성의 방향은 중요하다고 생각합니다.
이하진님께서 말씀해주신 부분을 경청하고 답변하며 나름대로 정리해본 바로는 다음과 같습니다.
(계층 아키텍처에서) 영속성 레이어 <- 서비스 레이어 <- 프레젠테이션 레이어(웹, 흔히 컨트롤러 부분입니다.) 순으로 의존하자.
기본적으로 해당 레이어에서 사용하기 위해 만든 DTO는 해당 레이어까지만 의존하는 게 좋다!
의존관계에 순환 사이클이 생겨서는 안된다.
ex) 서비스 레이어에서 사용한다고 만든 DTO를 영속성 레이어까지 가져가는 경우
기본적으로 해당 레이어에서 사용하기 위해 만든 DTO는 해당 레이어까지만 의존하는 게 좋다!
의존관계에 순환 사이클이 생겨서는 안된다.
잘 정리해주셔서 이해가 되는 것 같습니다. 부족한 질문에 좋은 답변 감사합니다!
답변 감사합니다. 이리저리 찾아보고 생각을 하다보니 DTO Entity에 관한 궁금증이 더 생겨 다시 한번 질문 드립니다.
y2gcoder님께서 어느 계층이든 DTO를 사용하는 것은 상관 없다고 말씀하셨는데, 우테코 리뷰어 분께서 한 말씀이 있더라구요
DTO가 서비스로 들어올 수 있기 때문에 초기에 도메인으로 변환한다는 규치을 지키지 않는다면 DTO가 Repository까지 진입하는 경우도 있습니다.
저 말씀대로라면 DTO가 Repository에 진입하는게 좋지않아 보이는데, 제 짧은 지식으로는 이해가 가지 않아서 서포터즈님께서는 어떻게 생각하시는지 견해를 듣고 싶습니다.
DTO와 Entity 변환에 관한 질문입니다.
https://www.inflearn.com/questions/53023/dto%EC%9D%98-layer%EC%97%90%EB%8C%80%ED%95%B4-%EC%A7%88%EB%AC%B8-%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4
해당 질문의 답변에서
이상적으로는 서비스 계층에서 엔티티를 바로 받는 것이 좋은 선택이라 생각합니다. 그러면 서비스 계층은 엔티티에만 의존하기 때문에 서비스 계층의 재사용성이 높아집니다.
라고 답변을 하셨는데, 밑의 질문의 답변의 일부 내용과 상충한 것 같아 질문드립니다.
https://www.inflearn.com/questions/139564/dto-%EC%82%AC%EC%9A%A9%EC%8B%9C%EA%B8%B0%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A7%88%EB%AC%B8
정말 중요한 것은 의존관계라는 관점이 중요합니다.
이 코드는 컨트롤러에서 XXService도 의존하고, XXDto도 의존하고, XX Entity도 의존합니다. 결과적으로 컨트롤러가 모든 곳에 의존하는 좋지 않은 상황입니다.
이렇게 말씀하셨는데, Service가 Entity를 파라미터로 받는다면 어쩔 수 없이 DTO, Entity, Service에 모두 의존하게 되는 것 아닌가요?
Serivice에 Entity를 넘겨주면 이렇게 컨트롤러가 DTO,Entity,Service까지 의존하게 되는데 만약 DTO를 그대로 넘겨주게 된다면 컨트롤러가 Entity와의 의존성은 사라지게 됩니다.
그렇다면 재사용성을 고려해서 Entity를 넘겨주는 것보다 그냥 DTO를 넘겨주고 Service에서 변환 작업을 하는 것이 더 나은 방법일까요?
아니라면 의존성 문제와 재사용성 문제 중 하나는 포기를 하고 둘 중 하나를 선택해 사용해야하는 문제일까요?
항상 정성스러운 답변 감사드립니다.