보통 DTO == VO 즉, DTO와 VO는 혼용해도 된다, DTO와 VO는 같은것이다. 라는 오해가 많이 있습니다.
(그런 오해를 가진 분들을 위한 글입니다!! 이미 알고 계신분은 글을 읽고 검수 해주시면 더욱 감사하겠습니다!!)
그럼 DTO와 VO의 사실에 대해 다뤄보겠습니다.
DTO (Data Transfer Object)
계층(Layer) 간 데이터를 교환을 위한 객체
DTO의 특징
- DTO는 데이터 접근 메서드 외에 기능을 가지지 않습니다.(getter, setter 메서드 외에 비지니스 로직을 가지지않음)
- 정렬, 직렬화 등 데이터 표현을 위한 기능은 가질 수 있음
- 값을 유연하게 변경할 수 있습니다.(가변성, mutable)
- 데이터 캡슐화를 통해 유연한 대응이 가능합니다.
- 데이터 요청 수 감소 효과
다음과 같이 게시글을 저장할 때 Posts 라는 Entity 클래스가 있습니다.
@Getter
@Entity
@NoArgsConstructor
public class Posts {
@Id
@GeneratedValue(strategy = GenerationType.IDENTIFY)
private Long id;
@Column
private String title;
@Column
private String content;
private String author;
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
}
이 게시글을 저장하기 위해 Request를 받을 DTO 'PostsSaveRequestDto 클래스'는 아래와 같이 작성할 수 있습니다. toEntity 메서드는 DTO 인 PostsSaveRequestDto의 객체를 Posts의 Entity 객체로 변환하기 위한 메서드입니다.
@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
private String title;
private String content;
private String author;
@Builder
public PostsSaveRequestDto(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
public Posts toEntity() {
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}
아래는 Posts의 객체가 응답을 위해 Entity 객체를 바로 주지 않고 DTO로 변환해서 응답하기 위한 PostsResponseDto 클래스 입니다. 생성자를 보면 Posts 객체를 인자로 받는것을 확인 할 수 있습니다.
@Getter
public class PostsResponseDto {
private Long id;
private String title;
private String content;
private String author;
public PostsResponseDto(Posts entity) {
this.id = entity.getId();
this.title = entity.getTitle();
this.content = entity.getContent();
this.author = entity.getAuthor();
}
}
VO (Value Object)
값을 가지는 객체
- 변하지 않는 값을 가지는 객체(불변성, immutable)
- 값이 변하지 않음을 보장하며 코드의 안정성과 생산성을 높임
- 값이 같다면 동일한 객체
- 각 객체를 비교하는데 사용되는 ID가 없음
- 같은 객체인지 판단하기 위해 각 속성들의 값을 비교함
- equals() 메서드와 hashCode() 메서드를 오버라이드해서 객체 비교를 구현합니다.
PostsVO 클래스는 다음과 같이 작성할 수 있습니다. equals() 와 hashCode() 가 재정의되어 객체 값 자체를 비교하는걸 볼 수 있습니다.
@Getter
public class PostsVO {
private Long id;
private String title;
private String content;
private String author;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PostsVO postsVO = (PostsVO) o;
return Objects.equals(id, postsVO.id) && Objects.equals(title, postsVO.title) && Objects.equals(content, postsVO.content) && Objects.equals(author, postsVO.author);
}
@Override
public int hashCode() {
return Objects.hash(id, title, content, author);
}
}
공통점 & 차이점
공통점
레이어 간 데이터를 전달할 때 사용 가능
- VO는 불변을 보장하기 때문에 데이터 전달 용도로 사용 가능
차이점
DTO | VO |
값이 변할 수 있음(가변 객체) | 값이 변하지 않음(불변 객체) |
레이어와 레이어 사이에서 사용 가능 | 모든 레이어에서 사용 가능 |
내부의 속성(필드)값이 같아도 다른 객체로 식별 | 내부의 속성(필드)값들이 같다면 같은 객체로 식별 |
데이터 접근 이외의 기능을 가지지않음 | 특정한 비즈니스 로직을 가질 수 있음 |
Entity
Entity 클래스는 실제 DataBase의 테이블과 1:1 로 매핑되는 클래스
DB의 테이블내에 존재하는 컬럼만을 속성(필드)으로 가져야 합니다.
Entity 클래스는 상속을 받거나 구현체여서는 안되며, 테이블내에 존재하지 않는 컬럼을 가져서도 안됩니다.
Dto 클래스와 Entity 클래스
Entity 클래스는 절대 Controller의 Request / Response 클래스로 사용해서는 안됩니다.
Entity 클래스는 데이터 베이스와 매핑된 핵심 클래스(Core Class) 입니다. Entity 클래스를 기준으로 테이블이 생성되고, 스키마가 변경됩니다. 화면 변경은 아주 사소한 변경이지만, 이를 위해 테이블과 연결된 Entity 클래스를 변경하는 것은 너무 큰 변경입니다.
수많은 서비스 클래스나 비즈니스 로직들이 Entity 클래스를 기준으로 동작합니다. Entity 클래스가 변경되면 여러 클래스에 영향을 끼치지만, Request 와 Response용 Dto는 View를 위한 클래스라 정말 자주 변경이 필요합니다.
View Layer와 DB Layer의 역할 분리는 철저하게 하는 것이 좋습니다.
따라서 반드시 Entity 클래스와 Controller 에서 쓸 Dto는 분리해서 사용해야 합니다.
참고문헌
- www.youtube.com/watch?v=J_Dr6R0Ov8E (라흐님의 DTO vs VO)
- www.youtube.com/watch?v=EeJnNaiMy3U&list=PLgXGHBqgT2TvpJ_p9L_yZKPifgdBOzdVH&index=91
(지노&비모님의 DTO vs VO)
- velog.io/@gillog/Entity-DTO-VO-%EB%B0%94%EB%A1%9C-%EC%95%8C%EA%B8%B0
- 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 (저자 : 이동욱님)
'10분 테코톡 정리' 카테고리의 다른 글
리눅스의 메모리 관리 (0) | 2021.09.19 |
---|---|
JCF(Java Collection Framework) (0) | 2021.09.13 |
Index (0) | 2021.01.28 |
댓글