HTTP 요청 / 응답에서의 DTO
마틴 파울러(Martin Fowler)가 ‘Patterns of Enterprise Application Architecture’라는 책에서 처음 소개한 엔터프라이즈 애플리케이션 아키텍처 패턴의 하나이다.
주로 클라이언트와 서버 사이에서 데이터를 주고받을 때 사용한다. 클라이언트의 Request Body를 하나의 객체로 전달받을 수 있기 때문에 코드가 간결해지며, Request Body의 데이터 유효성 검증이 단순해진다.
중요한 목적은 비용이 많이 드는 작업인 HTTP 요청의 수를 줄이고, 도메인 객체와 분리하기 위함이다.
🍑 코드의 간결성
@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> map = new HashMap<>();
map.put("email", email);
map.put("name", name);
map.put("phone", phone);
return new ResponseEntity<Map>(map, HttpStatus.CREATED);
}
}
클라이언트로부터 Parameter를 통해 전달받은 email,name, phone 데이터를 아래와 같이 간결하게 MemberDto만으로 한 번에 전달받을 수 있다.
@RestController
@RequestMapping("/v1/members")
public class MemberController {
@PostMapping
public ResponseEntity postMember(MemberDto memberDto) {
return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
}
}
🍑 데이터 유효성 검증의 단순화
유효성 검증이란 서버 쪽에서 유효한 데이터를 전달받기 위해 데이터를 검증하는 것이다.
public class MemberDto {
@Email // 유효한 이메일 주소가 포함되어 있지 않으면 클라이언트의 요청이 거부된다
private String email;
private String name;
private String phone;
}
@RestController
@RequestMapping("/v1/members")
public class MemberController {
@PostMapping
// @Valid: MemberDto 객체에 유효성 검증을 적용한다.
public ResponseEntity postMember(@Valid MemberDto memberDto) {
return new ResponseEntity<MemberDto>(memberDto, HttpStatus.CREATED);
}
...
}
🍑 적용하기
- Reqeust Body에 담겨서 JSON 형식으로 전달될 데이터를 다룰 DTO 클래스를 생성한다.
DTO 클래스에는 전달받는 각 데이터 항목을 멤버 변수로 추가한다. - 클라이언트의 요청 데이터를 `@RequestParam` 애너테이션으로 전달받는 핸들러 메서드를 찾는다.
Request Body를 사용하는 핸들러는 POST, PATCH, PUT 같이 리소스의 추가나 변경이 발생할 때이다. - `@RequestParam` 코드를 DTO 클래스의 객체로 수정한다.
- Map 객체로 작성되어 있는 메서드 바디를 DTO 클래스의 객체로 변경한다.
- `@RequestBody`: JSON 형식의 Request Body → DTO 클래스의 객체로 변환
- `@ResponseBody`: 클라이언트에게 전달하기 위해 DTO 클래스의 객체 → Response Body로 변환
핸들러 메서드에 `@ResponseBody` 애너테이션이 붙거나, 리턴 값이 `ResponseEntity`이면 Spring MVC는 내부적으로 HttpMessageConverter가 동작하게 되어 응답 객체를 JSON 형식으로 바꿔 준다.
JSON → DTO : 역직렬화(Deserialization)
DTO → JSON : 직렬화(Serialization)
DTO 유효성 검증(Validation)
//build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
...
...
}
public class MemberPostDto {
@NotBlank
@Email
private String email;
@NotBlank(message = "이름은 공백이 아니어야 합니다.")
private String name;
@Pattern(regexp = "^010-\\d{3,4}-\\d{4}$",
message = "휴대폰 번호는 010으로 시작하는 11자리 숫자와 '-'로 구성되어야 합니다.")
private String phone;
@PostMapping
public ResponseEntity postMember(@Valid @RequestBody MemberPostDto memberPostDto) {
return new ResponseEntity<>(memberPostDto, HttpStatus.CREATED);
}
- `@NotBlank`: null 값이나 공백(””), 스페이스(” “) 같은 값들을 모두 허용하지 않는다.
- 유효성 검증에 실패하면 message 애트리뷰트에 지정한 문자열이 에러 메시지로 콘솔에 출력된다.
- `@Email`: 유효한 이메일 주소인지를 검증한다.
- `@Pattern`: 정규 표힌식에 매치되는지 검증한다.
쿼리 파라미터 및 Pathvariable 검증
@RestController
@RequestMapping("/v1/members")
@Validated //쿼리 파라미터 검증 애너테이션 넣기
public class MemberController {
@PatchMapping("/{member-id}")
//파라미터의 memberId는 최소값이 1이다
public ResponseEntity patchMember(@PathVariable("member-id") @Min(1) long memberId,
@Valid @RequestBody MemberPatchDto memberPatchDto) {
Jakarta Bean Validation
유효성 검증을 위한 표준 스펙에서 지원하는 내장 애너테이션 모음
라이브러리처럼 사용할 수 있는 API가 아닌 스펙(사양, Specification) 자체
구현체: Hibernate Validator
- Jakarta Bean Validation Specification
- Jakarta Bean Validation Built-in Constraint definitions
- Hibernate Validator
- Java Bean 관련 자료
Custom Validator
원하는 목적에 맞는 애너테이션을 직접 만들어서 적용할 수 있다. 모든 로직을 정규 표현식로 작성하는 것은 좋은 개발 방식이 아니다.
- Custom Validator를 사용하기 위한 Custom Annotation을 정의한다.
- 정의한 Custom Annotation에 바인딩 되는 Custom Validator를 구현한다.
- 유효성 검증이 필요한 DTO 클래스의 멤버 변수에 Custom Annotation을 추가한다.
'Back-End > Spring' 카테고리의 다른 글
Spring MVC 패턴의 예외 처리 | @ExceptionHandeler, @RestControllerAdvice (0) | 2022.08.24 |
---|---|
서비스 계층에서의 DI (0) | 2022.08.23 |
[Error] unknown enum constant When.MAYBE (0) | 2022.08.16 |
AOP(Aspect-Oriented Programming) : 애너테이션(Annotation)의 이용 (0) | 2022.08.16 |
AOP(Aspect-Oriented Programming) : JoinPoint (0) | 2022.08.16 |
댓글