본문 바로가기
Dev/Java

DTO와 VO

by vellahw 2023. 2. 3.

 

개발을 하며 VO와 DTO를 사용하곤 했는데 뭔지는 알겠는데 누구에게 설명할 수 있을 정도의 명확한 이론 개념은 잡히지 않아 정리 해두고자 했다. 다시 까먹더라도 내가 내 블로그를 보며 이해할 수 있게끔 !!


 

1. DTO (Data Transfer Object)

DTO는 쉽게 말해 데이터를 전달하는 용도라고 볼 수 있다. 데이터를 주고 받을 때 데이터를 담아서 전달하는 바구니(Class)라고 이해하면 될 것 같다.

 

자세하게는 "계층 간" 데이터를 전달하기 위해 사용하는 객체로, 예를 들어 사진과 같이 Controller와 Servie 둘 사이에서 데이터를 주고 받기 위해 DTO에 데이터를 담아 전달한다.

 

💡 DTO의 특징

DTO는 오직 getter/setter 메서드만을 갖으며 다른 로직을 갖지 않는다.
그 이유는 DTO는  *데이터 전달만을 위한 객체이기 때문*!

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.hw.domain;
 
public class SampleDTO {
    
    private String name;
    private String nickName;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    
    public String getNickName() {
        return nickName;
    }
    
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
}
cs

(DTO 클래스 예시)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.hw.service;
 
import org.hw.domain.SampleDTO;
 
public class SampleService {
 
    public SampleDTO createUser() {
        
        String name = "벨라";
        String nickname = "vella";
        
        SampleDTO sampleDTO = new SampleDTO();
        
        sampleDTO.setName(name); //데이터 셋팅
        sampleDTO.setNickName(nickname);; //데이터 셋팅
        
        return sampleDTO;
    }
}
cs

위와 같이 데이터를 보내는 쪽에서 setter를 사용해 데이터를 DTO에 담아 보내고

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.hw.controller;
 
import org.hw.domain.SampleDTO;
import org.hw.service.SampleService;
 
public class SampleController {
    
    private SampleService sampleService;
    
    public String createUser() {
        SampleDTO sampleDTO = sampleService.createUser();
        
        String getUserName = sampleDTO.getName(); //데이터 꺼내오기
        String getUserNickname = sampleDTO.getNickName(); //데이터 꺼내오기
        
        return getUserName + getUserNickname;
    }
}
cs

받는 쪽에서는 getter를 사용해 전달 받은 DTO로부터 데이터를 꺼낸다.

 

⚒️ DTO 불변성 유지하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.hw.domain;
 
public class SampleDTO {
    
    private String name;
    private String nickName;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
    
    public String getNickName() {
        return nickName;
    }
    
    public void setNickName(String nickName) {
        this.nickName = nickName;
    }
}
cs

위와 같이 setter가 있는 DTO는 setter 메소드를 통해 데이터를 setting 할 수 있기 때문에 데이터가 가변적이라고 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package org.hw.domain;
 
public class SampleDTO {
    
    private final String name;
    private final String nickName;
 
    //속성값 초기화
    public SampleDTO(String name, String nickName) {
        this.name = name;
        this.nickName = nickName;
    }
    
    public String getName() {
        return name;
    }
 
    public String getNickName() {
        return nickName;
    }
}
cs


하지만 setter 메소드를 삭제하고 생성자를 통해 속성값들을 초기화하게 만들어 불변객체로 만들면 DTO가 전달하는 메소드가 전달 과정에서 변조 되지 않기 때문에 불변성을 유지 할 수 있다.

 

 

 

2. VO (Value Object)

VO는 Value Object의 약자 답게 쉽게 말해 값 표현용 객체라고 볼 수 있으며 구체적으로는 값 그 자체를 표현하는 객체로 값으로만 비교되는 객체를 말한다.

VO는 값 자체를 표현하기 때문에 불변객체여야 한다. 따라서 setter 메서드가 포함 될 수 없으며 생성자를 통해서만 값을 초기화 해야한다.

모든 속성값이 같다면 같은 객체로 판단하며 DTO와 달리 getter/setter 이외의 로직도 포함 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.hw.domain;
 
public class MoneyVO {
 
    //불변 객체
    private final int value;
    
    public MoneyVO(int value) {
        this.value = value;
    }
    
    //getter,setter 이외의 로직
     public int getHalf() {
        return value / 2;
    }
}
cs

(VO클래스 예시)

 

💡. 속성값이 같다?

돈을 예로 들어 모든 지폐에는 사진과 같이 고유번호가 있다.

우리는 만원짜리 지폐의 고유번호가 각각 다르다고 해서 다른 만원짜리 지폐로 보지 않는다. 왜냐하면 '만원짜리 지폐' 라는 속성값은 변하지 않기 때문이다.

그치만 예시로 작성한 코드와 같이 자바에서는 같다고 취급 하지 않는다! 

객체가 같은 값을 가지고 있다고 하더라도 다른 객체이기 때문에 같지 않다는 것이다. (이런 개념을 참조 객체라고 함)

 

이렇듯 객체가 참조하는 대상의 속성값을 비교하여 객체가 같음을 증명(?) 하면 되는데 VO에서 속성값 비교를 위해선 equals()와 hashCode()를 오버라이딩 해주어야만 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package org.hw;
 
public class Main {
 
    public static void main(String[] args) {
        
        Money money1 = new Money(12345678);
        Money money2 = new Money(12345678);
        
        if(money1.equals(money2)) { //equals로 비교
            System.out.println("같음");
        } else {
            System.out.println("다름");
        }
    }
}
    
    class Money { //만원짜리 지폐의 속성
        private int first; //고유번호
        private int second;
        
        public Money(int first, int second) {
            this.first = first;
            this.second = second;
        }
 
        //equals 오버라이드
        @Override
        public boolean equals(Object obj) {
            
            if(obj == this) {
                return true;
            }
            
            if(obj instanceof Money) {
                Money target = (Money) obj;
                if(this.first == target.first
                        && this.second == target.second) {
                    return true;
                }
            }
            return false;
        }
    }
cs

결과

 

 

 


참고

댓글