자바지기님의 애플리케이션 개발은 어떤 순서로 하면 좋을까?(http://www.slipp.net/questions/21)) 라는 주제의 글을 보다가 클래스에 속성을 추가한다는 글을 접하고 질문을 드립니다. (속성은 어떤 의미의 속성이고 말씀하시는 클래스는 어떠한 클래스일까요?)
그리고 몇개월전에 PHP개발자분에게 '어차피 db의 컬럼들이 추가되는 경우가 종종 있는데 DTO를 쓰는게 편리하냐? 오히려 생산성 저해하고 유지보수를 떨어뜨리는거 아니냐? 그냥 map 객체에 key-value로 박고 리스트로 넘겨줘야 할때엔 map을 list에 담아 넘겨주는게 훨씬 좋지 않겠냐?' 라는 얘길 들었던게 생각이 납니다.
그리고 오늘 아침 DTO라는 키워드로 검색하자 토비님의(토비의스프링3 저자) 오래된 글이 보였습니다.
아직 ORM으로 작업해본 경험이 없고;;; (금번 스터디를 통해 업글해야죠!! 어제 첨으루 TDD문서보면서 사칙연산하는데 재밌네요...ㅋㅋ) 전 과거의 EJB패턴이라 하는 이 DAO, DTO를 그대로 spring에서 작성하고 있거든요.
DTO를 작성할 시에 장점을 적자면 Object객체의 toString, equals 오버라이드나 Comparable interface의 implements, 등등의 여러 방법의 접근으로 data가공을 보다 편리하게 할 수 있으며, 때로는 reflection을 이용한 data get,set에 편의가 있습니다.
반면에 단점을 적자면 table의 컬럼추가가 그대로 로직에 영향을 주게 될 경우 DTO의 property도 함께 추가되어야하고 동시에 각 get,set이 발생되는 모든 layer에서는 해당 get,set을 명시적으로 작성해주어야합니다. (물론 컬럼이 삭제될 경우엔 위 작업의 반대작업이 이루어져야겠죠.) 그리고 비즈니스 로직이 다양해질수록(다루는 data가 다양해질수록(테이블간의 join 등)) 점점 늘어나는 DTO에 지쳐갑니다. (심지어 하나의 로직 당 하나의 DTO가 작성될 수도 있겠죠)
혹시 DTO를 작성하지 않는다면 어떠한 개발방법(?)이 있을까요? 조만간 사내에서 정산툴과 게임운영툴을 다시 새로이 만들 때에 어떻게 할까 고민하는 시점에서 질문을 드려봅니다. ㅎㅎ
10개의 의견 from SLiPP
DTO와 관련해 얼마 전에 적었던 글을 공유해 봅니다. http://www.slipp.net/wiki/pages/viewpage.action?pageId=2031636
전 개인적으로 DTO를 반드시 만들지는 않고요. 요구사항에 따라 DTO에서 데이터를 가공할 경우가 많은 경우에 DTO를 만들고 있습니다. 그렇지 않고 로직이 복잡하지 않다면 대부분의 기능을 Domain에 넣고 사용합니다. 상황과 요구사항에 따라 적절하게 사용하는 연습을 하는 것이 중요하다고 생각합니다.
저도 겪는 고민거리 글이 올라와 자세히 읽고 갑니다. ㅋ 같은 기능 클래스에 용어도 참 다양하더라구요. 프레임워크 마다 VO, Command, Domain, DTO, TO 등 ㅎ
@자바지기 님께서 이전 글에서 작성하셨던(애플리케이션 개발은 어떤 순서로 하면 좋을까?) 글에서 말씀하신 클래스에 속성을 추가한다는 글에 대해서 사실은 DTO의 field(=property=DB의 table column mapping variable)를 인스턴스 변수로 추가한다는 말로 잘못 오해하던 중이었습니다. 답글로 걸어주신 링크를 보니 이해가 좀 되네요. persistent object역할을 하는 클래스의 구현은 인스턴스변수를 가지고 있는 것으로 이해되네요. 그 인스턴스 변수는 바로 database와 mapping이 되고... 스터디 첫날 술자리에서 @자바지기님께서 말씀해주셨던 persistent object 에 대한 개념과 기선님의 글을 보면서 persistence object에 대해서 조금은 감이 오는 것 같습니다. (ㅠㅠ) (참고한 기선님의 글 : http://whiteship.tistory.com/309 ) 정리하자면 @자바지기님께서는 요구사항과(DB와 동기화되지 않은 채 데이터 가공 등) 상황에(링크의 JSON 변환상황 등) 따라서 사용하라는 말씀으로 이해가 되네요. 으아 ㅠㅠ 답없는 이야기를 질문 드려보았습니다. ㅋㅋ
@stone 프레임웤도 프레임워크인데 지금 게임 서버개발자로 겸직하고 있다보니 용어에서 혼선이 오는 경우가 간혹 있더군요 ㅎㅎ; 그러다보니 점점 상황에 따라 용어를 이해하고 해석하는게 몸에 익혀지고 있어요. ㅋㅋㅋ 전 보통 로직구현 전에 필요한 속성들을 뽑아서 DTO로 만들고 그걸 interface의 파라미터나 리턴값으로 쓰고있어요 ㅋ 다른 분들은 어떻게 작업하시는지 궁금하네요 ㅎㅎ
이전 OKJSP에 답변 했던 글 링크 입니다. : http://www.okjsp.pe.kr/seq/115632
귀찮으신 분들을 위해 일부 긁어 왔습니다.
-- 긁어온 부분 시작 -- DTO랑 VO는.. 사실 좀 역사가 복잡합니다. ;;
Core J2EE Patterns 라는 책에서는... Value Object랑 Transfer Object를 동일한 뜻으로 사용합니다만(http://java.sun.com/blueprints/corej2eepatterns/Patterns/TransferObject.html)....) 반대로 Martin Fowler는 저서 Patterns of Enterprise Application Architecture에서 약간 다른 의미로 이야기 합니다. DTO(http://martinfowler.com/eaaCatalog/dataTransferObject.html)는는) 메소드 호출 횟수를 줄이기 위해 데이터를 담고 있는 녀석으로, VO(http://www.martinfowler.com/eaaCatalog/valueObject.html)는는) 값이 같으면 동일 오브젝트라고 볼 수 있는 녀석으로 표현을 하고 있죠.
예를 들자면
이라고 했을때, a != b 이지만,
이라고 했을때 a == b 라고 정의하는 형태라고 보시면 됩니다.
개인적으로는, 구분의 용이성과 용도가 갈리는 경우가 분명히 존재한다는 면에서, 마틴 파울러의 정의에 한표를 던집니다만.. ;; 사실 저거 구분하는것도 일이라.. 보통은 동일한 의미로 사용합니다. -- 긁어온 부분 끝 --
사실 DTO / VO 모두 DB랑 무관합니다. DB랑 관련이 있는 부분은 Active Record http://www.martinfowler.com/eaaCatalog/activeRecord.html 나 Data Mapper http://www.martinfowler.com/eaaCatalog/dataMapper.html 같은 녀석들이겠죠.
전 ORM을 사용할 경우에 Persistent Object 를 주로 이용하고, iBatis나 MyBatis 같은 녀석을 이용할 때는 Map 쪽을 선호합니다. (상황에 따라 Map -> VO / DTO 와 같은 형태를 취할 때도 있습니다.)
지금 프로젝트에서 Lombok(http://projectlombok.org/)) 을 활용하고 있습니다. 특히 Domain, DTO를 만들어 쓸때 용이합니다. DTO 를 만들 때, 도메인의 필드를 복사해서 붙여넣기 하고 컬럼속성만 지우면 되거든요.
Lombok을 사용하면 게터, 세터를 작성하지 않아도 되고, 접근제어를 해야하는 경우에는
-> public String getField(); private void setField(); 가 컴파일할 때 자동으로 클래스에 추가되거든요(이게 맞던가? ㅡ0-;).
만드는 것이 쉬워지니까 만드는 것을 망설이지 않게 되는 효과가 있습니다. 위의 방법으로 만들어두면 ModelMapper를 활용해서 필드값을 손쉽게 주입해서 활용할수도 있습니다. 그 외에 @ToString, @EqualsAndHashCode, @NoArgsConstructor, @RequiredArgsConstructor and @AllArgsConstructor 등의 애노테이션을 활용해서 객체 생성시에 필요한 요소들을 정의해줄 수도 있습니다.
Lombok, 좋아요. >ㅅ<)b 아... 난 또 상관없는 소리를...
나도 DTO와 VO 개념은 @Kenny 의견처럼 분리해서 사용하는 것이 좋다고 생각합니다. 각각의 용도가 분명 다른데 같은 개념으로 접근하는 것은 좋지 않다고 생각해요. DTO를 사용하는 경우가 많은데 생각보다 VO를 사용하는 경우는 많지 않더군요. VO 사용하는 사례를 찾아보면 좋겠네요.
@김지헌 나도 Lombok 사용하는데 유용하더라. 물론 Lombok을 사용할 때의 단점이 있기는 하지만 잘 사용하면 단점보다는 장점이 더 많은 듯하다. 특히 DTO의 경우에는 Lombok 사용해서 처리하는 것이 딱이라는 생각이 든다. Domain의 경우에도 Lombok을 사용하는 경우가 많은데 Domain 클래스의 경우는 굳이 사용하지 않아도 괜찮겠다는 생각도 든다. 특히 Lombok을 사용할 경우 리팩토링이 제대로 적용되지 않아서 그 부분에 불편한 점은 있더라. Lombok 사용에 한표.
@자바지기 골뱅이 댓글 잘달리는 군요. ㅎㅎ
@eclipse4j 골뱅이 댓글을 누가 만들었더라. 시간이 지나면서 점점 더 활성화 되리라 생각한다. 조만간 네가 한단계 더 진화한 모습의 골뱅이 댓글을 만들어 줄거잖아. 기대하고 있으마. ㅋㅋ
@kenny 와...그렇군요..*_* 전에 TDD서적 뒤적거리다 DTO와 VO를 하나로 보길래 같은 것으로 보고 있었는데 아니었군용. DTO와 VO 다시 정리하는 댓글 감사합니다 ㅋㅋ DTO와 VO의 구분도 잘 몰랐는데 정리 감사합니다 :) 그리고 제 질문에 대해서 아래의. http://blog.naver.com/PostView.nhn?blogId=gunlee00&logNo=10100486790&redirect=Dlog&widgetTypeCall=true 이러한 링크를 발견하게 되었네요. @자바지기 @kenny 님께서 해주신 말씀이 위에도 정리되어있는 것 같아요 ㅎㅎ @김지헌 앗 첨봤어요. C#에 property 개념처럼 보이지만 여러 애노테이션들이 bean을 보다 강력하게 하는 것 같네요 ㅋ 앞으로라도 몇몇 DTO 만들 일이 생기면 써봐야겠어요 ㅋㅋ
@김문수 공유해 주신 링크에 있는 논의가 내용이 좋네요. 사실, DTO를 사용할 것인가, MAP을 사용할 것인가 라는 부분에서는 설계에 맞게라는 대답이 돌아올 뿐이죠. @.@ 미묘한 부분 중 하나는, JavaBeans가 과연 DTO인가? 라는 부분입니다. 때에 따라 JavaBeans를 DTO로 사용할 수 있겠지만, 그렇다고 JavaBeans == DTO는 아니라는거죠. 원래 JavaBeans는 Java에서 사용할 Component Model로 나온 Spec 입니다. 단순히 Getter/Setter를 담고 있다고 JavaBeans Spec을 만족시키는 것도 아니고요.
JavaBeans의 정의는 다음과 같습니다.
JavaBeans API Specification Version 1.01, Page 9 : http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html A Java Bean is a reusable software component that can be manipulated visually in a builder tool.
보시다시피, NetBeans나 Eclipse등의 IDE에, GUI Builder 화면 위주로 설계된 스펙입니다. 좀 더 확장하여 일반적인 Component Model 정의에도 사용하고 있지만, 기본 목표는 저 부분입니다. (X-Code의 Builder라던가, VisualStudio의 GUI Builder 화면등을 생각하셔도 좋습니다.)
JavaBeans로 인정 받으려면 해당 Bean은 다음과 같은 특징을 가져야 합니다.
JavaBeans API Specification Version 1.01, Page 9 : http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html * Support for “introspection” so that a builder tool can analyze how a bean works • Support for “customization” so that when using an application builder a user can customize the appearance and behaviour of a bean. • Support for “events” as a simple communication metaphor than can be used to connect up beans. • Support for “properties”, both for customization and for programmatic use. • Support for persistence, so that a bean can be customized in an application builder and then have its customized state saved away and reloaded later.
따라서, 가장 기본적인 JavaBeans는 다음과 같은 구현을 가지게 됩니다.
```class Beans implements java.io.Serializable { private String foo; private int bar;
public void setFoo(String foo) { this.foo = foo; }
public void setBar(int bar) { this.bar = bar; }
public String getFoo() { return foo; }
public int getBar() { return bar; } }```
중요한 점은, java.io.Serializable 인터페이스를 구현하고 있다는 부분입니다. JavaBeans는 위에서 설명했듯, Component Model에 대한 정의이기 때문에, Support for persistence 가 기본 지원 되어야만 합니다. 해당 Component는 원격으로 전송될 수 있으며, 상태가 저장될 수 있어야 하기 때문입니다. persistence가 지원되지 않는 JavaBeans는 없습니다. 상태를 저장하고 복원할 수 없으면 JavaBeans가 아닙니다.
JSP이후로, EL Expression등에서 JavaBeans를 많이 사용해서 DTO를 구현할 때 JavaBeans 형태로 많이 사용하긴 하나, 일단 Serializable 인터페이스 구현을 넣는 경우가 거의 없더군요. 이 경우 해당 Object는 JavaBeans의 껍데기만 가져다 쓴 일반 Java Object이며, DTO로 기능하는데는 문제가 없으나, JavaBeans 규격에 맞지 않으므로 JavaBeans라고 부를 수는 없습니다.
결국... DB와 교신 할 때에 스타일 적인 문제에서 getter/setter 쌍으로 되어 있는 Object(이걸 DTO나 VO라고 부를 수 있는지는 별도로 치고요. 개인적으로는 걍 Object 라고 봅니다만...)를 사용할 것인가, MAP을 사용할 것인가라는 부분에서는 목적에 맞게라는 원론적인 답만이 정답이라고 봅니다. ^^
의견을 남기기 위해서는 SLiPP 계정이 필요합니다.
안심하세요! 회원가입/로그인 후에도 작성하시던 내용은 안전하게 보존됩니다.
SLiPP 계정으로 로그인하세요.
또는, SNS 계정으로 로그인하세요.