TIL - 20251114

juni·2025년 11월 14일

TIL

목록 보기
177/317

1114 Spring Boot 심화: 예외 처리, 검증, 비동기 처리


✅ 1. Spring의 예외 처리 (Exception Handling)

  • 애플리케이션 실행 중 발생하는 예외를 처리하여, 사용자에게는 친절한 에러 메시지를 보여주고, 시스템은 비정상적으로 종료되지 않도록 관리하는 것은 매우 중요합니다. Spring은 예외를 효율적으로 처리하기 위한 여러 방법을 제공합니다.

@ExceptionHandler

  • 개념: 특정 컨트롤러 내에서 발생한 예외를 잡아 처리하는 메서드를 지정하는 어노테이션입니다.
  • 단점: 해당 컨트롤러 내에서만 유효하므로, 여러 컨트롤러에서 발생하는 공통 예외를 처리하려면 코드 중복이 발생합니다.

@RestControllerAdvice (또는 @ControllerAdvice)

  • 개념: 애플리케이션 전역(Global)에 걸쳐 발생하는 예외를 한 곳에서 중앙 집중적으로 처리할 수 있게 해주는 강력한 기능입니다.

  • 동작 방식: @RestControllerAdvice 어노테이션이 붙은 클래스 내에 @ExceptionHandler 메서드를 작성하면, 어떤 컨트롤러에서 예외가 발생하든 이 클래스가 예외를 가로채 처리합니다.

  • 장점:

    • 예외 처리 로직의 중앙화: 예외 처리 코드가 비즈니스 로직과 완전히 분리되어 코드의 가독성과 유지보수성이 향상됩니다.
    • 일관된 에러 응답: 모든 API가 동일한 형식의 에러 메시지를 반환하도록 표준화할 수 있습니다.
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        // BusinessException이라는 커스텀 예외를 전역적으로 처리
        @ExceptionHandler(BusinessException.class)
        public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
            ErrorCode errorCode = ex.getErrorCode();
            ErrorResponse response = new ErrorResponse(errorCode);
            return new ResponseEntity<>(response, errorCode.getHttpStatus());
        }
    
        // 모든 종류의 Exception을 처리 (가장 마지막에 위치)
        @ExceptionHandler(Exception.class)
        public ResponseEntity<ErrorResponse> handleException(Exception ex) {
            // ...
        }
    }

✅ 2. 데이터 검증 (Validation)

  • 클라이언트로부터 받은 데이터(e.g., Request DTO)가 비즈니스 규칙에 맞는지(e.g., null이 아닌지, 이메일 형식인지) 검증하는 것은 데이터의 무결성을 지키기 위한 필수적인 과정입니다.

spring-boot-starter-validation

  • Spring Boot는 Jakarta Bean Validation (과거 Java EE Validation) 표준을 쉽게 사용할 수 있도록 이 스타터 의존성을 제공합니다.
  • DTO의 필드에 @NotNull, @NotEmpty, @Email, @Size 등과 같은 어노테이션을 붙여 검증 규칙을 선언적으로 정의할 수 있습니다.

@Valid 어노테이션

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

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

  • 검증 예외 처리: 위에서 설명한 @RestControllerAdvice를 사용하여, MethodArgumentNotValidException을 잡아내고, 어떤 필드가 어떤 규칙을 위반했는지에 대한 상세한 정보를 담아 사용자 친화적인 에러 메시지를 생성하여 응답할 수 있습니다.

    // Request DTO
    public class SignUpRequest {
        @NotEmpty(message = "이메일은 필수입니다.")
        @Email(message = "이메일 형식이 올바르지 않습니다.")
        private String email;
    
        @Size(min = 8, message = "비밀번호는 8자 이상이어야 합니다.")
        private String password;
    }
    
    // Controller
    @RestController
    public class AuthController {
        @PostMapping("/signup")
        public ResponseEntity<Void> signUp(@Valid @RequestBody SignUpRequest request) {
            // 검증을 통과한 경우에만 이 로직이 실행됨
            authService.signUp(request);
            return ResponseEntity.status(HttpStatus.CREATED).build();
        }
    }

✅ 3. 비동기 처리 (@Async)

  • 문제점: 웹 애플리케이션에서 이메일 발송, 파일 업로드, 외부 API 호출 등 시간이 오래 걸리는 작업을 동기(Synchronous) 방식으로 처리하면, 해당 작업이 끝날 때까지 스레드가 차단(Blocking)되어 전체적인 시스템의 응답성과 처리량이 저하됩니다.

  • 해결책 (비동기 처리): 시간이 오래 걸리는 작업을 별도의 스레드에서 실행하도록 위임하고, 원래의 스레드는 즉시 다음 작업을 계속하거나 사용자에게 응답을 반환하는 방식입니다.

➕ Spring의 @Async 어노테이션

  • Spring은 @Async 어노테이션을 통해 비동기 처리를 매우 간단하게 구현할 수 있도록 지원합니다.

  • 사용법:

    1. @EnableAsync: 메인 애플리케이션 클래스에 @EnableAsync 어노테이션을 붙여 비동기 기능을 활성화합니다.
    2. @Async: 별도의 스레드에서 실행하고 싶은 public 메서드@Async 어노테이션을 붙입니다. 이 메서드는 반드시 다른 클래스(Bean)에서 호출되어야 합니다. (AOP 프록시 방식)
    3. 반환 타입:
      • 반환 값이 없는 경우: void
      • 반환 값이 필요한 경우: Future<T> 또는 CompletableFuture<T>
    @Service
    public class NotificationService {
    
        @Async // 이 메서드는 별도의 스레드에서 비동기적으로 실행됨
        public void sendEmail(String to, String subject, String body) {
            // 시간이 오래 걸리는 이메일 발송 로직
            System.out.println("Sending email to: " + to);
            // ...
            System.out.println("Email sent!");
        }
    }
    
    // Controller에서 호출
    @RestController
    public class OrderController {
        // ...
        @PostMapping("/orders")
        public ResponseEntity<String> createOrder() {
            // ... 주문 생성 로직 ...
    
            // 이메일 발송은 비동기로 위임하고, 즉시 사용자에게 응답
            notificationService.sendEmail("user@example.com", "주문 완료", "...");
    
            return ResponseEntity.ok("주문이 성공적으로 생성되었습니다.");
        }
    }

📌 요약

  • @RestControllerAdvice@ExceptionHandler를 사용하면, 애플리케이션 전역에서 발생하는 예외를 중앙에서 일관되게 처리할 수 있습니다.
  • spring-boot-starter-validation@Valid 어노테이션을 통해, DTO에 선언된 검증 규칙을 자동으로 검사하여 데이터의 무결성을 보장할 수 있습니다.
  • 시간이 오래 걸리는 작업(이메일 발송 등)은 @Async 어노테이션을 사용하여 비동기적으로 처리함으로써, 시스템의 전체적인 응답성과 처리량을 향상시킬 수 있습니다.

0개의 댓글