![[Spring-Error] @ExceptionHandler 호출이 안 되는 문제 (예외 처리가 안됨)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw7m0v%2FbtslCkLhJiP%2FLwQ8Aa0vbkMl99DGbenMpK%2Fimg.png)
내가 겪은 문제
API 통신을 할 때 @ExceptionHandler 애너테이션이 호출되지 않는 문제가 있었다.
문제가 발생하는 코드는 아래와 같다.
ApiExceptionController
@Slf4j
@RestController
public class ApiExceptionController {
@GetMapping("/api/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자");
}
if (id.equals("bad")) {
log.info("ApiExceptionController 호출");
throw new IllegalArgumentException("잘못된 입력 값");
}
return new MemberDto(id, "hello " + id);
}
@Data
@AllArgsConstructor
static class MemberDto {
private String memberId;
private String name;
}
}
ApiExceptionV2Controller
@RestController
@Slf4j
public class ApiExceptionV2Controller {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalException(IllegalArgumentException ex) {
log.info("illegalArgument Exception");
return new ErrorResult("BAD_REQUEST", ex.getMessage());
}
}
ApiExceptionController를 보면 /api/members/{id}와 같이 되어있다. id에 bad 값을 넘겼을 때 IllegalArgumentException을 발생시키는 것을 알 수 있다. /api/members/bad로 서버 요청을 보내면 ApiExceptionV2Controller에 있는 ExceptionHandler가 해당 예외를 처리할 거라고 생각했다. 그러나 생각과 달리 ExceptionHandler 자체가 호출되지가 않았다.
해결
예외가 발생한 컨트롤러 안에 예외를 처리하는 ExceptionHandler가 같이 있어야 한다. 그게 무슨 말이냐면 ApiExceptionV2Controller에 예외를 발생시키는 코드와 이 예외를 처리하는 ExceptionHandler가 같이 있어야 한다는 것이다.
ApiExceptionV2Controller
@RestController
@Slf4j
public class ApiExceptionV2Controller {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalException(IllegalArgumentException ex) {
log.info("illegalArgument Exception");
return new ErrorResult("BAD_REQUEST", ex.getMessage());
}
@GetMapping("/api2/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) throws Exception {
if (id.equals("bad")) {
throw new IllegalArgumentException("잘못된 입력 값");
}
return new MemberDto(id, "hello " + id);
}
@Data
@AllArgsConstructor
static class MemberDto {
private String memberId;
private String name;
}
}
위와 같이 해결할 수 있으나 이는 예외 발생 코드와 예외 처리 코드가 하나의 컨트롤러에 같이 있기 때문에 분리하는 것이 좋다. 이때 사용하는 것이 @ControllerAdvice 애너테이션이다.
ControllerAdvice
아래는 RestControllerAdvice를 사용했는데 @Controller + @ResponseBody를 합친 것이다. 그냥 HTTP Body에 데이터를 담아서 컨트롤러에 넘기려고 사용한 것이다.
@Slf4j
@RestControllerAdvice
public class ExControllerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalException(IllegalArgumentException ex) {
log.info("illegalException 발생");
return new ErrorResult("BAD_REQUEST", ex.getMessage());
}
}
ControllerAdivce를 사용하면 내가 겪었던 문제가 바로 해결된다. 예외를 발생시키고 처리하는 코드가 분리되어 있음에도
/api/members/bad로 서버에 요청해도 예외를 처리해준다
결론
ExceptionHandler는 하나의 컨트롤러에 정상 코드(예외 발생시키는 코드)와 예외를 처리하는 ExceptionHanlder가 같이 있어야 한다.
ControllerAdivce를 사용하면 정상 코드와 예외 처리 코드가 분리되어 있어도 예외를 정상적으로 처리하고 클라이언트에 응답해준다.