관리 메뉴

♠개발자의 작은 서재♠

@ExceptionHandler와 @ControllerAdvice를 활용한 글로벌 예외 처리 본문

Spring MVC/웹 애플리케이션 개발

@ExceptionHandler와 @ControllerAdvice를 활용한 글로벌 예외 처리

♠디지털 모험일지♠ 2024. 10. 21. 20:29

Spring Framework에서 애플리케이션 전반에 걸쳐 발생하는 예외를 효율적으로 관리하기 위해 @ExceptionHandler@ControllerAdvice를 사용할 수 있습니다. 이 두 어노테이션을 사용하면 특정 컨트롤러나 애플리케이션 전체에서 발생하는 예외를 전역적으로 처리할 수 있습니다.

@ExceptionHandler

@ExceptionHandler는 특정 예외가 발생했을 때 이를 처리하는 메서드를 정의하는 데 사용됩니다. 이 어노테이션은 개별 컨트롤러 클래스 안에서 사용할 수 있으며, 해당 컨트롤러에서 발생하는 특정 예외를 처리하도록 합니다.

java코드 복사@RestController
public class ExampleController {

    @GetMapping("/example")
    public String getExample() {
        if (true) { // 예외 상황 예시
            throw new IllegalArgumentException("잘못된 요청입니다.");
        }
        return "Hello, World!";
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

위의 예시에서 IllegalArgumentException이 발생하면 handleIllegalArgumentException 메서드가 호출되어 적절한 응답을 반환합니다.

@ControllerAdvice

@ControllerAdvice는 모든 컨트롤러에서 발생하는 예외를 전역적으로 처리하는 데 사용됩니다. 이를 통해 중복 코드를 줄이고, 애플리케이션 전반에 걸쳐 일관된 예외 처리를 구현할 수 있습니다.

java코드 복사@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGeneralException(Exception ex) {
        return new ResponseEntity<>("서버에서 오류가 발생했습니다.", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

위의 GlobalExceptionHandler 클래스는 모든 컨트롤러에서 발생하는 예외를 처리합니다. IllegalArgumentException뿐만 아니라, 일반적인 Exception도 전역적으로 처리하여 일관된 오류 메시지를 제공할 수 있습니다.

추가 예시 코드

사용자 정의 예외 처리

사용자 정의 예외 클래스를 만들어 이를 처리할 수 있습니다.

java코드 복사public class CustomNotFoundException extends RuntimeException {
    public CustomNotFoundException(String message) {
        super(message);
    }
}

@RestController
public class CustomController {

    @GetMapping("/custom")
    public String getCustom() {
        throw new CustomNotFoundException("리소스를 찾을 수 없습니다.");
    }
}

@ControllerAdvice
public class CustomGlobalExceptionHandler {

    @ExceptionHandler(CustomNotFoundException.class)
    public ResponseEntity<String> handleCustomNotFoundException(CustomNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

위 코드에서 CustomNotFoundException이 발생하면 handleCustomNotFoundException 메서드가 호출되어 404 상태 코드와 함께 오류 메시지를 반환합니다.

Validation 예외 처리

Spring의 @Valid를 사용하여 유효성 검사 실패 시 발생하는 예외를 처리할 수 있습니다.

java코드 복사@RestController
public class ValidationController {

    @PostMapping("/validate")
    public ResponseEntity<String> validateRequest(@Valid @RequestBody UserRequest userRequest) {
        return new ResponseEntity<>("유효한 요청입니다.", HttpStatus.OK);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
        String errorMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        return new ResponseEntity<>(errorMessage, HttpStatus.BAD_REQUEST);
    }
}

public class UserRequest {
    @NotNull(message = "이름은 필수입니다.")
    private String name;

    @Min(value = 18, message = "나이는 18세 이상이어야 합니다.")
    private int age;

    // getters and setters
}

위 코드에서 UserRequest 객체의 유효성 검사가 실패하면 handleValidationException 메서드가 호출되어 오류 메시지와 400 상태 코드를 반환합니다.

자주 묻는 질문 (FAQ)

1. @ControllerAdvice와 @ExceptionHandler의 차이점은 무엇인가요?

@ExceptionHandler는 특정 컨트롤러 클래스 내에서만 발생하는 예외를 처리하는 데 사용되는 반면, @ControllerAdvice는 애플리케이션 전체에 걸쳐 발생하는 예외를 전역적으로 처리할 수 있도록 합니다. 따라서 예외 처리 로직의 재사용성과 유지 보수성을 높이기 위해 @ControllerAdvice를 사용하는 것이 좋습니다.

2. @ControllerAdvice는 모든 예외를 처리할 수 있나요?

네, @ControllerAdvice는 모든 컨트롤러에서 발생하는 예외를 처리할 수 있습니다. @ExceptionHandler 메서드에 예외 클래스를 지정함으로써 특정 예외 유형에 대해 처리 로직을 정의할 수 있습니다. 또한, 예외의 계층 구조를 활용해 부모 예외 클래스도 처리할 수 있습니다.

3. 예외 처리에서 ResponseEntity를 사용하는 이유는 무엇인가요?

ResponseEntity는 HTTP 응답의 상태 코드, 헤더, 본문을 모두 제어할 수 있는 유연한 객체입니다. 이를 사용하면 예외가 발생했을 때 사용자에게 적절한 HTTP 응답 상태 코드와 메시지를 전달할 수 있습니다. 예를 들어, 잘못된 요청에 대해서는 HttpStatus.BAD_REQUEST (400)를 반환하고, 서버 오류에 대해서는 HttpStatus.INTERNAL_SERVER_ERROR (500)를 반환하여 클라이언트가 응답의 성격을 이해할 수 있도록 합니다.

4. 글로벌 예외 처리의 테스트는 어떻게 하나요?

글로벌 예외 처리를 테스트하려면 해당 예외를 발생시키는 요청을 테스트 코드로 작성하고, 기대하는 응답 상태 코드와 메시지를 검증하면 됩니다. 예를 들어, MockMvc를 사용하여 컨트롤러 요청을 테스트하고, 응답이 올바른지 확인할 수 있습니다.

java코드 복사@RunWith(SpringRunner.class)
@WebMvcTest(ExampleController.class)
public class ExampleControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testIllegalArgumentException() throws Exception {
        mockMvc.perform(get("/example"))
                .andExpect(status().isBadRequest())
                .andExpect(content().string("잘못된 요청입니다."));
    }
}
Comments