일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- root passwd
- JAVA 재귀함수
- RestControllerAdvice
- 탐욕 알고리즘
- set-version
- Spring
- ubuntu passwd
- git workflow
- ubuntu
- AOP
- 스키마 디자인
- mapstruct
- 자료구조
- 코드스테이츠
- custom exception
- char to int
- Java
- ubuntu 패스워드
- REST HTTP API
- file i/o
- 함수형 인터페이스
- http 응답코드
- N:N
- git 설정
- 리눅스 사용권한
- O(log n)
- Spring 예외처리
- Spring MVC
- 스키마 설계
- 배열 탐색
Archives
- Today
- Total
개발소설
DTO(Data Transfer Object) 본문
- 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)라고 한다.
- java -> json 직렬화, json -> java 역직렬화
DTO 유효성 검증 (Validation)
- 프론트엔드 쪽에서 유효성 검증을 진행했다 하더라도 서버 쪽에서 추가적으로 유효성 검증을 반드시 진행해야 한다.
- 프론트엔드 쪽에서의 유효성 검증 프로세스는 사용자 편의성 측면에서 필요한 작업이다.
- 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을 구현한 후, 적용할 수 있다.
실습 예제
Controller
@RestController
@RequestMapping("/v1/coffees")
public class CoffeeController {
// 1. DTO 클래스 및 유효성 검증을 적용하세요.
@PostMapping
public ResponseEntity postCoffee(@Valid @RequestBody CoffeePostDto coffeePostDto){
return new ResponseEntity<>(coffeePostDto, HttpStatus.CREATED);
}
// 2. DTO 클래스 및 유효성 검증을 적용하세요.
@PatchMapping("/{coffee-id}")
public ResponseEntity patchCoffee(@PathVariable("coffee-id") @Min(1) long coffeeId,
@Valid @RequestBody CoffeePatchDto coffeePatchDto) {
coffeePatchDto.setCoffeeId(coffeeId);
return new ResponseEntity<>(coffeePatchDto, HttpStatus.OK);
}
@GetMapping("/{coffee-id}")
public ResponseEntity getCoffee(@PathVariable("coffee-id") long coffeeId) {
System.out.println("# coffeeId: " + coffeeId);
// not implementation
return new ResponseEntity<>(HttpStatus.OK);
}
@GetMapping
public ResponseEntity getCoffees() {
System.out.println("# get Coffees");
// not implementation
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/{coffee-id}")
public ResponseEntity deleteCoffee(@PathVariable("coffee-id") long coffeeId) {
// No need business logic
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
}
- @RequestBody : DTO 객체를 ResponseEntity 클래스의 생성자 피라미터로 전달
- @Valid : DTO 객체에 유효성 검증을 적용하게 해주는 애너테이션
- @Min(N) : 해당 피라미터가 N이상 인지 검증하는 애너테이션 - @Min(1) -> 1이상
- 예제에선 식별자가 1이상인 경우 검증성공
DTO 클래스 (Patch)
package com.codestates.coffee;
import com.codestates.CustomAnnotation.PriceRange;
import javax.validation.constraints.*;
public class CoffeePatchDto {
private long coffeeId;
@Pattern(regexp = "^\\S+(\\s?\\S+)*$",
message= "한글명은 공백만으로 구성되지 않아야 합니다.")
private String korName;
@Pattern(regexp = "^[a-zA-Z ]*$",
message = "영문명은 영문(대소문자 모두 가능)만 허용합니다.")
@Pattern(regexp = "^\\S+(\\s?\\S+)*$",
message = "영문명은 워드 사이에 한칸의 공백(스페이스)만 포함 될 수 있습니다.")
private String engName;
@PriceRange
private int price;
public long getCoffeeId() {
return coffeeId;
}
public void setCoffeeId(long coffeeId) {
this.coffeeId = coffeeId;
}
public String getKorName() {
return korName;
}
public void setKorName(String korName) {
this.korName = korName;
}
public String getEngName() {
return engName;
}
public void setEngName(String engName) {
this.engName = engName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
- @Pattern : 정규표현식(Reqular Expression)에 매치되는지 검증
- regexp : 정규표현식
- message : 유효성검사를 통과하지 못하는경우 발생시키는 메시지
- ex) 한글명은 공백만으로 구성되지 않아야 합니다.
- 미설정시 내장된 디폴트 에러 메시지가 콘솔에 출력
- 정규표현식 해석
- ^[a-zA-Z ]*$ - 대소문자+공백 가능
- ^\\S+(\\s?\\S+)*$ - 공백 아닌 문자 1개 이상((공백인 문자 0개 또는 1개)(공백이 아닌 문자 1개 이상)) -> 마지막 맨 바깥 쪽 괄호 조건이 0개 이상(즉, 있어도 되고 없어도 된다)
- @PriceRange : 직접 만든 유효성검사 애너테이션 - 포스트 맨 아래 코드 확인 (Custom Validator - PriceRange)
DTO 클래스 (POST)
package com.codestates.coffee;
import com.codestates.CustomAnnotation.PriceRange;
import javax.validation.constraints.*;
public class CoffeePostDto {
@NotBlank(message = "한글명은 공백이 아니어야 합니다.")
private String KorName;
@NotNull(message = "영문명을 반드시 입력해야 합니다.")
@Pattern(regexp = "^[a-zA-Z ]*$",
message = "영문명은 영문(대소문자 모두 가능)만 허용합니다.")
@Pattern(regexp = "^\\S+(\\s?\\S+)*$",
message = "영문명은 워드 사이에 한칸의 공백(스페이스)만 포함 될 수 있습니다.")
private String engName;
@NotNull(message = "가격을 반드시 입력해야 합니다.")
@Max(value = 50000, message = "50000이하")
@Min(value = 100, message = "100이상")
private Integer price;
public String getKorName() {
return KorName;
}
public void setKorName(String korName) {
KorName = korName;
}
public String getEngName() {
return engName;
}
public void setEngName(String engName) {
this.engName = engName;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
- @NotBlank
- 정보가 비어있지 않은지를 검증
- null 값이나 공백(””), 스페이스(” “) 같은 값들을 모두 허용하지 않음
- 유효성 검증에 실패하면 에러 메시지가 콘솔에 출력
- @Max(N)
- 값이 N이하 인지 검증
- @Max(50000) - 50000이하면 검증성공
Custom Validator - PriceRange
package com.codestates.CustomAnnotation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {PriceRangeValidator.class}) // (1)
public @interface PriceRange {
String message() default "가격은 100이상 50000이하를 입력해야합니다."; // (2)
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
- PriceRange 애너테이션이 멤버 변수에 추가되었을때, 동작 할 Custom Validator를 (1)과 같이 추가
- (2) : 애너테이션 추가 시 별도로 정의하지 않으면 유효성 검증 실패 시, 표시되는 디폴트 메시지
package com.codestates.CustomAnnotation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PriceRangeValidator implements ConstraintValidator<PriceRange, Integer> {
@Override
public void initialize(PriceRange constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return value >= 100 && value <= 50000 || value == 0;
}
}
- ConstraintValidator<PriceRange, String>에서 PriceRange는 CustomValidator와 매핑된 Custom Annotation을 의미하며, Integer은 Custom Annotation으로 검증할 대상 멤버 변수의 타입을 의미
- isValid() : 검증할 대상 멤버 변수 검증 로직, return값이 true일 경우 검증 성공, failed일 경우 검증 실패
- 예제에선 값이 0(Integer의 초기값(입력을 안받았을 경우)) 이거나 값이 100~50000사이일 경우 검증 성공
Spring Boot 유효성 검사 (Validation) 관련 어노테이션 - ThinkGround
Spring Boot 유효성 검사 (Validation) 관련 어노테이션 관련 포스팅입니다.어떤 어노테이션이 있는지 확인이 필요하신 분들을 위해 표로 정리하여 공유드립니다. 주로 쓰이는 어노테이션에 대해서 정
thinkground.studio
자주 사용하는 정규표현식 - https://zzokma.tistory.com/1644
정규표현식 - https://regexr.com/
'Spring Framework' 카테고리의 다른 글
Spring MVC 예외처리 (0) | 2023.04.16 |
---|---|
DI를 통한 서비스 계층 ↔ API 계층 연동, 매퍼(Mapper), 엔티티(Entity) (0) | 2023.04.14 |
SpringMVC - Controller (0) | 2023.04.12 |
Spring MVC (0) | 2023.04.12 |
AOP(Aspect Oriented Programming) (0) | 2023.04.08 |
Comments