❯ DTO(Data Transfer Object)
어제 작성한 코드에서는 클라이언트로부터 요청 데이터를 받을 때 각 파라미터를 @RequestParam 으로 하나씩 직접 삽입하였다.
이러한 방법은 파라미터가 늘어날 경우 코드가 복잡해진다. 또한 유효성 검사가 필요할 경우 코드가 더욱 복잡해지게된다.
이러한 단점을 해결하기 위해 사용하는 것이 DTO 방식이다.
어제 코드를 작성할 때는 아래와 같이 작성했었다.
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/v1/members")
public class MemberController {
@PostMapping
public ResponseEntity postMember(@RequestParam("email") String email,
@RequestParam("name") String name,
@RequestParam("phone") String phone) {
Map<String, String> body = new HashMap<>();
body.put("email", email);
body.put("name", name);
body.put("phone", phone);
return new ResponseEntity<Map>(body, HttpStatus.CREATED);
}
}
위 코드를 DTO 를 사용하게 되면 아래와 같이 간결하게 바꿀 수 있다.
DTO 클래스 적용을 위한 절차는 다음과 같다.
요청받는 DTO 클래스 생성 -> 클라이언트가 요청한 데이터를 @RequestParam으로 전달받는 메서드 찾기 -> 해당 메서드의 파라미터를 DTO 클래스 객체로 변경 -> ResponseBody를 DTO 객체로 변경
- @RequestBody: JSON 형태의 Request body를 DTO 클래스 객체로 변경해 줌 (역직렬화)
- @ResponseBody: DTO 클래스 객체를 JSON 형태로 변경해 줌(직렬화). 핸들러 메서드에 이 애너테이션이 붙거나 리턴값의 타입이
ResponseEntity인 경우 내부적으로 HttpMessageConverter가 동작하여 응답 객체를 JSON으로 변경해줌
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/v1/members")
public class MemberController {
@PostMapping
public ResponseEntity postMember(@RequestBody MemberPostDto memberPostDto){
return new ResponseEntity<>(memberPostDto, HttpsStatus.CREATED);
}
}
public class MemberPostDto {
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;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
❯ DTO 유효성 검증
클라이언트로 요청을 입력받을 때, 해당 요청이 유효한지 확인하는 작업이 필요할 수 있다.
ex. 이메일 입력값이 올바른 형태인지, 전화번호의 형태가 유효한지 등
DTO 객체를 사용하게 되면 DTO 클래스에서 해당 변수 선언부에 적합한 유효성검사 애너테이션을 붙여서 유효성을 검사할 수 있다.
<예시코드>
아래 코드와 같이 @Email(이메일 형식인지 검증), @NotBlank(비어있지않은 값인지 검증), @Pattern(특정 패턴인지 검증)등의 애너테이션을 활용해 유효성 검사를 수행할 수 있다.
@Pattern에서 사용하는 식은 정규표현식으로, 해당 정규표현식을 만족하는지 검증하게 된다.
또한 이러한 유효성 검증이 수행되는 DTO는 컨트롤러에서 @Valid 를 붙여주어야 한다.
// DTO
public class MemberPostDto {
@Email
@NotBlank
private String email;
@NotBlank(message = "이름은 공백이 아니어야 합니다.")
private String name;
@Pattern(regexp = "^010-\\d{3,4}-\\d{4}$",
message = "휴대폰 번호는 010으로 시작하는 11자리 숫자와 '-'로 구성되어야 합니다.")
private String phone;
(...)
}
// 컨트롤러
class MemberController {
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberPostDto) {
return new ResponseEntity<>(memberPostDto, HttpStatus.CREATED);
}
(...)
}
위 코드와 같이 @Email(이메일 형식인지 검증), @NotBlank(비어있지않은 값인지 검증), @Pattern(특정 패턴인지 검증)등의 애너테이션을 활용해 유효성 검사를 수행할 수 있다.
@Pattern에서 사용하는 식은 정규표현식으로, 해당 정규표현식을 만족하는지 검증하게 된다.
또한 경로의 유효성을 검사하는 방법도 있는데, 아래 코드와 같이 @Pathvariable에 유효성 검사 조건을 기재하고, 해당 클래스에 @Validated 애너테이션을 붙여주어야 한다.
// 컨트롤러 -> 경로에 대한 유효성 검증 추가 : @Min(1)
@RestController
@RequestMapping("/v1/members")
@Validated
class MemberController {
@PatchMapping("/{member-id}")
public ResponseEntity patchMember(@PathVariable("member-id") @Min(1) long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
memberPatchDto.setMemberId(memberId);
return new ResponseEntity<>(memberPatchDto, HttpStatus.OK);
}
(...)
}
오늘 진행된 과정은 실습이 많아서 더 재밌게 할 수 있었던 것 같다.
지금은 주어진 내용을 따라가는 형태라서 크게 어려운 부분이 없었지만 나중에 프로젝트가 진행되면 꽤 까다로운 부분이 될 것 같기는 하다.
일단 각 역할과 정규식에 대한 규칙을 좀 더 학습해두어야 할 것 같다.
'부트캠프 개발일기 > Spring MVC' 카테고리의 다른 글
46일차: Spring MVC(JDBC, Spring Data JDBC) (0) | 2023.04.18 |
---|---|
45일차: Spring MVC(비즈니스적인 예외 던지기, throw) (0) | 2023.04.17 |
44일차: Spring MVC(예외처리) (0) | 2023.04.14 |
43일차: Spring MVC(서비스 계층, Mapper) (0) | 2023.04.13 |
41일차: Spring MVC(API 계층, Controller) (0) | 2023.04.11 |