TIL - 20260202

juni·2026년 2월 2일

TIL

목록 보기
256/316

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


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

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

➕ 의존성 추가 (build.gradle)

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-validation'
}

➕ 주요 검증 어노테이션

어노테이션설명
@NotBlanknull이 아니고, 최소한 하나의 비공백 문자를 포함해야 함. (문자열 전용)
@NotNullnull이 아니어야 함.
@Size(min=, max=)문자열, 배열 등의 크기가 지정된 범위 내에 있어야 함.
@Email유효한 이메일 형식이어야 함.
@Min / @Max지정된 숫자 값 이상/이하이어야 함.

➕ DTO 및 컨트롤러 적용

// PostCreateRequest.java
@Getter
@NoArgsConstructor
public class PostCreateRequest {
    @NotBlank(message = "제목은 필수 입력 값입니다.")
    @Size(max = 100, message = "제목은 100자 이내로 입력해주세요.")
    private String title;

    @NotBlank(message = "내용은 필수 입력 값입니다.")
    private String content;
}

// PostController.java
@PostMapping
public ResponseEntity<ApiResponse<Long>> createPost(@Valid @RequestBody PostCreateRequest request) {
    // @Valid: DTO의 검증 어노테이션을 기반으로 유효성 검사를 수행
    Long postId = postService.createPost(request);
    return ResponseEntity.ok(ApiResponse.success(postId));
}

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

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

@RestControllerAdvice

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

➕ 커스텀 예외 클래스 설계

  • 비즈니스 로직 상의 오류를 명확히 표현하기 위해 RuntimeException을 상속받은 커스텀 예외를 사용합니다.
// BusinessException.java
@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()));
    }

    // @Valid 검증 실패 예외 처리
    @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의 응답 형식을 표준화함으로써 클라이언트와의 통신 규약을 명확히 합니다.

0개의 댓글