DTO는 어느 레이어까지 사용하는 것이 맞을까?

2013-02-22 18:40

지금까지 나는 Data Transfer Object(이하 DTO)를 다음과 같이 사용했다. 예를 들어 slipp.net의 질문 데이터를 QuestionDto에 담는다고 가정할 경우 다음과 같이 처리했다.

QuestionController.create(QuestionDto questionDto) => QuestionDto를 create() 메소드에서 Question 도메인 클래스(ORM을 사용할 경우 테이블과 매핑되는 클래스)로 변환한 후에 QuestionService.create(Question question) 메소드를 호출했다.

즉, QuestionDto를 Controller까지만 사용하는 것이 지금까지의 일반적인 개발 방식이었다. 그런데 이번에 slipp.net을 개발하면서 QuestionDto를 Question 데이터로 변환할 때 데이터베이스에서 데이터를 조회할 필요가 생겼다. 이렇다보니 QuestionDto를 QuestionService까지 전달한 후 QuestionService에서 QuestionDto => Question으로 변환하는 작업을 했다.

public class QuestionService {
	public Question createQuestion(SocialUser loginUser, QuestionDto questionDto) {
		Assert.notNull(loginUser, "loginUser should be not null!");
		Assert.notNull(questionDto, "question should be not null!");


		Set<Tag> tags = tagService.processTags(questionDto.getPlainTags());


		Question newQuestion = new Question(loginUser, questionDto.getTitle(), questionDto.getContents(), tags);
		Question savedQuestion = questionRepository.save(newQuestion);
		return savedQuestion;
	}
}

일반적으로 DTO 클래스는 어느 레이어까지 전달해 사용하는 것이 맞을까?

0개의 의견 from FB

8개의 의견 from SLiPP

2013-02-23 08:37

레이어를 나눠서 생기는 문제가 아닐까요? 레이어를 나누면 package가 나눠져서 이름만 보면 코드가 연상되지만, 관련된 반복적인 코드 pattern이 나오는 낭비가 생기는 것 같아요. 그냥 repository와 value object만 사용해서 보니 뭐~~ 익숙하기가 힘들지 Service, DTO 같은 suffix가 붙은 코드도 필요없고 Value Object, DTO간 변환 코드도 필요없구요. DTO는 그냥 Parameter object로 단순히 데이터전달할 때만 사용하고요.

2013-02-23 16:23

레이어 별로 멀티 티어로 구성하는 수직적 분산 환경(EJB 같이)이 아니고 도메인 모델 객체가 인프라에 오염되지 않은 순수 객체(POJO)라면 굳이 DTO를 서비스 계층 하부로만 제한할 필요가 없다고 봅니다. 서비스 계층 밖에서 받거나 전달할 객체(DTO)가 도메인 모델이 전혀 다른 구조라면 물론 DTO 성격의 별도 객체를 만들어 써야겠지만 말이죠. 제 말은, POJO일 경우 도메인 모델을 DTO 성격으로 전용하는 것이 문제는 아니라는 것... 다만 도메인 모델 객체에 비지니스 로직이 있어서 이를 사용하지 못하게 하고 싶거나 값을 변경하지 못하게 하고 싶다면...

  • DTO 사용
  • 서비스 계층 밖에서는 Getter 메서드만 있는 Interface를 사용해서 객체에 접근하도록 함
  • Dynamic Proxy를 사용해서 Setter 메서드나 로직 수행 메서드를 접근하지 못하게 차단함
2013-02-24 15:08

그런데 질문을 다시 보니 질문의 방향을 바꾸고 싶어지네요. 저라면 도메인 객체를 어디까지 노출하겠느냐는 식으로 질문했을 것 같아요. 결과적으로는 같은 얘기라고 볼 수 있겠지만 사고의 전개 과정에서 오류나 불필요한 혼돈이 생길 수도 있으니...

2013-02-25 01:22

DTO는 J2EE core pattern에서 처음으로 배웠던 용어로 기억합니다.

martin fowler PEAA에서 개념적으로 다루었는데요. http://martinfowler.com/eaaCatalog/dataTransferObject.html 주로 remote인터페이스에 모델의 구조를 노출시키지 않고, 성능문제를 해결하기 위해서 적용을 하는데요.

저는 요즘처럼 POJO기반의 개발에서의 DTO는 레이어 or 로컬/리모트 구분을 하지 않고 본래의 의도에 충실해서 도메인 모델에 대한 의존성을 줄이거나 데이터기반으로 인터페이싱을 하는 로직에서 사용을 합니다. 같은 레이어 안에서도 의존성을 줄이거나 성능이 고려되는 곳에는 사용을 하는 것이죠.

참고, LocalDTO http://martinfowler.com/bliki/LocalDTO.html

그리고, 서비스 레이어의 인터페이스는 주로 DTO를 인터페이스 파라미터로 노출하지 않고 도메인 모델을 노출하려고 합니다. 대부분 UX layer에서 transform from DTO to domain model을 해서 모델에 populating을 한 후에 서비스 인터페이스를 호출하죠.

아래와 같은 코드가 되겠네요.

public class QuestionService {
  public Question createQuestion(SocialUser loginUser, Question newQuestion) {
    Assert.notNull(loginUser, "loginUser should be not null!");
    Assert.notNull(questionDto, "question should be not null!");
 
    Set<Tag> tags = tagService.processTags(questionDto.getPlainTags());
    Question savedQuestion = questionRepository.save(newQuestion);
    return savedQuestion;
  }
}

본래 질문의 취지는 fupfin님의 답변처럼 도메인 모델을 어디까지 노출하는가로 보이긴 하네요..^^; 2000년대 후반까지는 가급적 안하는 쪽이었고, 지금의 저의 생각은 노출해도 된다쪽으로 생각이 변경이 되었습니다.

2013-02-25 09:38

@강용석 Ruby On Rails나 Play와 같은 프레임워크를 보면 Active Record 패턴 기반으로 Domain 객체에서 많은 일을 처리하고 있다. 나도 이 패턴에 호기심을 가지고 한번 적용해 봤는데 생각보다 복잡도가 증가해서 유지보수 하기 힘들더라.

네가 말한 repository와 value object를 활용하는 방법도 접근해 볼 수 있는 방법인데 이와 같이 구현하더라도 중복 코드가 생기면서 중복을 제거하기 위한 코드는 계속해서 등장하는 듯하다. 근데 이슈는 이 작업들을 계속해 나가야 되는데 그러지 않다보니 Service라는 놈도 두고, 클래스도 많아지니 패키지도 분리하는 작업을 하고 있다. 최초 프로젝트 진행할 때 미리 규정하고 시작하는 것이지. 점진적인 리팩토링을 하는 경우가 많지 않다 보니. 이와 관련한 논의는 계속하지만 나도 딱히 뭐가 최선이다라고 이야기 못하겠다.

2013-02-25 10:28

@fupfin 공유해 주신 방법이 최범균씨가 JCO에서 두 번째 시간에 도메인 객체를 UI에 직접 노출하지 않도록 하기 위해 제안한 방법들입니다. 저도 기본적으로 도메인 객체를 UI까지 노출하는 것을 기본으로 하고 있습니다. DTO를 사용하는 경우는 사용자 입력을 받는 부분에서 일부 사용하고요. 데이터를 조회할 때 DTO를 사용하는 경우는 거의 없었네요.

과거 데이터 조회에 DTO를 사용하는 기반으로 개발했는데 이 방식도 나쁘지는 않다는 생각이 들더라고요. 단, 작업량이 좀 많아지는 단점이 있지만요. 프로젝트 상황에 따라 다르겠죠.

@ologist 나도 지금까지 네가 제안한 방법을 사용해 왔는데 이번에 slipp.net 개발하다보니까 다른 상황이 생겨서 질문으로 쓰게 됐다. 그런데 Service Layer까지 DTO를 사용하는 것도 괜찮다는 생각이 들더라. 어차피 레이어간의 데이터를 전달할 목적으로 DTO를 사용하는 것이기 Service 레이어에 데이터를 전달할 때 도메인 객체로 변환할 필요는 없지 않을까라는 생각을 하게 됐거든. 이에 대한 변화 작업은 Service에서 해도 충분하다 정도..

2013-03-02 21:27

@자바지기 transform은 레이어 중간에 있기때문에 어떤 레이어에 있어도 큰 문제는 없지만, 서비스의 인터페이스와 클라이언트의 의존성을 줄이기 위해서 controller에서 처리를 합니다. 저는 서비스 레이어로 transform을 하는 경우 facade형태의 application layer로 발전해서 4-tier로 가기도 합니다.

2014-08-28 13:43

@ologist 고민하던 내용인데 도움을 얻고 갑니다. DTO 객체는 보통 여러 도메인 객체 정보를 포함하고 있는 경우가 많아서, controller 레이어에서만 처리하려다 보니 controller 코드에서 input/output 에 대한 명확성이 떨어지는 단점이 있고, service 레이어로 내리려고 보니 도메인 레벨의 코드가 명확성이 떨어지는 고민이 있었습니다. transform tier 를 하나 더 두는 것은 코드 관리 차원에서 좋은 전략 같습니다.

의견 추가하기

연관태그

← 목록으로