본문 바로가기
Today I Learned/Spring

[Spring] DTO(Data Transfer Object)

by 프로그래 밍구 2022. 9. 24.

DTO (Data Transfer Object)

 DTO는 Data Transfer Object의 약자로 마틴 파울러(Martin Fowler)가 ‘Patterns of Enterprise Application Architecture’ 라는 책에서 처음 소개한 엔터프라이즈 애플리케이션 아키텍처 패턴의 하나이다. Transfer 라는 단어에서 알 수 있듯이 데이터를 전송하기 위한 용도의 객체 정도로 생각할 수 있다. 바로 클라이언트에서 서버 쪽으로 전송하는 요청 데이터, 서버에서 클라이언트 쪽으로 전송하는 응답 데이터의 형식으로 클라이언트와 서버 간에 데이터 전송이 이루어지는데 이 구간에서 DTO를 사용할 수 있다. 

DTO가 필요한 이유

1. DTO 클래스를 이용한 코드의 간결성

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name {
        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
 
        return new ResponseEntity<Map>(map, HttpStatus.CREATED);
    }
}

postMember( )_1

 

 위와같이 회원정보를 받아 저장하는 postMember( ) 핸들러메서드가 있다. 회원정보를 저장하기 위해 두 개의 @RequestParam 애너테이션을 사용하고 있다. 실제 회원정보를 관리하는 프로그램에서는 주소, 전화번호, 로그인, 패스워드 등 더 많은 정보들이 회원정보에 포함되어 있을 수 있다. 그러면 postMember( )에 추가되는 @RequestParam의 개수는 늘어날 수 밖에 없다. 이 때 요청데이터를 하나의 객체로 전달받는 DTO 클래스가 있다면 코드가 간결해질 것이다. 아래는 MemberDto 클래스이다.

 

public class MemberDto {
    private String email;
    private String name;
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

MemberDto 클래스

 

MemberDto 클래스가 만들어지고 postMember( )가 적용되면 아래와 같다.

 

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(MemberDto memberDto) {
        return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
    }
}

 간단해진 postMember( )

 

 @RequestParam을 통해 전달 받은 요청 데이터들을 Map에 추가하는 로직이 사라지고, MemberDto 객체를 ResponseEntity 클래스의 생성자 파라미터로 전달하도록 변경되었다. DTO 클래스를 사용하니 postMember( )의 코드가 눈에 띄게 간결해진 것을 알 수 있다.

2. 데이터 유효성(Validation) 검증의 단순화

 클라이언트 쪽에서 회원 정보의 email 주소를 ‘hgd1234’ 같은 단순 문자열이 아닌 ‘hgd1234@gmail.com’ 같은 이메일 주소 형식을 지키도록 하고 싶은 상황이 있다. 이처럼 서버 쪽에서 유효한 데이터를 전달 받기 위해 데이터를 검증하는 것을 유효성(Validation)검증이라고 한다.

 postMember( )에서 이메일 주소 형식을 지키도록 하기 위해 단순히 if문을 이용한 코드를 추가할 수 있다.

 

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name {
        if (!email.matches("^[a-zA-Z0-9_!#$%&'\\*+/=?{|}~^.-]+@[a-zA-Z0-9.-]+$")) {
            throw new InvalidParameterException();
        }
        Map<String, String> map = new HashMap<>();
        map.put("email", email);
        map.put("name", name);
 
        return new ResponseEntity<Map>(map, HttpStatus.CREATED);
    }
}

if문이 추가된 postMember( )_1

 

 하지만 회원이름이나 전화번호, 주소 등 정보 등이 많이 포함한 프로그램이라면 그만큼 코드의 복잡도도 높아지게 된다. 이럴 때, DTO 클래스를 사용하면 유효성 검증 로직을 DTO 클래스로 빼내어 핸들러 메서드의 간결함을 유지할 수 있다.

아래는 MemberDto의 email 멤버 변수에 유효성 검증을 적용하는 코드이다.

 

public class MemberDto {
    @Email
    private String email;
    private String name;
    private String phone;
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

@Email이 추가된 MemberDto 클래스

 

 위 코드 2번 줄과 같이 email 멤버 변수에 @Email 애너테이션을 추가하면 클라이언트의 요청 데이터에 유효한 이메일 주소가 포함되어 있지 않을 경우 유효성 검증에 실패하기때문에 클라이언트의 요청은 거부된다. MemberDto 클래스에서 이메일에 대한 유효성 검증을 진행하므로, MemberController의 postMember() 핸들러 메서드는 아래 코드와 같이 간결해진다.

 

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@Valid MemberDto memberDto) {
        return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
    }
}

@Valid 추가된 postMember( )

 

 MemberDto 앞에 붙은 @Valid 애너테이션은 MemberDto 객체에 유효성 검증을 적용하게 해주는 애너테이션이다.

JSON 직렬화(Serialization)와 역직렬화(Deserialization)

 클라이언트 쪽에서 JSON 형식의 데이터를 서버 쪽으로 전송하면 서버 쪽의 웹 애플리케이션은 전달 받은 JSON 형식의 데이터를 DTO 같은 Java의 객체로 변환하는데 이를 역직렬화(Deserialization)라고 한다.

 반면에 서버 쪽에서 클라이언트에게 응답 데이터를 전송하기 위해서 DTO 같은 Java의 객체를 JSON 형식으로 변환하는 것을 직렬화(Serialization)라고 한다.

DTO 클래스의 단점

각 Controller 클래스마다 DTO 클래스(xxxPostDto, xxxPatchDto 등)를 여러개 생성해야한다. 즉 Controller 클래스가 늘어남에 따라 필요한 DTO 클래스가 배로 늘어날 수 있다.

댓글