0202 스프링 부트 프로젝트 (4/N): 유효성 검증과 전역 예외 처리
✅ 1. 데이터 유효성 검증 (Bean Validation)
- 클라이언트로부터 받은 데이터(DTO)가 비즈니스 규칙에 맞는지 검증하는 것은 시스템의 안정성과 데이터 무결성을 위해 필수적입니다. Spring Boot는
Bean Validation 표준을 통해 이를 선언적으로 처리할 수 있게 돕습니다.
➕ 의존성 추가 (build.gradle)
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
➕ 주요 검증 어노테이션
| 어노테이션 | 설명 |
|---|
@NotBlank | null이 아니고, 최소한 하나의 비공백 문자를 포함해야 함. (문자열 전용) |
@NotNull | null이 아니어야 함. |
@Size(min=, max=) | 문자열, 배열 등의 크기가 지정된 범위 내에 있어야 함. |
@Email | 유효한 이메일 형식이어야 함. |
@Min / @Max | 지정된 숫자 값 이상/이하이어야 함. |
➕ DTO 및 컨트롤러 적용
@Getter
@NoArgsConstructor
public class PostCreateRequest {
@NotBlank(message = "제목은 필수 입력 값입니다.")
@Size(max = 100, message = "제목은 100자 이내로 입력해주세요.")
private String title;
@NotBlank(message = "내용은 필수 입력 값입니다.")
private String content;
}
@PostMapping
public ResponseEntity<ApiResponse<Long>> createPost(@Valid @RequestBody PostCreateRequest request) {
Long postId = postService.createPost(request);
return ResponseEntity.ok(ApiResponse.success(postId));
}
✅ 2. 전역 예외 처리 (Global Exception Handling)
- 애플리케이션 전역에서 발생하는 예외를 한 곳에서 중앙 집중적으로 처리하여, 사용자에게 일관된 에러 응답을 제공하고 코드 중복을 제거합니다.
➕ @RestControllerAdvice
- 개념: 모든 컨트롤러에서 발생하는 예외를 가로채서 처리하는 클래스를 선언합니다.
@ExceptionHandler: 특정 예외 타입을 지정하여 해당 예외가 발생했을 때 실행될 메서드를 정의합니다.
➕ 커스텀 예외 클래스 설계
- 비즈니스 로직 상의 오류를 명확히 표현하기 위해
RuntimeException을 상속받은 커스텀 예외를 사용합니다.
@Getter
public class BusinessException extends RuntimeException {
private final ErrorCode errorCode;
public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
✅ 3. 일관된 API 응답 형식 설계
- 성공과 실패 응답의 구조를 통일하여 프론트엔드 개발자가 예측 가능한 데이터를 처리할 수 있도록 합니다.
➕ 공통 응답 객체 (ApiResponse)
@Getter
@AllArgsConstructor
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, "요청 성공", data);
}
public static ApiResponse<Void> error(String message) {
return new ApiResponse<>(false, message, null);
}
}
➕ 전역 예외 핸들러 구현 예시
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException e) {
return ResponseEntity
.status(e.getErrorCode().getStatus())
.body(ApiResponse.error(e.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Void>> handleValidationException(MethodArgumentNotValidException e) {
String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return ResponseEntity.badRequest().body(ApiResponse.error(errorMessage));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Void>> handleException(Exception e) {
return ResponseEntity.internalServerError().body(ApiResponse.error("서버 내부 오류가 발생했습니다."));
}
}
📌 요약
spring-boot-starter-validation을 사용하여 DTO 레벨에서 데이터의 유효성을 선언적으로 검증합니다.
@Valid 어노테이션을 컨트롤러 파라미터에 붙여 검증 로직을 활성화합니다.
@RestControllerAdvice와 @ExceptionHandler를 통해 애플리케이션 전역의 예외 처리 로직을 한 곳으로 모아 관리 효율성을 높입니다.
ApiResponse와 같은 공통 응답 객체를 사용하여 모든 API의 응답 형식을 표준화함으로써 클라이언트와의 통신 규약을 명확히 합니다.