728x90
유효성 검증의 필요성
클라이언트가 회원가입 요청을 보낼 때, 서버는 사용자의 이름, 나이, 이메일, 아이디, 비밀번호, 생일, 가입 날짜 등이 올바른 형식인지 확인해야 합니다. 예를 들어:
- 이름을 입력하지 않았을 때는?
- 나이가 -10살이나 200살일 때는?
- 비밀번호가 "123"처럼 보안에 취약할 때는?
이러한 검증을 if 문을 통해 하나하나 확인할 수 있지만, 검증할 항목이 많아질수록 코드의 길이는 길어질 수밖에 없습니다. 각 항목에 대해 개별적으로 검증하면 코드가 길어지고 중요한 서비스 로직이 누락될 수 있습니다.
Spring Boot에서 유효성 검증
이러한 문제를 쉽게 해결하기 위해 Spring Boot에서는 spring-boot-starter-validation 의존성을 사용할 수 있습니다.
Validation 2.0 스펙
https://beanvalidation.org/2.0/spec/#builtinconstraints
- Validation을 사용하는 이유
- 유효성 검증 코드의 길이 감소: 유효성 검증을 별도로 처리하여 서비스 로직을 간결하게 유지할 수 있습니다.
- 일관성 유지: 여러 명이 코딩할 때, 각자의 스타일에 따라 유효성 검증을 하게 되면 일관성을 유지하기 어렵습니다.
- 유지 보수 용이성: 검증 로직이 변경될 때, 테스트 코드 등 전체 로직에 미치는 영향을 최소화할 수 있습니다.
자주 사용하는 Validation 어노테이션
- @Size: 문자열의 길이 검증 (정수 타입에는 사용 불가)
- @NotNull: null 불가
- @NotEmpty: null과 빈 문자열 불가 (" "는 가능)
- @NotBlank: null, 빈 문자열, 공백 문자열 불가
- @Pattern: 정규식 사용
- @Max: 최대값 (정수 타입)
- @Min: 최소값 (정수 타입)
- @AssertTrue/False: 논리값 검증
- @Valid: 객체 내부의 validation 실행
- @Past: 과거 날짜
- @PastOrPresent: 과거 또는 오늘 날짜
- @Future: 미래 날짜
- @FutureOrPresent: 미래 또는 오늘 날짜
간단한 예제: 학생의 정보와 점수 받기
Student 모델 만들기
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student {
private String id;
private String password;
private String email;
private String name;
private Integer score;
}
기본 Post 컨트롤러 생성
import lombok.extern.slf4j.Slf4j;
import org.example.model.Student;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequestMapping("/student/info")
public class StudentValidationController {
@PostMapping("")
public Student studentInfo(
@RequestBody
Student student
) {
log.info("info : {}", student);
return student;
}
}
유효성 검사를 하지 않았을 때
{
"id": " ",
"password": "",
"name": "홍길동",
"email": "hong.com",
"score": 1000
}
아이디와 비밀번호는 입력하지 않았고, 이메일도 형식에 맞지 않습니다.
점수도 100점을 넘길 수 없는데 1000점으로 잘못 입력되어 있지만
유효성 검사를 하지 않았기 때문에 오류가 발생하지 않습니다.
Validation을 사용해보기
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student {
@NotBlank // null, "", " " 를 허용하지 않음
@Size(min = 3, max = 10) // 문자열 크기 3~10
private String id;
@NotBlank
@Size(min = 4, max = 12)
private String password;
@Email // email 형식인지
private String email;
@NotBlank
private String name;
@Min(1) @Max(100) // 정수형은 Min, Max 사용
private Integer score;
}
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.example.model.Student;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
@RequestMapping("/student/info")
public class StudentValidationController {
@PostMapping("")
public Student studentInfo(
@Valid // Valid 어노테이션 추가
@RequestBody
Student student
) {
log.info("info : {}", student);
return student;
}
}
유효성 검사를 적용한 후
이렇게 했을 때 똑같은 데이터를 보내면:
2024-07-04T16:18:04.614+09:00 WARN 8584 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.example.model.Student org.example.controller.StudentValidationController.studentInfo(org.example.model.Student)
with 6 errors: [Field error in object 'student' on field 'id': rejected value [ ]; codes [Size.student.id,Size.id,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.id,id]; arguments []; default message [id],10,3]; default message [크기가 3에서 10 사이여야 합다]]
[Field error in object 'student' on field 'email': rejected value [hong.com]; codes [Email.student.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.email,email]; arguments []; default message [email],[Ljakarta.validation.constraints.Pattern$Flag;@3cd0ea2a,.*]; default message [올바른 형식의 이메일 주소여야 합니다]]
[Field error in object 'student' on field 'password': rejected value []; codes [Size.student.password,Size.password,Size.java.lang.String,Size]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.password,password]; arguments []; default message [password],12,4]; default message [크기가 4에서 12 사이여야 합니다]]
[Field error in object 'student' on field 'password': rejected value []; codes [NotBlank.student.password,NotBlank.password,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.password,password]; arguments []; default message [password]]; default message [공백일 수 없습니다]]
[Field error in object 'student' on field 'score': rejected value [1000]; codes [Max.student.score,Max.score,Max.java.lang.Integer,Max]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.score,score]; arguments []; default message [score],100]; default message [100 이하여야 합니다]]
[Field error in object 'student' on field 'id': rejected value [ ]; codes [NotBlank.student.id,NotBlank.id,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [student.id,id]; arguments []; default message [id]]; default message [공백일 수 없습니다]] ]
이렇게 에러가 나오는 것을 확인할 수 있습니다.
클라이언트에게 문제를 알리는 방법
현재 코드는 서버가 어떤 문제인지 알 수 있지만, 클라이언트는 400 Bad Request만 알 수 있습니다.
즉, 클라이언트는 어떤 문제인지 파악할 수 없습니다.
클라이언트에게 상세한 오류 메시지를 전송하는 방법은 다른 글에서 정리하겠습니다
728x90
'Spring Boot' 카테고리의 다른 글
[Spring Boot] 유효성 검사와 예외 처리를 통한 API 구현 (0) | 2024.07.05 |
---|---|
[Spring Boot] Validation 유효성 검사 클라이언트 오류 보내기 (0) | 2024.07.04 |
[Spring Boot] Web에서 응답 만드는 방법 - Response Entity (0) | 2024.07.02 |
[Spring Boot] Rest API Put 메서드 + boolean is 변수명의 문제점 (0) | 2024.07.01 |
[Spring Boot] Rest API Post 메서드 (1) | 2024.07.01 |