@ExceptionHandler@RestControllerAdvice (또는 @ControllerAdvice)개념: 애플리케이션 전역(Global)에 걸쳐 발생하는 예외를 한 곳에서 중앙 집중적으로 처리할 수 있게 해주는 강력한 기능입니다.
동작 방식: @RestControllerAdvice 어노테이션이 붙은 클래스 내에 @ExceptionHandler 메서드를 작성하면, 어떤 컨트롤러에서 예외가 발생하든 이 클래스가 예외를 가로채 처리합니다.
장점:
@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) {
// ...
}
}
spring-boot-starter-validationJakarta Bean Validation (과거 Java EE Validation) 표준을 쉽게 사용할 수 있도록 이 스타터 의존성을 제공합니다.@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();
}
}
@Async)문제점: 웹 애플리케이션에서 이메일 발송, 파일 업로드, 외부 API 호출 등 시간이 오래 걸리는 작업을 동기(Synchronous) 방식으로 처리하면, 해당 작업이 끝날 때까지 스레드가 차단(Blocking)되어 전체적인 시스템의 응답성과 처리량이 저하됩니다.
해결책 (비동기 처리): 시간이 오래 걸리는 작업을 별도의 스레드에서 실행하도록 위임하고, 원래의 스레드는 즉시 다음 작업을 계속하거나 사용자에게 응답을 반환하는 방식입니다.
@Async 어노테이션Spring은 @Async 어노테이션을 통해 비동기 처리를 매우 간단하게 구현할 수 있도록 지원합니다.
사용법:
@EnableAsync: 메인 애플리케이션 클래스에 @EnableAsync 어노테이션을 붙여 비동기 기능을 활성화합니다.@Async: 별도의 스레드에서 실행하고 싶은 public 메서드에 @Async 어노테이션을 붙입니다. 이 메서드는 반드시 다른 클래스(Bean)에서 호출되어야 합니다. (AOP 프록시 방식)voidFuture<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 어노테이션을 사용하여 비동기적으로 처리함으로써, 시스템의 전체적인 응답성과 처리량을 향상시킬 수 있습니다.