[ 김영한 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 #8 ] 예외 처리와 오류 페이지 (2)

김수호·2023년 9월 22일
0
post-thumbnail

지난 포스팅에 이어, 이번 포스팅에서는 7) ~ 9) 까지의 내용을 정리한다.

👉 목차는 다음과 같다.

1) 프로젝트 생성
2) 서블릿 예외 처리 - 시작
3) 서블릿 예외 처리 - 오류 화면 제공
4) 서블릿 예외 처리 - 오류 페이지 작동 원리
5) 서블릿 예외 처리 - 필터
6) 서블릿 예외 처리 - 인터셉터
7) 스프링 부트 - 오류 페이지1
8) 스프링 부트 - 오류 페이지2
9) 정리


7) 스프링 부트 - 오류 페이지1

지금까지 예외 처리 페이지를 만들기 위해서 다음과 같은 복잡한 과정을 거쳤다.

  • WebServerCustomizer 를 만들고
  • 예외 종류에 따라서 ErrorPage 를 추가하고
  • 예외 처리용 컨트롤러 ErrorPageController 를 만듦.

 

스프링 부트는 이런 과정을 모두 기본으로 제공한다.

  • ErrorPage 를 자동으로 등록한다. 이때 /error 라는 경로로 기본 오류 페이지를 설정한다.
    • new ErrorPage("/error") 상태코드와 예외를 설정하지 않으면 기본 오류 페이지로 사용된다.
    • 서블릿 밖으로 예외가 발생하거나, response.sendError(...) 가 호출되면 모든 오류는 /error 를 호출하게 된다.
  • BasicErrorController 라는 스프링 컨트롤러를 자동으로 등록한다.
    • ErrorPage 에서 등록한 /error 를 매핑해서 처리하는 컨트롤러다.
  • (참고) ErrorMvcAutoConfiguration 이라는 클래스가 오류 페이지를 자동으로 등록하는 역할을 한다.

주의

  • 이제 스프링 부트가 제공하는 기본 오류 매커니즘을 사용하도록 WebServerCustomizer 에 있는 @Component 를 주석 처리하자.
  • 그러면 이제 오류가 발생했을 때 오류 페이지로 /error 를 기본 요청한다. 스프링 부트가 자동 등록한 BasicErrorController 는 이 경로를 기본으로 받는다.

이렇게 되면 이제 개발자는 오류 페이지만 등록

  • BasicErrorController 는 기본적인 로직이 모두 개발되어 있다.
  • 개발자는 오류 페이지 화면만 BasicErrorController 가 제공하는 룰과 우선순위에 따라서 등록하면 된다. 정적 HTML이면 정적 리소스, 뷰 템플릿을 사용해서 동적으로 오류 화면을 만들고 싶으면 뷰 템플릿 경로에 오류 페이지 파일을 만들어서 넣어두기만 하면 된다.

뷰 선택 우선순위
( 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

해당 경로 위치에 HTTP 상태 코드 이름의 뷰 파일을 넣어두면 된다.
뷰 템플릿이 정적 리소스보다 우선순위가 높고, 404, 500처럼 구체적인 것이 5xx처럼 덜 구체적인 것 보다 우선순위가 높다. 5xx, 4xx 라고 하면 500대, 400대 오류를 처리해준다.

 

👉 코드로 적용해보자.

  • 오류 뷰 템플릿 추가 - 4xx.html 생성: resources/templates 디렉토리 내부에 error 디렉토리를 생성하자. 그리고 그 내부에 4xx.html 파일을 생성하자.
    • 4xx대 오류는 다 해당 파일(4xx.html)이 호출된다.
  • 오류 뷰 템플릿 추가 - 404.html 생성: resources/templates/error 디렉토리 내부에 404.html 파일을 생성하자.
    • 404 오류는 해당 파일(404.html)이 호출된다.
  • 오류 뷰 템플릿 추가 - 500.html 생성: resources/templates/error 디렉토리 내부에 500.html 파일을 생성하자.
    • 500 오류는 해당 파일(500.html)이 호출된다.
  • 실행해보자.
    • 정상적으로 각 오류에 해당하는 오류페이지가 노출됨을 확인할 수 있다.
  • 이번엔 다음과 같이 실행해보자.
    • ServletExController에 아래 코드 추가
    • 실행해보자.
      • 400 오류 페이지가 없지만 4xx가 있기에 해당 오류페이지가 호출된다.
  • 테스트 결과
    • http://localhost:8080/error-404 -> 404.html
    • http://localhost:8080/error-400 -> 4xx.html (400 오류 페이지가 없지만 4xx가 있음)
    • http://localhost:8080/error-500 -> 500.html
    • http://localhost:8080/error-ex -> 500.html (예외는 500으로 처리)

 

정리해보면, 우리가 추가한 건 오류 페이지만 규칙에 맞게 추가해준 것 밖에 없다. (나머지는 스프링 부트가 다 알아서 해준다.)

다음 내용에서는 BasicErrorController가 제공하는 기본 정보들에 대해서 좀 더 알아보자.


8) 스프링 부트 - 오류 페이지2

BasicErrorController가 제공하는 기본 정보들에 대해서 알아보자.

BasicErrorController 컨트롤러는 다음 정보를 model에 담아서 뷰에 전달한다.
뷰 템플릿은 이 값을 활용해서 출력할 수 있다.

  • 참고)
    • 시간, 상태코드, 오류 등의 정보 전달. (뷰 템플릿은 이 값을 활용해서 출력할 수 있다.)

 

👉 코드로 적용해보자.

  • 500.html - 오류 정보 추가: resources/templates/error/500.html을 다음과 같이 수정해보자. (BasicErrorController가 model에 담아서 넘겨주는 값들을 출력해보자.)
  • 실행해보자.
    • 정상적으로 출력됨을 확인할 수 있다. 그런데 보면 exception이나 trace 등 중요한 정보 여러가지가 빠져있다.

오류 관련 내부 정보들을 고객에게 노출하는 것은 좋지 않다. 고객이 해당 정보를 읽어도 혼란만 더해지고, 보안상 문제가 될 수도 있다. 그래서 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 포함 여부

기본 값이 never 인 부분은 다음 3가지 옵션을 사용할 수 있다. ( never, always, on_param )

  • never: 사용하지 않음
  • always: 항상 사용
  • on_param: 파라미터가 있을 때 사용

on_param 은 파라미터가 있으면 해당 정보를 노출한다. 디버그 시 문제를 확인하기 위해 사용할 수 있다. 그런데 이 부분도 개발 서버에서 사용할 수 있지만, 운영 서버에서는 권장하지 않는다.
on_param 으로 설정하고 다음과 같이 HTTP 요청시 파라미터를 전달하면 해당 정보들이 model 에 담겨서 뷰 템플릿에서 출력된다. ( ?message=&errors=&trace= )

 

👉 해당 정보들을 출력해보자.

  • application.properties 에 다음 코드를 추가하자.
  • 실행해보자.
    • 오류에 대한 정보가 확인된다. (이런 정보들은 노출하지 않는게 좋다.)
    • 실무에서는 이것들을 노출하면 안된다!
      사용자에게는 이쁜 오류 화면과 고객이 이해할 수 있는 간단한 오류 메시지를 보여주고 끝내야 한다. (오류는 서버에 로그로 남겨서 로그로 확인해야 한다.)

 

✔️ 참고

  • 스프링 부트 오류 관련 옵션
    • server.error.whitelabel.enabled=true : 오류 처리 화면을 못 찾을 시, 스프링 whitelabel 오류 페이지 적용
    • server.error.path=/error : 오류 페이지 경로, 스프링이 자동 등록하는 서블릿 글로벌 오류 페이지 경로와 BasicErrorController 오류 컨트롤러 경로에 함께 사용된다.
  • 확장 포인트
    • 에러를 공통으로 처리하는 컨트롤러의 기능을 변경하고 싶으면 ErrorController 인터페이스를 상속 받아서 구현하거나 BasicErrorController 상속 받아서 기능을 추가하면 된다.

✔️ 정리

  • 스프링 부트가 기본으로 제공하는 오류 페이지를 활용하면 오류 페이지와 관련된 대부분의 문제는 손쉽게 해결할 수 있다.

9) 정리

  • 프로젝트 생성
    • 먼저 프로젝트를 생성해보았다.
  • 서블릿 예외 처리 - 시작
    • 서블릿 예외 처리에 대해서 알아보았다.
    • 서블릿은 두가지 방식으로 예외를 처리한다.
      • Exception (예외)
      • response.sendError(HTTP 상태 코드, 오류 메시지)
  • 서블릿 예외 처리 - 오류 화면 제공
    • 서블릿 컨테이너가 제공하는 기본 예외 처리 화면은 실제 사용자가 보기에 불편하니, 오류 페이지를 등록해보았다. (WebServerFactoryCustomizer를 구현해서 원하는 오류 페이지를 등록할 수 있었다.)
  • 서블릿 예외 처리 - 오류 페이지 작동 원리
    • 오류 페이지 작동 원리를 알아보았다.
    • 예외 발생과 오류 페이지 요청 흐름을 확인해보았다.
  • 서블릿 예외 처리 - 필터
    • 오류가 발생하면 오류 페이지를 출력하기 위해 WAS 내부에서 다시 한번 호출이 발생한다. 이때 필터, 인터셉터도 모두 다시 호출된다. 이와 관련해서 DispatcherType에 대해 알아보았다. (기본: REQUEST)
  • 서블릿 예외 처리 - 인터셉터
    • 인터셉터는 스프링 기술이기 때문에, 인터셉터의 경우에는 DispatcherType을 적용할 수 없었다. 그래서 excludePathPatterns을 통해 오류페이지 경로를 제외하였다.
  • 스프링 부트 - 오류 페이지1
    • 이전까지 했던 복잡한 작업들은 사실 스프링 부트가 기본적으로 제공해준다.
      • ErrorPage 를 자동으로 등록해준다. ( /error 라는 기본 경로 하나를 등록한다. )
      • 그리고 위 경로를 처리하기 위한 BasicErrorController 라는 스프링 컨트롤러를 자동으로 등록한다.
    • 뷰 선택 우선순위에 대해서 알아보았다. ( 뷰 템플릿 -> 정적 리소스 -> error(기본) )
  • 스프링 부트 - 오류 페이지2
    • BasicErrorController가 제공하는 기본 정보들에 대해서 알아보았다.

 

이번 섹션에서 끝나면 좋겠지만, 사실 예외와 오류 페이지를 처리하는 것은 크게 두가지가 있다.

  • 지금처럼 웹 페이지를 해결해야 하는 경우
  • API 오류를 처리해야 하는 경우

다음 섹션에서는 API에서 오류가 발생했을 때, 어떤식으로 오류를 처리할 수 있는지에 대해서 알아보자.


강의를 듣고 정리한 글입니다. 코드와 그림 등의 출처는 김영한 강사님께 있습니다.

profile
현실에서 한 발자국

0개의 댓글