기본적으로 스프링 부트는 모든 에러를 처리하는 /error 매핑을 제공하며 서블릿 컨테이너에 전역 오류페이지로 등록한다.
머신 클라이언트의 경우는 오류, http 상태 exception메세지를 json으로 응답하며 브라우저 클라이언트에서는 동일한 데이터를 html 포맷으로 렌더링하는 "whitelabel" 에러 뷰를 보여준다.
Browser Client
Machine Client
BasicErrorController
스프링 부트는 BasicErrorController에서 에러핸들링을 하고있다.
생성자와 stacktrace, errors, message속성에대한 결정을 하는 메서드들은 생략하였다.
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
}
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}
}
하나씩 확인해보자
// 1 : 2 : 3
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
...
}
property에 server.error.path가 있으면 그 값을 없을경우 error.path를 사용하고 둘다 없는 경우 /error를 맵핑한다.
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
...
}
Accept Header에 text/html가 포함된 경우 errorHtml()에서 응답을 처리한다.
@RequestMapping
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
...
}
그렇지 않은 경우에는 error()에서 ResponseEntity로 리턴한다(json)
Custom error page
응답의 상태코드에 따라 에러페이지를 다르게 보여줄 수있다.
기본 경로는 src/main/resources의 static 혹은 templates에 error 디렉토리를 만들어주고 파일명을 정확한 상태코드나 시리즈 마스크로 하면 된다.
여기서는 템플릿 엔진으로 Thymeleaf를 사용하였다.
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 404.html
| +- 5xx.html
+- <other public assets>
404.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>404 error!</h1>
<p th:text="${'timestamp: ' + timestamp}"></p>
<p th:text="${'path: ' + path}"></p>
<p th:text="${'status: ' + status}"></p>
<p th:text="${'timestamp: ' + timestamp}"></p>
<p th:text="${'error: ' + error}"></p>
</body>
</html>
5xx의경우 동일하며 h1태그의 내용만 505로 하였다.
이처럼 응답코드로 view의 이름을 작성하여 커스텀한 에러 페이지를 작성할 수 있다.
@ExceptionHandler
스프링에서는 @ExceptionHandler를 통해 발생한 예외 처리를 할 수 있다.
Controller에서 예외를 던지도록 추가하였다.
//ErrorData
public class ErrorData {
private String message;
private String code;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
//TestException
public class TestException extends RuntimeException {
public TestException() {
super();
}
public TestException(String message) {
super(message);
}
}
//ExceptionController
@RestController
public class ExceptionController {
@GetMapping("/exception")
public String exception(){
throw new TestException("알수없는 에러 발생");
}
@ExceptionHandler(TestException.class)
public ResponseEntity sampleError(TestException e){
ErrorData error = new ErrorData();
error.setCode("ERROR_UNKNOWN");
error.setMessage(e.getMessage());
return ResponseEntity.status(HttpStatus.OK).body(error);
}
이처럼 특정 Controller에서 예외가 발생하는 경우 @ExceptionHandler를 서칭하여 예외를 처리할 수 있다.
오류가 발생하는 /exception으로 요청을 날리면 다음과 같은 응답을 얻을 수 있다.
HTTP/1.1 200
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 05 Jul 2020 11:53:00 GMT
{"message":"알수없는 에러 발생","code":"ERROR_UNKNOWN"}%
참고문서
'프로그래밍 > Spring' 카테고리의 다른 글
패스워드 암호화 (0) | 2021.04.02 |
---|---|
Errors나 BindingResult사용시 주의할 점 (0) | 2020.08.09 |
스프링부트의 자동설정 (0) | 2020.06.21 |
스프링 AOP (0) | 2020.04.19 |
빈의 스코프 (0) | 2020.03.18 |