DTO란
Data Transfer Object의 약자로 데이터를 전송하기 위한 용도의 객체로 생각할 수 있다.
클라이언트에서 서버로 전송하는 요청 데이터, 서버에서 클라이언트 쪽으로 전송하는 응답 데이터의 형식으로 클라이언트와 서버 간에 데이터 전송이 이루어진다.
DTO를 사용하는 이유
@PostMapping
public ResponseEntity postMember(@RequestParam("email") String email,
@RequestParam("name") String name,
@RequestParam("phone") String phone) {
Map<String, String> map = new HashMap<>();
map.put("email", email);
map.put("name", name);
map.put("phone", phone);
return new ResponseEntity<Map>(map, HttpStatus.CREATED);
}
기존에는 @RequestParam을 통해 회원 정보를 받아와야 했으며 요청 데이터가 늘어날 때마다 추가해줘야 한다. 하지만 DTO를 통해 PostDto라는 클래스를 만들게 된다면 Dto 클래스가 요청 데이터를 하나의 객체로 전달 받는 역할을 해 간결한 코드를 만들 수 있다.
public class PostDto{
private String email;
private String name;
private String phone;
}
@PostMapping
public ResponseEntity postMember(PostDto postDto) {
return new ResponseEntity<PostDto>(postDto, HttpStatus.CREATED);
}
Dto를 사용하지 않으면 핸들러 메서드에서 유효성을 검증해야 하며 여러 데이터의 유효성을 검증해야 한다면 다음과 같이 많은 로직이 필요하게 되고 코드의 복잡성이 올라갈 것이다.
@PostMapping
public ResponseEntity postMember(@RequestParam("email") String email,
@RequestParam("name") String name,
@RequestParam("phone") String phone) {
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);
map.put("phone", phone);
return new ResponseEntity<Map>(map, HttpStatus.CREATED);
}
DTO 클래스를 사용하면 유효성 검증 로직을 DTO 클래스로 빼내 핸들러 메서드의 간결함을 유지할 수 있다. 아래의 코드에서는 @Email 어노테이션이 이메일의 유효성을 확인해준다.
public class PostDto{
@Email
private String email;
private String name;
private String phone;
}
@PostMapping
public ResponseEntity postMember(@Valid MemberDto memberDto) {
return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
}
결론적으로 DTO를 사용하면 코드가 간결해지고 로직이 단순해진다.
DTO 만들기
DTO를 만들 때에는 멤버 변수 이외에 getter 메서드를 추가해주어야 한다. setter 메서드의 경우는 필수는 아니지만, 필요에 따라 존재할 수 있다. 항상 getter/setter를 설정하는 것은 매우 귀찮은 일이기에 @Getter
, @Setter
어노테이션을 DTO 클래스레벨에 사용하면 lombok
라이브러리가 자동으로 만들어준다.
Spring MVC에서는 핸들러 메서드에 @RequestBody
가 붙거나 핸들러 메서드의 리턴 값이 ResponseEntity
인 경우, 내부적으로 HttpMessageConverter
가 자동으로 JSON 형식을 반환해준다.
이러한 과정을 직렬화와 역직렬화로 부를 수 있다.
클라이언트에서 JSON 형식의 데이터를 서버로 전송하면 서버에서는 JSON 형식의 데이터를 DTO 같은 자바의 객체로 변환하는 것을 말한다.
역직렬화의 반대로 서버에서 클라이언트로 DTO 같은 자바 객체를 JSON 형식으로 변환하는 것을 말한다.
DTO 클래스의 단점
DTO 클래스는 Controller 클래스가 늘어남에 따라 DTO 클래스가 무수히 많아지게 된다. 이러한 부분은 공통 부분을 추출 및 내부 클래스를 이용하여 어느 정도 개선이 가능하다.
DTO 유효성 검증
유효성 검증을 하기 위해서는 아래 의존 라이브러리가 필요하다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
데이터가 null
, ""
, " "
인 경우 허용하지 않는다.
정규 표현식을 통해 데이터의 유효성을 검증할 수 있다.
^
은 문자열의 시작을 의미$
는 문자열의 끝을 의미*
는 *
앞에 평가할 대상이 0개 또는 1개 이상인지를 평가\s
는 공백 문자열을 의미\S
공백 문자열이 아닌 나머지 문자열을 의미?
는 ?
앞에 평가할 대상이 0개 또는 1개인지를 의미+
는 +
앞에 평가할 대상이 1개인지를 의미^\\S+(\\s?\\S+)*$
를 정규 표현식으로 추가하면 아래 예시의 문자열은 유효성 검증에 실패
""
→ 공백 문자만 있으므로 검증 실패
" 홍길동"
→ 시작 문자가 공백이므로 검증 실패
"홍길동 "
→ 끝 문자가 공백이므로 검증 실패
"홍 길동"
→ 문자와 문자 사이의 공백이 1개를 초과하므로 검증 실패
@PathVariable("member-id") @Range(min = 1, max = 10000) long memberId,
@Valid @RequestBody MemberDto memberDto)
숫자를 검증하기 위해 @Min
, @Max
, @Range(min = 1, max = 10)
, @Positive
등을 사용할 수 있다.
해당 유효성의 검증을 위해서는 클래스 레벨에서 @Validated 어노테이션을 사용해야 유효성 검증이 정상적으로 동작한다. 또한 DTO 클래스의 유효성 검증 로직이 실행되게 하기 위해서는 @Valid 어노테이션을 사용해야 한다.