| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- mybatis
- redis
- JWT
- @Transactional
- securitycontextholderfilter
- 컨테이너
- 스프링 부트
- DI
- 서블릿 컨테이너
- Spring Container
- 페이징
- Dead Letter Queue
- Web
- Spring
- kafka
- Spring Data JPA
- dockerhub
- JPQL
- docker
- MSA
- CORS
- 지연 로딩
- JPA
- 쿠버네티스
- @ComponentScan
- Routing Key
- docker compose
- DLQ
- JdbcTemplate
- AWS
- Today
- Total
look-forest
예외 처리와 오류 페이지 본문
Spring MVC에서 예외 처리 방법을 알아보자.
1. WAS 서블릿 컨테이너가 제공하는 예외 처리 및 오류 페이지
2. 스프링에서 제공하는 예외 처리 및 오류 페이지
1. 서블릿 예외 처리
서블릿은 다음 2가지 방식으로 예외 처리를 지원한다.
- Exception (예외) → 500 에러
- response.sendError(HTTP 상태 코드, 오류 메시지)
자바의 메인 메서드를 직접 실행하는 경우 main 이라는 이름의 쓰레드가 실행된다. 실행 도중에 예외를 잡지 못하고 처음 실행한 main() 메서드를 넘어서 예외가 던져지면, 예외 정보를 남기고 해당 쓰레드는 종료된다.
웹 애플리케이션은 사용자 요청별로 별도의 쓰레드가 할당되고, 서블릿 컨테이너 안에서 실행된다. 애플리케이션에서 try ~ catch로 예외를 잡아서 처리하지 못하고, 서블릿 밖으로 까지 예외가 전달되면 WAS 까지 예외가 전달된다.
그러면 tomcat이 기본으로 제공하는 오류 화면이 나타난다.
Exception 의 경우 서버 내부에서 처리할 수 없는 오류가 발생한 것으로 생각해서 HTTP 상태 코드 500을 반환한다.
WAS(여기까지 전파) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(예외발생) .
서블릿 컨테이너는 고객에게 응답 전에 response 에 sendError() 가 호출되었는지 확인한다.
그리고 호출 되었다면 설정한 오류 코드에 맞추어 기본 오류 페이지를 보여준다.
WAS(sendError 호출 기록 확인) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러 (response.sendError())
서블릿 컨테이너가 제공하는 기본 예외 처리 화면은 사용자가 보기에 불편하다. 의미 있는 오류 화면을 제공해보자.
서블릿 예외 처리 - 오류 화면 제공
서블릿은 Exception (예외)가 발생해서 서블릿 밖으로 전달되거나 또는 response.sendError() 가 호출 되었을 때 각각의 상황에 맞춘 오류 처리 기능을 제공한다.

ErrorPage를 등록한 후, 오류가 발생했을 때 처리할 수 있는 컨트롤러가 필요하다.
예를 들어서 RuntimeException 예외가 발생하면 errorPageEx 에서 지정한 /error-page/500 이 호출된다.

즉 예외 발생 / sendError 호출기록이 WAS에 전달되면,
WAS는 해당 예외를 처리하는 오류 페이지 정보를 확인 후 에러 페이지를 다시 요청한다.
1. WAS(여기까지 전파) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(예외발생)
2. WAS `/error-page/500` 다시 요청 → 필터 → 서블릿 → 인터셉터 → 컨트롤러(/error-page/ 500) → View
서블릿 예외 처리 - 필터
오류가 발생하면 오류 페이지를 출력하기 위해 WAS 내부에서 다시 한번 호출이 발생한다. 이때 필터, 서블릿, 인터셉터도 모두 다시 호출된다. 그런데 로그인 인증 체크 같은 경우를 생각해보면, 서버 내부에서 오류 페이지를 호출한다고 해서 해당 필터나 인터셉트가 한번 더 호출되는 것은 비효율적이다. 결국 클라이언트로 부터 발생한 정상 요청인지, 아니면 오류 페이지를 출력하기 위한 내부 요청인지 구분할 수 있어야 한다. 서블릿은 이런 문제를 해결하기 위해 DispatcherType 이라는 추가 정보를 제공한다. request.getDispatcherType()
DispatcherType
- REQUEST : 클라이언트 요청
- ERROR : 오류 요청
- FORWARD : MVC에서 배웠던 서블릿에서 다른 서블릿이나 JSP를 호출할 때
RequestDispatcher.forward(request, response); - INCLUDE : 서블릿에서 다른 서블릿이나 JSP의 결과를 포함할 때
RequestDispatcher.include(request, response); - ASYNC : 서블릿 비동기 호출

DispatcherType에 REQUEST, ERROR를 둘 다 넣으면 클라이언트 요청은 물론 오류 페이지 요청에서도 필터가 호출된다.
기본 값은 DispatcherType.REQUEST이다. 즉 클라이언트의 요청이 있는 경우에만 필터가 적용된다. 특별히 오류 페이지 경로도 필터를 적용할 것이 아니면, 기본 값을 그대로 사용하면 된다.
서블릿 예외 처리 - 인터셉터
인터셉터는 서블릿이 제공하는 기능이 아니라 스프링이 제공하는 기능이므로, DispatcherType과 무관하게 항상 호출된다.
대신에 인터셉터는 다음과 같이 요청 경로에 따라서 추가하거나 제외하기 쉽게 되어 있기 때문에, 이러한 설정을 사용해서 오류 페이지 경로를 excludePathPatterns 를 사용해서 빼주면 된다.

정리
1. WAS(/error-ex, dispatchType=REQUEST) → 필터 → 서블릿 → 인터셉터 → 컨트롤러
2. WAS(여기까지 전파) ← 필터 ← 서블릿 ← 인터셉터 ← 컨트롤러(예외발생)
3. WAS 오류 페이지 확인
4. WAS(/error-page/500, dispatchType=ERROR) → 필터(x) → 서블릿 → 인터셉터(x) → 컨트롤러 → View
2. 스프링 부트 - 오류 페이지
지금까지 예외 처리 페이지를 만들기 위해서 다음과 같은 복잡한 과정을 거쳤다.
1. WebServerCustomizer 를 만들고 예외 종류에 따라서 ErrorPage를 추가
2. 예외 처리용 컨트롤러 ErrorPageController 생성
스프링 부트는 이런 과정을 모두 기본으로 제공한다.
1. ErrorPage 를 자동으로 등록한다. 이때 /error 라는 경로로 기본 오류 페이지를 설정한다.
2. BasicErrorController 라는 컨트롤러를 자동으로 등록한다.
따라서 개발자는 오류 페이지 화면만 BasicErrorController 가 제공하는 룰과 우선순위에 따라서
해당 경로 위치에 HTTP 상태 코드 이름의 뷰 파일을 넣어두면 된다!
BasicErrorController 의 처리 순서
- 뷰 템플릿
resources/templates/error/500.html
resources/templates/error/5xx.html - 정적 리소스( static , public )
resources/static/error/400.html
resources/static/error/404.html
resources/static/error/4xx.html - 적용 대상이 없을 때 뷰 이름( error )
resources/templates/error.html
추가로, BasicErrorController 컨트롤러는 에러 정보를 model에 담아서 뷰에 전달한다. 뷰 템플릿은 이 값을 활용해서 출력할 수 있으나, 고객에게 노출하는 것은 좋지않으며 보안상으로도 취약하다.

그래서 BasicErrorController 오류 컨트롤러에서 다음 오류 정보를 model 에 포함할지 여부 선택할 수 있다.
[application.properties]
server.error.include-exception=false : exception 포함 여부( true , false )
server.error.include-message=never : message 포함 여부
server.error.include-stacktrace=never : trace 포함 여부
server.error.include-binding-errors=never : errors 포함 여부
on_param 은 파라미터가 있으면 해당 정보를 노출한다. 디버그 시 문제를 확인하기 위해 사용할 수 있다. 그런데 이 부분도 개발 서버에서 사용할 수 있지만, 운영 서버에서는 권장하지 않는다.
error-ex/?message=&errors=&trace=
참고 자료 & 이미지 출처
스프링 MVC 2편 (김영한 님)
'Spring > Spring MVC - 웹 개발 활용 기술' 카테고리의 다른 글
| 요약 - 검증과 예외 처리 (0) | 2024.08.24 |
|---|---|
| API 예외 처리 (0) | 2024.08.11 |
| 로그인 처리2 - 필터, 인터셉터 (0) | 2024.08.10 |
| 로그인 처리 - 쿠키, 세션 (0) | 2024.08.06 |
| Bean Validation (0) | 2024.08.03 |