Java/Java 궁금했던 개념들

[Java] VO 와 DTO 구분하기

Dev dreamer 2023. 2. 2. 11:47

 

 

💡 1. VO와 DTO 알아보기


Java 를 공부하면서 계속 VO와 DTO에 대한 부분이 나오고

 

언제 한번은 꼭 머릿속에 정리하고 싶어서 이렇게 따로 정리하게 되었다.

 

VO와 DTO는 Data를 담고 전달하는 객체로 동일한 개념이라고도 볼 수 있다.

하지만 몇가지 차이점이 있는데 그 부분을 정리해보자.

 

 

🔍 1-1. DTO(Data Transfer Object)

 

DTO는 순수하게 데이터를 담아서 계층 간으로 이동하는 객체이다.

이때 객체는 getter와 setter 외에는 다른 로직이 관여 할 수 없다.

 

public class PersonDTO {
 
    private String name;
    private int age;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}

여기서 getter/setter를 사용하면서 알아야할 Property(프로퍼티)개념이 있다.

자바에서는 Property가 문법적으로 따로 제공되지는 않는다.

 

자바에서 프로퍼티라는 개념을 사용하기 위해 쓰는 약속이 있는데 getter/setter 에서 get/set 이후 나오는 단어가 property라고 약속하는 것이다.

 

위 코드에서는 프로퍼티가 name 과 age가 되는 것이다.

 

중요한 것은 프로퍼티가 멤버변수 name, age로 결정되는 것이 아닌 getter/setter에서의 name과 age임을 기억해야한다.

즉 멤버변수는 아무렇게 지어도 영향이 없고 getter/setter 로 프로퍼티(데이터)를 표현한다는 것이다.

 

자바는 다양한 프레임 워크에서 데이터 자동화 처리를 위해 리플렉션 기법을 사용하는데, 데이터 자동화 처리에서 제일 중요한 것은 표준규격이다. 예를들어 DTO에서 property가 name,age라면 키값으로 들어온 데이터는 리플렉션 기법으로 setter를 실행시켜 데이터를 넣을 수 있다.

 

중요한 것은 우리가 setter에 요청하는 것이 아닌  프레임 워크 내부에서 setter가 실행된다는 점이다.(눈에 안보임)

 

그래서 layer간에 데이터를 넘길때 DTO를 쓰면 편하다는 것이 이런 이유 때문이다.

View에 있는 form에서 name 필드 값을 프로퍼티에 맞춰 넘겼을 때, 받아야 하는 곳에서는 일일히 처리하는것이 아니라 name 속성의 이름이랑 매칭되는 프로퍼티에 자동적으로 DTO가 인스턴스화 되어 PersonDTO를 자료형으로 값을 받을 수 있다.

그래서 key-value 로 존재하는 데이터는 자동화 처리된 DTO로 변환되어 쉽게 데이터가 세팅된 오브젝트로 받을 수 있다.

 

⚡ Java 리플랙션(Reflection)기법 개념

리플렉션이란 객체를 통한 클래스의 정보를 분석해 내는 프로그램 기법을 말한다.

 

자바는 스크립트 언어가 아닌 컴파일 언어이다. .java➡.class➡실행 이라는 2단계 메커니즘을 가지고 있지만 컴파일 언어로 분리하는 게 옳다. 원래 자바에서는 동적으로 객체를 생성하는 기술이 없었다. 그리고 동적으로 인스턴스를 생성하는 Reflection으로 그 역할을 대신하게 된다.

 

reflection은 자바의 특징이다. 실행중인 자바프로그램 내부를 검사하고 내부의 속성을 수정할 수 있도록 한다. 예를들어 어떤 자바 클래스가 가진 모든 멤버의 이름을 얻거나 보여줄 수 있다. reflection 의 구체적인 쓰임중 하나가 빌더툴을 이용해 소프트웨어 컴포넌트를 만드는 곳에서 보인다. 툴은 reflection을 사용해서 동적으로 로딩되는 자바 컴포넌트(클래스)의 속성을 얻을 수 있다.

 

reflection 출처 : Java Reflection 개념 및 사용법 (tistory.com)

위 블로그에 가서 예시들을 봐야 리플랙션이 좀 더 이해가 되는듯 해요.

 

🔍1-2. VO(Value Object)

 

VO는 특정 값 자체를 표현하므로 불변성을 보장하기 위해 성상자를 사용해야 한다.

 

VO는 값 자체를 나타내므로 setter 같은 새로 값을 지정하는 메서드가 존재하면 안된다.(위와 동일한 이유로)

 

그래서 오직 read만 가능하며 getter만 가능해야 합니다.

 

다시 한번 정리하면

 

객체의 불변성을 보장하기 때문에 값을 설정한 뒤 수정할 수 없다. 즉 setter를 쓰지 않는다.

다른 하나는 equals()와 hashCode()를 재정의(Override)해서 각 객체의 동일성을 판별할 수 있다.

위처럼 VO 클래스를 만들고 두 값을 비교하는 코드로 테스트 했을 때 오류가 난다고 한다

그래서 위처럼 equals 메서드와 hashCode 메서드를 재정의 해줘야 동일성을 판단할 수 있다.

 

즉 VO는 값 그 자체를 나타내기 때문에 Setter 같은 성격의 변조 가능성이 있는 메서드가 존재해서는 안되며, 두 객체의 필드 값이 같다면 모두 같은 객체로 만드는 것이 핵심이다.

 

hashCode()란 ? 해시코드를 간단하게 말하면 해시 알고리즘에 의해 생성된 정수값이다.

List의 개수가 엄청 많다고 하면 탐색할 때 상당히 오래걸리는데 이러한 경우 HashTable 을 사용하면 아주 효과적이다.

HashTable 은 hashCode() 메소드를 사용해 주어진 키에 대한 해시 값을 계산하고 내부적으로 이 값을 사용해 데이터를 저장하기 때문에 접근에 훨씬 용이하다.

 

hashCode 출처 : [Java] 해시코드(hashCode)란 무엇인가? :: Gyun's 개발일지 (tistory.com)

 

🤔VO는 그럼 어떤 경우에 사용해야 할까?

 

➡ 데이터가 불변이여야 하고 단순히 저장된 값을 불러 와야 하는 경우에 사용한다.

 

ex) 서울의 지역번호는 누구나 알듯 02이다. 차량 번호나 핸드폰 번호와 같은 경우 변하지 않고 고정된 값을 요구한다.

그렇기 때문에 위와 같이 고정된 값등은 VO로 저장후 Getter로 호출하는게 일반적이다.

 

 

 

💡 2. DTO와 VO의 차이점


 

DTO는 데이터의 전송만을 위한 객체이며 VO는 특정한 비지니스 로직을 가질 수 있습니다.

 

DTO는 데이터 전달만을 목적으로 하며 VO는 객체 자체를 어떠한 값(Value)으로 사용합니다.

(외부 시스템과 데이터 통신을 할 경우 DTO로, DB에서 가져오는 경우 Data는 VO로 정의 후 사용한다)

 

DTO는 목적 자체가 데이터 전달이고 읽고 쓰는것이 가능해 가변성을 갖는다.

VO는 불면성을 갖으며 read-only의 속성을 갖는다.

VO는 equals와 hashCode를 재정의(Override)해서 각 객체의 동일성을 판단할 수 있습니다.

 

DTO a = new DTO(1);

DTO b = new DTO(1);

a!=b 이다.

 

VO a = new VO(1);

VO b = new VO(1);

a==b 이다.

 

  DTO VO
목적 계층간 데이터 전달 값 자체 표현
동등성 필드값이 같아도 같은 객체가 아니다 필드값이 같으면 같은 객체이다.
가변성 setter 존재시 가변
setter 비존재시 불가변
불변
로직 getter/setter외 로직이 필요하지 않다 getter/setter외의 로직이 있어도 무방하다.

 

 

 

💡 3. 마무리 정리.


DTO는 데이터 전달만을 위한 객체라는 점이 핵심 정의이다.

 

그렇기 때문에 완전히 데이터 전달용도로만 사용하는게 목적이라면, getter/setter만 필요하지, 이외의 비지니스 로직은 굳이 있을 이유가 없다.

 

VO는 두 객체의 모든 필드 값들이 동일하면 두 객체가 같다! 가 핵심 정의이다.

 

그렇기 때문에 완전히 값 자체 표현 용도로만 사용하는 게 목적이라면, 두 객체에 모든 필드값들이 모두 같으면 같은 객체이도록 만드는 것(equals()와 hashCode()의 오버라이딩)이 중요하지, 메소드는 어떤 메소드가 있든 말든 상관 없다.

 

단, setter 성격을 없애고 불변 객체로 만들어야 한다. 왜냐하면 VO는 특정 값 자체를 표현하기 때문이다.

 

로또 미션에서, 각 로또 번호들을 VO로 만들어서 사용한다고 해보자. LottoNumber(1번)의 객체를 불변으로 만들어야 "이 객체는 로또번호 1번 이다." 라고 할 수 있는 것이지, 중간에 1번이라는 숫자가 바뀔 수 있는 여지(setter 성격의 함수의 존재)가 있으면, "이 객체는 로또 번호 1번 그 자체이다." 라고 할 수 없을 것이다.

 

정체성 자체(속성 값)가 계속 변할 가능성이 있으면 이 객체는 A라는 특정 값이다 라고 할 수 없다.

 

 

 

 

 

 

출처 :

[JAVA] DTO와 VO 의 차이 (tistory.com)

DTO와 VO 그리고 Entity 차이점 알아보기 — 슬기로운 개발생활 (tistory.com)

DTO vs VO (velog.io)

[JAVA] DTO와 VO의 차이 (tistory.com)