프로젝트의 REST API를 구축하면서 관련된 예외 처리 방법이 궁금해졌습니다.
이전에 강의를 통해서 HTML 페이지 에서의 예외처리는 생각보다
간단하다는 것을 알 수 있었습니다!
BasicErrorController가 error 패키지 안에 4xx 또는 5xx와 같은 html 예외 페이지를 찾아
관련된 예외가 발생했을 경우 해당 html을 자동으로 띄어주기 때문입니다!
반면에 API 예외처리는 HTML에서의 예외 페이지 처리 방식보다 조금 복잡합니다.
HTML 페이지의 예외처리는 고객에게 단순한 예외관련 페이지를 보여주면 끝이지만
API는 서버와 서버간의 통신 규악이기 때문에 각 오류 상황에 맞는 오류 스펙을 정하고
Json으로 데이터를 내려줘야 하기 때문입니다.
물론 BasicErrorController가 application/json 형식으로 요청이 들어왔을 때 자동으로
API 형식의 예외 메세지를 출력해주긴 합니다.
하지만!!! 오류를 보여주는 스펙이 일정하기 때문에 각 상황에 맞는
오류 스펙을 지정해서 보여주는 것이 불가합니다.
BasicErrorController도 확장하면 JSON메세지 형식을 변경할 수 있지만
그것보다 ExceptionHandler을 활용하는 방법이 더 나은 방법입니다.
ExceptionHandler란?
BasicErrorController는 controller에서 예외가 발생할 경우 해당 내용이
WAS까지 올라가고 WAS에서 /error url을 다시 내려주면
이 때 실행이 되게 됩니다. 다시 말하자면
에러를 처리하기 위해 WAS까지 올라갔다 내려가는 번거로운 작업을 수행해야 합니다.
반면 ExceptionrResolver를 사용하면 에러를 WAS까지 올라가게 하지 않고
그 전에 에러를 처리하여 정상 응답으로 한번에 WAS까지 전달할 수 있습니다.
ExceptionResolver의 사용방법은 다음과 같습니다.
@Slf4j
@RestController
public class ApiExceptionController {
@GetMapping("/api2/members/{id}")
public MemberDto getMember(@PathVariable("id") String id) {
if (id.equals("ex")) {
throw new RuntimeException("잘못된 사용자");
}
if (id.equals("bad")) {
throw new IllegalArgumentException("잘못된 입력 값");
}
if (id.equals("user-ex")) {
throw new UserException("사용자 오류");
}
return new MemberDto(id, "hello " + id);
}
@Data
@AllArgsConstructor
static class MemberDto {
private String memberId;
private String name;
}
}
위와 같이 /api2/members/ url 다음에 어떤 값이 경로로 넘어올 때
해당 값이 "ex", "bad", "user-ex" 들과 동일하다면
각각 다른 예외를 발생시키는 컨트롤러가 있습니다.
@Slf4j
@RestController
public class ApiExceptionV2Controller {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler
public ErrorResult illegalExHandle(IllegalArgumentException e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("BAD", e.getMessage());
}
@ExceptionHandler
public ResponseEntity<ErrorResult> userExHandle(UserException e) {
log.error("[exceptionHandle] ex", e);
ErrorResult errorResult = new ErrorResult("USER-EX", e.getMessage());
return new ResponseEntity<>(errorResult, HttpStatus.BAD_REQUEST);
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ErrorResult exHandle(Exception e) {
log.error("[exceptionHandle] ex", e);
return new ErrorResult("EX", "내부 오류");
}
.
. 생략
.
}
해당 컨트롤러에 위와 같이 ExceptionHandler 어노테이션이 붙은 메서드를 추가하면
각각의 예외 상황에 맞게끔 다르게 return 값을 설정할 수 있습니다.
이때 처리 우선순위는 부모 예외보다 자식 예외가,
전체적인것보다는 더 자세한 예외가 우선적으로 처리됩니다.
ExceptionHandler을 사용할 때 주의할 점은 WAS에 정상 응답을 해주기 때문에
HTTP 상태코드를 @Responstatus나 ResponseEntity로 설정해주지 않으면
예외가 발생하였는데도 정상 상태 코드가 전송되게 됩니다.
이를 해결하기 위해 위와 같이 @Responsestatus 어노테이션을 활용하거나
ResponseEntity에 상태코드를 담아 보내는 방식을 사용해야 합니다.
이상으로 ExceptionHandler을 활용하여 예외처리를 어떻게 편리하게 할 수 있는지 알아봤습니다.
'Spring' 카테고리의 다른 글
스프링 빈 스코프란? (0) | 2022.12.23 |
---|---|
Reflection 이란? (0) | 2022.12.16 |
스프링과 스프링부트 무슨 차이가 있을까? (0) | 2022.11.25 |
DI 인젝션이 필요한 이유는? (0) | 2022.11.19 |
스프링 시큐리티를 쓰는 이유는? (0) | 2022.11.09 |