DTO(Data Transfer Object)

seongmin·2022년 10월 23일
0

Spring

목록 보기
22/38
post-thumbnail

DTO

DTO는 Data Transfer Object의 약자로 마틴 파울러(Martin Fowler)가 ‘Patterns of Enterprise Application Architecture’ 라는 책에서 처음 소개한 엔터프라이즈 애플리케이션 아키텍처 패턴의 하나입니다.

Transfer라는 의미에서 알 수 있듯이 데이터를 전송하기 위한 용도의 객체 정도로 생각할 수 있습니다.

DTO가 필요한 이유

@RestController
@RequestMapping("/v1/members")
public class MemberController {
    @PostMapping
    public ResponseEntity postMember(@RequestParam("email") String email,
                                     @RequestParam("name") String name,
                                     @RequestParam("phone") String phone) {
                                     
                                     ...
                                     
                                     }

회원 정보를 저장하기 위해서 총 세 개의 @RequestParam 애너테이션을 사용하고 있다.

현재는 요청 데이터가 세 개밖에 없지만 실제로는 회원의 주소 정보, 로그인 패스워드, 패스워드 확인 정보 등 더 많은 정보들이 회원 정보에 포함되어 있을 수 있다.

따라서 postMember() 에 파라미터로 추가되는 @RequestParam 의 개수는 계속 늘어날 수 밖에 없다.

이 경우, 클라이언트의 요청 데이터를 하나의 객체로 모두 전달 받을 수 있다면 코드 자체를 간결하게 만들 수 있다.

DTO 클래스가 바로 요청 데이터를 하나의 객체로 전달 받는 역할을 해준다.

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

		...
		...
}
  • postMember() 에서 @RequestParam 을 사용하는 부분이 사라지고 MemberDto memberDto 가 추가되었다.

  • @RequestParam 을 통해 전달 받은 요청 데이터들을 Map에 추가하는 로직이 사라지고, MemberDto 객체를 ResponseEntity 클래스의 생성자 파라미터로 전달하도록 변경되었다.

  • 결과적으로 DTO 클래스를 사용하니 코드 자체가 매우 간결해진 것을 알 수 있다.


DTO 클래스를 만들 때, 주의해야 할 부분은 멤버 변수 이외에 각 멤버 변수에 해당하는 getter 메서드가 있어야 한다는 것이다.

getter 메서드가 없으면 Response Body에 해당 멤버 변수의 값이 포함되지 않는 문제가 발생한다.

setter 메서드는 필수 항목은 아니지만 개발자의 필요에 의해서 있을 수도 있고, 없을 수도 있다.

애너테이션 정리

  • @RequestBody

@RequestBody 애너테이션은 JSON 형식의 Request Body를 Dto 클래스의 객체로 변환을 시켜주는 역할을 한다.

이 말의 의미는 클라이언트 쪽에서 전송하는 Request Body가 JSON 형식이어야 한다는 말과 같다.

만일 JSON 형식이 아닌 다른 형식의 데이터를 전송한다면, Spring 내부에서 ‘Unsupported Media Type’ 과 같은 에러 메시지를 포함한 응답을 전달한다.

  • @ResponseBody

@RequestBody 의 역할이 클라이언트 쪽에서 전송한 JSON 형식의 Request Body를 DTO 클래스의 객체로 변환하는 것이라면, @ResponseBody 는 JSON 형식의 Response Body를 클라이언트에게 전달하기 위해 DTO 클래스의 객체를 Response Body로 변환하는 역할을 한다.

Spring MVC에서는 핸들러 메서드에 @ResponseBody 애너테이션이 붙거나 핸들러 메서드의 리턴 값이 ResponseEntity 일 경우, 내부적으로 HttpMessageConverter 가 동작하게 되어 응답 객체(여기서는 DTO 클래스의 객체)를 JSON 형식으로 바꿔준다.


직렬화 | 역직렬화

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

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

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

JSON 직렬화(Serialization): Java 객체 → JSON

JSON 역직렬화(Deserialization): JSON → Java 객체

정리

  • DTO는 Data Transfer Object의 약자로 마틴 파울러(Martin Fowler)가 ‘Patterns of Enterprise Application Architecture’ 라는 책에서 처음 소개한 엔터프라이즈 애플리케이션 아키텍처 패턴의 하나이다.

  • DTO는 주로 클라이언트에서 서버 쪽으로 전송하는 요청 데이터를 전달 받을 때, 서버에서 클라이언트 쪽으로 전송하는 응답 데이터를 전송하기 위한 용도로 사용된다.

  • DTO가 필요한 이유

    • 클라이언트의 Request Body를 하나의 객체로 모두 전달 받을 수 있기때문에 코드 자체가 간결해진다.
    • Request Body의 데이터 유효성(Validation) 검증이 단순해진다.
  • JSON 형식의 Request Body를 전달 받기 위해서는 DTO 객체에 @RequestBody 애너테이션을 붙여야 한다.

  • Response Body를 JSON 형식으로 전달하기 위해서는 @ResponseBody 애너테이션을 메서드 앞에 붙여 주어야하지만 ResponseEntity 객체를 리턴 값으로 사용할 경우 @ResponseBody 를 생략할 수 있다.

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

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


DTO 유효성 검증(Validation)이 필요한 이유

그림과 같이 잘못된 형식의 이메일 주소를 입력하면 프런트엔드 쪽에서 1차적으로 먼저 유효성 검사를 진행한 후에 사용자에게 이를 알려주는 것이 일반적이다.

프런트엔드 쪽에서 유효성 검증을 하는데 굳이 백엔드 애플리케이션 쪽에서 유효성 검증을 할 필요가 있나 싶지만 프런트엔드 쪽에서 유효성 검사에 통과되었다고 그 값이 반드시 유효한 값이라고 보장할 수 없다.

그림에서 유효성 검사를 통과한 뒤 등록 버튼을 누르면 서버 쪽으로 HTTP Request 요청이 전송될 것이다.

하지만 자바스크립트로 전송되는 데이터는 브라우저의 개발자 도구를 사용해서 브레이크포인트(breakpoint)를 추가한 뒤에 얼마든지 그 값을 조작할 수 있기 때문에 프론트엔드 쪽에서 유효성 검사를 진행했다고 하더라도 서버 쪽에서 한번 더 유효성 검사를 진행해야 된다!

프론트엔드 쪽에서 진행하는 유효성 검사는 사용자 편의성때문에 진행한다고 생각해볼 수 있다.

유효성 검증 적용하기

  • Spring Boot 에서 유효성 검증을 적용하기 위해서는 build.gradle 에 아래의 코드를 추가해야한다.
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-validation'
}
  • @Email

    • 유효한 이메일 주소 형식인지 검증한다.
    • 유효성 검증에 실패하면 내장된 디폴트 에러 메시지가 콘솔에 출력된다.
  • @NotBlank

    • 정보가 비어있지 않은지를 검증한다.
    • null 값이나 공백(””), 스페이스(” “) 같은 값들을 모두 허용하지 않는다.
    • 유효성 검증에 실패하면 에러 메시지가 콘솔에 출력 된다.
  • @Pattern

    • 작성 내용이 정규표현식(Reqular Expression)에 매치되는 유효한 내용인지를 검증한다.
    • 유효성 검증에 실패하면 내장된 디폴트 에러 메시지가 콘솔에 출력된다.

정리

  • 프런트엔드 쪽에서 유효성 검증을 진행했다 하더라도 서버 쪽에서 추가적으로 유효성 검증을 반드시 진행해야 한다.

  • 프런트엔드 쪽에서의 유효성 검증 프로세스는 사용자 편의성 측면에서 필요한 작업이다.

  • Jakarta Bean Validation의 애너테이션을 이용하면 Controller 로직에서 유효성 검증 로직을 분리할 수 있다.

  • Jakarta Bean Validation은 애너테이션 기반 유효성 검증을 위한 표준 스펙이다.

  • Hibernate Validator는 Jakarta Bean Validation 스펙을 구현한 구현체이다.

  • Spring에서 지원하는 @Validated 애너테이션을 사용하면 쿼리 파라미터(Query Parameter 또는 Query String) 및 @Pathvariable에 대한 유효성 검증을 진행할 수 있다.

  • Jakarta Bean Validation에서 빌트인(Built-in)으로 지원하지 않는 애너테이션은 Custom Validator를 통해 Custom Annotation을 구현한 후, 적용할 수 있다.


References

0개의 댓글