TIL - 20260312

juni·2026년 3월 12일

TIL

목록 보기
290/318

0312 스프링 부트 프로젝트 (4/N): 유효성 검증과 전역 예외 처리


✅ 1. 데이터 유효성 검증 (Bean Validation)

  • 클라이언트로부터 받은 데이터(DTO)가 비즈니스 규칙에 맞는지(e.g., null이 아닌지, 이메일 형식인지) 검증하는 것은 시스템의 안정성과 데이터 무결성을 위해 필수적입니다. Spring Boot는 Bean Validation 표준을 통해 이를 선언적으로 처리할 수 있게 돕습니다.

➕ 의존성 추가 (build.gradle)

  • spring-boot-starter-web에 기본적으로 포함되어 있어, 별도의 의존성을 추가할 필요가 없는 경우가 많습니다. 만약 없다면 추가합니다.
    implementation 'org.springframework.boot:spring-boot-starter-validation'

➕ 주요 검증 어노테이션

어노테이션설명
@NotBlanknull이 아니고, 최소한 하나의 비공백 문자를 포함해야 함. (문자열 전용)
@NotNullnull이 아니어야 함. (모든 타입)
@NotEmptynull이 아니고, 비어있지 않아야 함. (문자열, 컬렉션, 맵)
@Size(min=, max=)문자열, 컬렉션 등의 크기가 지정된 범위 내에 있어야 함.
@Email유효한 이메일 형식이어야 함.
@Min(value) / @Max(value)지정된 숫자 값 이상/이하이어야 함.
@Pattern(regexp=)지정된 정규 표현식과 일치해야 함.

➕ DTO 및 컨트롤러 적용

  • @Valid 어노테이션을 컨트롤러 메서드의 파라미터(주로 @RequestBody가 붙은 DTO) 앞에 붙이면, Spring이 해당 DTO에 정의된 검증 규칙을 자동으로 검사합니다.

  • 만약 검증에 실패하면, MethodArgumentNotValidException이라는 예외가 발생합니다.

    // PostCreateRequest.java
    @Getter
    @NoArgsConstructor
    public class PostCreateRequest {
        @NotBlank(message = "제목은 필수 입력 값입니다.")
        @Size(max = 100, message = "제목은 100자 이내로 입력해주세요.")
        private String title;
    
        @NotEmpty(message = "내용은 비어있을 수 없습니다.")
        private String content;
    }
    
    // PostController.java
    @PostMapping
    public ResponseEntity<Void> createPost(@Valid @RequestBody PostCreateRequest request) {
        // @Valid: DTO의 검증 어노테이션을 기반으로 유효성 검사를 수행
        Long postId = postService.createPost(request);
        return ResponseEntity.created(URI.create("/api/posts/" + postId)).build();
    }

✅ 2. 전역 예외 처리 (Global Exception Handling)

  • 애플리케이션 전역에서 발생하는 예외를 한 곳에서 중앙 집중적으로 처리하여, 사용자에게 일관된 에러 응답을 제공하고 컨트롤러의 코드 중복을 제거합니다.

@RestControllerAdvice (또는 @ControllerAdvice)

  • 개념: 모든 @RestController에서 발생하는 예외를 가로채서 처리하는 클래스를 선언합니다.
  • @ExceptionHandler: 특정 예외 타입을 지정하여, 해당 예외가 발생했을 때 실행될 메서드를 정의합니다.

➕ 커스텀 예외와 에러 코드 설계

  • 비즈니스 로직 상의 다양한 오류 상황(e.g., "게시글을 찾을 수 없음", "이미 존재하는 이메일")을 명확하게 표현하기 위해, 커스텀 예외 클래스에러 코드(Enum)를 설계하는 것이 좋습니다.
// ErrorCode.java (Enum)
@Getter
@RequiredArgsConstructor
public enum ErrorCode {
    POST_NOT_FOUND(HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다."),
    INVALID_INPUT(HttpStatus.BAD_REQUEST, "입력값이 올바르지 않습니다.");

    private final HttpStatus status;
    private final String message;
}

// BusinessException.java
@Getter
public class BusinessException extends RuntimeException {
    private final ErrorCode errorCode;

    public BusinessException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }
}

➕ 전역 예외 핸들러 구현 예시

// ErrorResponse.java (에러 응답 DTO)
@Getter
@AllArgsConstructor
public class ErrorResponse {
    private int status;
    private String message;
}

// GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler {

    // 우리가 직접 정의한 비즈니스 예외 처리
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {
        ErrorCode errorCode = e.getErrorCode();
        return ResponseEntity
                .status(errorCode.getStatus())
                .body(new ErrorResponse(errorCode.getStatus().value(), errorCode.getMessage()));
    }

    // @Valid 검증 실패 예외 처리
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException e) {
        // 여러 검증 오류 중 첫 번째 오류 메시지를 사용
        String errorMessage = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        return ResponseEntity
                .status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), errorMessage));
    }

    // 그 외 예상치 못한 모든 서버 예외 처리
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(Exception e) {
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), "서버 내부 오류가 발생했습니다."));
    }
}

📌 요약

  • spring-boot-starter-validation@Valid 어노테이션을 통해, DTO에 선언된 검증 규칙을 컨트롤러에서 자동으로 검사하여 데이터의 무결성을 보장할 수 있습니다.
  • @RestControllerAdvice@ExceptionHandler를 사용하면, 애플리케이션 전역에서 발생하는 예외를 중앙에서 일관되게 처리할 수 있습니다.
  • 커스텀 예외에러 코드(Enum)를 설계하여 비즈니스 오류를 체계적으로 관리하고, 일관된 에러 응답 DTO를 사용하여 클라이언트에게 명확한 피드백을 제공하는 것이 안정적인 API 설계의 핵심입니다.

0개의 댓글