VO, DAO, DTO, ENTITY의 개념과 장단점 및 차이

Benjamin·2022년 10월 26일
3

CS

목록 보기
5/10

VO (Value Object)

  • DTO와 비슷하지만 차이점은 VO는 read only 속성을 갖는다.
  • 관계 데이터 베이스의 레코드에 대응되는 자바 클래스이다. (레코드 = row 한 줄)
  • 형태 : db 레코드를 구성하는 필드(=컬럼)들을 VO의 Attribute로 하고 해당 변수에 접근할 수 있는 Getter, Setter 메소드의 조합으로 클래스가 형성
  • 특성 : 불변성, equlas()로 비교할 때 객체의 모든 값을 비교해야 한다.
  • 예를 들면 빨강은 Color.RED, 초록은 Color.GREEN 이렇게 단순히 값만 표현하기 위해 getter기능만 존재한다.
  • 테이블 내에 있는 속성 외에 추가적인 속성을 가질 수 있으며, 여러 테이블(A, B, C)에 대한 공통 속성을 모아서 만든 BaseVO 클래스를 상속받아서 사용할 수 도 있다.
  • VO의 핵심 역할은 equals()와 hashcode() 를 오버라이딩 하는 것이다.

프로그램의 사용자가 화면에서 어떤 데이터를 입력하거나 조회 요청이 왔을 때 입력된 데이터나 조회하는 조건을 VO에 담아서 DAO에 요청하면 DAO는 저장소(일반적으로 Database )로부터 데이터를 입력하거나 조회한 후 그 결과를 돌려준다.

💡VO -> DAO

VO는 간단한 독립체( Entity )를 의미하는 작은 객체를 의미한다.
VO의 같음은 그 정체성에 의해 결정되지 않는데, 그 뜻은 내부에 선언된 속성(필드)의 모든 값들이 VO 객체마다 값이 같아야, 똑같은 객체라고 판별하지, 같은 객체라고 해서 같지 않다는 것이다!

작기 때문에, 같은 독립체를 대변하는 복수의 같은 VO들이 존재할 수 있다.
하나의 인스턴스에 의존하고 그에 기반한 레퍼런스를 사용하기 보다는 새 객체를 생성하는 것이 더 간편할 수 있다.

기대효과

  • Network traffic이 줄어든다

장단점

  • 장점
    - 클라이언트도 네트워크 오버헤드 없이 영속성 데이터에 액세스 할 수 있다
  • 단점
    - 클래스의 선언을 위해 많은 코드가 필요하다. 즉 파일수가 많아지게 되고 관리도 힘들어진다.

VO 예제코드

@Getter 
@Setter
@Alias("article")
class ArticleVO {
private Long id;
private String title;
private String contents;

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Article article = (Article) o;
    return Objects.equals(id, article.id);
}

@Override
public int hashCode() {
    return Objects.hash(id);
}
}

DTO (Data Transfer Object : 데이터 전송 객체)

  • 계층 간(Controller, View, Business Layer / Spring Boot에서는 뷰, 컨트롤러, 서비스, DAO, DB를 의미) 데이터 교환을 하기 위해 사용하는 객체 (자바 빈즈(Java Beans))

  • 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스)입니다.

  • setter를 갖고있어, 값이 변할 수 있다.

  • 주로 비동기 처리를 할 때 사용

  • DB의 데이터를 Service나 Controller 등으로 보낼 때 사용하는 객체. 즉,DB의 데이터가 Presentation Logic Tier로 넘어올때는 DTO로 변환되어 오고가는 것이다.

  • Controller Layer에서 Response DTO 형태로 Client에 전달한다.

  • 이용 이유 : 프로세스 간의 커뮤니케이션이 주로 개별 호출이 부담스러운 작업일 경우가 많은 원격 인터페이스에 의해 이루어지기 때문이다.
    대부분의 개별 호출이 클라이언트와 서버 간의 왕복 시간을 소모하기 때문에, 호출 횟수를 줄이는 방법 중 하나는 몇 번의 호출에 의해 전송될 데이터를 모으는 DTO를 이용해서 한번만 호출하는것이다.

Java Beans

  • Java로 작성된 소프트웨어 컴포넌트를 지칭하는 단어
  • 비즈니스 로직 부분을 담당하는 Java 프로그램 단위
    장점
  • JSP페이지가 복잡한 자바 코드로 구성되는 것을 피할 수 있음
  • 재사용 가능한 컴포넌트를 만들 수 있음

DTO vs VO

DTO는 가변의 성격을 가진 클래스이며 데이터 전송을 위해 존재한다.(getter/setter)
그에 반해 VO는 값 그 자체의 의미를 가진 불변 클래스(Read-Only)를 의미한다.(getter만 존재)

DTO는 인스턴스 개념이라면 VO는 리터럴 개념.
즉, VO는 특정한 비즈니스 값을 담는 객체이고, DTO는 Layer간의 통신 용도로 오고가는 객체를 말한다.

Spring Boot DTO 예제코드

@Getter 
@Setter
class ArticleDTO {
  private String title;
  private String content;
  private String writer;
}

DAO (Data Access Object : 데이터 접근 객체)

  • data에 접근하기 위한 객체
  • 커넥션을 하나만 두고, 여러 사용자가 DAO의 인터페이스를 사용하여 필요한 자료에 접근 하도록 하는 것
  • DB에 대한 insert, update, delete, select를 처리한다.
  • 특정 타입의 데이터베이스나 다른 지속적인 메커니즘( Persistence Mechanism )에 추상 인터페이스를 제공하는 객체
  • 어플리케이션 호출을 데이터 저장 부분( Persistence Layer )에 매핑함으로써 DAO는 데이터베이스의 세부 내용을 노출하지 않고 특정 데이터 조작 기능을 제공한다. 이 고립성은 단일 책임 원칙( Single Responsibilty Principle )에 기반한다.

단일 책임 원칙 : 객체 지향 프로그래밍에서 모든 컨텍스트( 클래스, 기능, 변수 등 )은 하나의 책임만 가져야 한다는 것이며, 이는 컨텍스트에 의해 완전히 캡슐화 되어야 한다는 것이다. 그리고 모든 서비스들은 해당 책임에 맞춰 조정되어야 한다.
다른 말로는 "클래스를 수정해야 할 이유는 오직 하나여야 한다"고 한다.

필요성

  • DataBase에 접근 하기 위한 로직 & 비지니스 로직을 분리하기 위해 사용
  • 모든 데이터베이스에 공통적으로 접속 할 수 있는 ODBC가 나왔지만 여전히 로우 레벨의 API를 포함하고 있었기 때문에 개발 장벽이 높았다.
    이런 이유 때문에 개발자들은 정작 데이터베이스에 들어 있는 데이터를 어떻게 이용할지에 초점을 맞추기 보다, 어떻게 데이터베이스에 접속해서 데이터베이스와 교류하는지에 더 초점을 기울였다.
    즉, 데이터를 활용하는 논리적 고민보다 기술적 고민에 더 많은 신경을 썼었다. 이런 이유로 DAO란 대안이 나오게 되었다.

기대 효과

사용자는 자신이 필요한 interface를 DAO에게 던지고 DAO는 이 인터페이스를 구현한 객체를 사용자가 편리하게 사용 할 수 있도록 반환해준다.

장점

  • 장점
    - 데이터베이스 액세스를 DAO에서만 하게 되면 다수의 원격 호출을 통한 오버헤더를 VO나 DTO를 통해 줄일 수 있고, 다수의 DB 호출 문제를 해결할 수 있다.

Entity

  • Entity 클래스는 실제 DataBase의 테이블과 1 : 1로 매핑 되는 클래스로, DB의 테이블내에 존재하는 컬럼만을 속성(필드)으로 가져야 한다.

  • Entity 클래스는 상속을 받거나 구현체여서는 안되며, 테이블내에 존재하지 않는 컬럼을 가져서도 안된다.

  • Entity 클래스 또는 가장 Core한 클래스라고 부른다.

  • 최대한 외부에서 Entity 클래스의 getter method를 사용하지 않도록 해당 클래스 안에서 필요한 로직 method을 구현 해야하고, Domain Logic만 가지며 Presentation Logic을 가지고 있어서는 안된다.

구현 method는 주로 Service Layer에서 사용한다.

Entity, DTO Class 분리 이유

Entity와 DTO를 분리해서 관리해야 하는 이유는 DB Layer와 View Layer 사이의 역할을 분리 하기 위해서다.

Entity 클래스는 실제 테이블과 매핑되어 만일 변경되게 되면 여러 다른 클래스에 영향을 끼치고, DTO 클래스는 View와 통신하며 자주 변경되므로 분리 해주어야 한다.

결국 DTO는 Domain Model 객체를 그대로 두고, 복사하여 다양한 Presentation Logic을 추가한 정도로 사용한다.

Entity Setter 금지 및 생성자, 접근 제어

Entity를 작성할 때 setter를 무분별하게 사용하면 객체(Entity)의 값을 변경할 수 있으므로 객체의 일관성을 보장할 수 없다.
객체의 일관성을 유지할 수 있어야 유지 보수성이 올라가기 때문에 setter를 사용해서는 안되며, 객체의 생성자에 값들을 넣어줌으로써 setter 사용을 줄일 수 있다.

// 객체 생성자 설정
@Builder
public Member(String username, String password, String name) {
this.username = username;
this.password = password;
this.name = name;
}

// 객체 생성 시 값 세팅(빌더패턴 사용)
Member member = Member.Builder()
.username("name")
.password("1234")
.name("name)
.build();

아래와 같이 기본 생성자 접근 제한자를 protected로 변경하면 new Member() 사용을 제한해 Entity의 일관성을 더 유지할 수 있다.

// Member 엔티티
@Entity
@Getter
@Table(name = "member")
public class Member{

// 기본 생성자 protected로 접근 제한(기본 생성자 접근 제한자는 protected 까지 허용
//기본 생성자의 접근 제한자를 private으로 걸면, 추후에 Lazy Loading 사용 시 Proxy 관련 예외가 발생)
protected Member(){};

...
}

@NoArgsconstructor 어노테이션을 통한 protected 접근 제어.

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "member")
public class Member {

}

전체 구조

다시 정리하자면,
데이터를 셋팅하고 가져오는 용도로는 DTO를 사용하며, getter/setter 메소드 외에 로직은 절대 있으면 안됨!
VO는 데이터를 그대로 가져오는 경우 사용한다.

참고사이트
https://melonicedlatte.com/2021/07/24/231500.html
https://m.blog.naver.com/ljc8808/220462395989
https://velog.io/@ha0kim/DAO-DTO-VO-차이

1개의 댓글

comment-user-thumbnail
2024년 4월 2일

vo에 getter기능만 존재한다고 작성하셨는데 예제로 적으신 vo에는왜 setter가 있는건가요?

답글 달기