사용자가 필수 파라미터를 빼고 요청을 보냄. 서버 내에서 데이터베이스 조회 오류. 그 외 예상치 못한 오류. 등등 예외가 발생하면 어떻게 처리해야 하는가? 기본적으로 스프링이 적절한 예외 페이지를 보여준다. 그러나 좀 더 자세하게 오류 정보를 전달하고시다면 따로 설정해주어야 한다.
예외가 발생하였을 때, 표현 방법에 따라 2가지로 나눌 수 있다. 웹에서 요청한 경우와 api를 요청한 경우이다. 각각 html, json으로 오류 정보를 응답한다.
1. html로 오류 표시 (웹 클라이언트 요청)
스프링에서는 resources/templates/error또는 resources/static/error디렉토리에 에러코드.html을 만들면 된다.
resources/templates/error/500.html resources/templates/error/5xx.html
resources/static/error/400.html resources/static/error/404.html
등등을 만들면 되는 것이다.
오류 페이지의 model에 timestamp, path, status, message, error, exception, errors, trace값들을 전달해주기 때문에 정보를 출력해줄 수 있다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>4xx 에러 </title>
</head>
<body>
<p th:text="|timestamp = ${timestamp}|"></p>
<p th:text="|path = ${path}|"></p>
<p th:text="|status = ${status}|"></p>
<p th:text="|message = ${message}|"></p>
<p th:text="|error = ${error}|"></p>
<p th:text="|exception = ${exception}|"></p>
<p th:text="|errors = ${errors}|"></p>
<p th:text="|trace = ${trace}|"></p>
</body>
</html>
resources/templates/error/4xx.html
위 그림과 같이 표시된다. 기본적으로 보안상 message, exception, errors, trace는 전달되지 않는다. 전달하고 싶다면 application.properties에서 설정해주면 된다. 그러나 보통 해커들에게 정보를 주는것을 막기 위해 정보들을 잘 주지 않는다.
서버 내에서 예외가 생겼을 경우, status 500으로 보인다. 그러나 해당 예외에 맞는 자세한 상태코드를 응답하고싶다면 @ExceptionHandler와 @ResponseStatus를 사용하면 된다.
@Controller
public class htmlController {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public String handleIllegalArgumentException(IllegalArgumentException ex) {
return "error/400";
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public String handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
return "error/missingRequestParameter";
}
@GetMapping("/aa")
public String aa() {
throw new IllegalArgumentException("잘못된 매개변수");
}
@GetMapping("/bb")
public String bb(@RequestParam String id) {
return "index";
}
}
/aa경로에서 어떤 오류가 생겨 IllegalArgumentException이 발생한다고 해보자. 그러면 원래는 사용자 화면에 status 500가 보이게 된다. 이 예외가 발생했을 때, status 400를 전달하고싶다면 @ExceptionHandler에 예외를 넣고 @ResponseStatus에 status를 등록하면 된다.
/bb경로는 id라는 파라미터를 받는데, 이 파라미터가 없다면 MissingServletRequestParameterException 예외가 발생하며 status 400을 응답한다. 그러나 파라미터가 없다는 정확한 정보전달을 위해 따로 페이지를 만들어 전달한다면 위와 같이 만들 수 있다.
2. json으로 오류 표시 (api 요청)
api요청은 보통 json으로 주고받는다. 예외가 발생했다면 오류 응답 스펙에 맞춰 클래스를 하나 생성해준다.
@AllArgsConstructor
@Getter @Setter
public class ErrorResult {
private String status;
private String message;
}
그리고 @ExceptionHandler와 @ResponseStatus를 사용하여 적절한 응답을 만들면 된다.
@RestController
@RequestMapping("/api")
public class ApiController {
@ResponseStatus(code= HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalArgumentException(IllegalArgumentException e) {
return new ErrorResult("bad", e.getMessage());
}
@GetMapping("/ex")
public String ex() {
throw new RuntimeException("ex");
}
@GetMapping("/bad")
public String bad() {
throw new IllegalArgumentException("잘못된 입력 값");
}
@GetMapping("/param")
public String param(@RequestParam String param) {
return "ok";
}
}
ApiController.class
{
"timestamp": "2025-04-17T09:01:48.929+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/api/ex"
}
/api/ex 응답
{
"status": "bad",
"message": "잘못된 입력 값"
}
/api/bad응답
이렇게 @ExceptionHandler를 사용하면 컨트롤러 단위로 예외를 처리할 수 있다.
@ControllerAdvice 또는 @RestControllerAdvice를 사용해 여러 컨트롤러 단위로 예외를 처리할 수 있다. html에 @ControllerAdvice를, api에 @RestControllerAdvice를 지정하면 된다.
@RestControllerAdvice(annotations = RestController.class)
public class ExRestControllerAdvice {
@ResponseStatus(code= HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public ErrorResult illegalArgumentException(IllegalArgumentException e) {
return new ErrorResult("bad", e.getMessage());
}
}
api의 예외처리부분을 별도의 클래스로 뺀 것이다. RestController 어노테이션이 붙은 클래스에 적용된다.
@ControllerAdvice(annotations = Controller.class)
public class ExControllerAdvice {
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public String handleIllegalArgumentException(IllegalArgumentException ex) {
return "error/400";
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public String handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
return "error/missingRequestParameter";
}
}
html 예외처리부분을 별도의 클래스로 뺀 것이다. Controller 어노테이션이 붙은 클래스에 적용된다.
@ControllerAdvice 와 @RestControllerAdvice는 범위를 지정할 수 있는다. 어노테이션, 패키지, 클래스 를 지정할 수 있다.
@ControllerAdvice(annotations = Controller.class) => 어노테이션 지정
@ControllerAdvice("org.example.controller") => 패키지 지정
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class}) => 클래스 지정
'Java' 카테고리의 다른 글
spring 검증(validation, bean validation) (0) | 2025.04.11 |
---|---|
spring 로그인 세션과 인터셉터 (0) | 2025.04.03 |
spring 단위 테스트 코드 작성 (0) | 2025.03.11 |
spring 기본 개념 (0) | 2025.03.11 |
thymeleaf 템플릿 엔진 (0) | 2025.02.11 |