티스토리 뷰

반응형

(참고: Spring Boot와 Thymeleaf 그리고 IntelliJ(Gradle)를 기준으로 작성된 글입니다)

 

Error Pages

클라이언트(사용자)가 잘못된 데이터를 입력하여 Form을 전송하거나, 잘못된 요청을 보내 에러가 나는 경우가 있다 (404, 500 등등)

이때 클라이언트에게 조금 더 사용자 친화적인 오류 페이지를 보여주는 게 좋다

스프링 부트로 매우 간단하게 설정할 수 있지만 그전에 이 오류 페이지를 띄우기 위해서 서버에서의 작동원리를 먼저 살펴보고자 한다

 

Servlet Container의 예외 흐름

보통 예외가 발생하는 경우는 클라이언트가 웹 애플리케이션에 요청을 하였을때, 애플리케이션은 스레드를 할당하여 해당 로직을 수행한다, 이때 예외가 발생하게 된다면 try ~ catch로 예외를 처리하면 좋겠지만 내부에서 처리하지 못하고 서블릿 밖까지 예외가 전달되게 되면 WAS(톰캣)까지 예외가 전달되게 된다

이 상황에서 WAS는 예외 페이지를 띄우기 위하여 다시 요청을 하게 되는데

이때 주의할 점은 클라이언트 사이드(웹 브라우저)단에서는 이러한 요청이 드러나지 않는다

즉 WAS에게 예외가 흘러 들어올 경우 알아서 다시 예외 페이지를 호출하는 요청을 보낸다

WAS에서 에러로 인하여 다시 요청을 하면 단순히 요청뿐만 아니라 오류에 대한 정보를 request의 attribute에 추가해서 넘겨준다

javax.servlet.error.exception: //예외
javax.servlet.error.exception_type: //예외 타입
javax.servlet.error.message: //오류 메시지
javax.servlet.error.request_uri: //클라이언트 요청 URI
javax.servlet.error.servlet_name: //오류가 발생한 서블릿 이름
javax.servlet.error.status_code: //Http 상태 코드

핵심을 정리해보자면 예외가 발생할 경우 WAS까지 흘러가게 된다, 이때 WAS는 오류 페이지 경로를 찾아서 오류 페이지를 호출한다

 

Filter 와 Interceptor를 사용할 때 문제점

만약 웹과 관련된 공통 관심사를 처리하고자 Filter와 Interceptor를 적용하였다면 위에서 설명한 대로 WAS에서 다시 호출을 진행하기 때문에 Filter와 Interceptor를 거치게 될 수 있다, 이를 방지하기 위하여 각각 다른 방법을 제공한다

 

Filter

필터의 경우 DispatcherType이라는 옵션을 사용하여 구분을 하게 된다

//javax.servlet.DispatcherType

public enum DispatcherType {
    FORWARD,
    INCLUDE,
    REQUEST,
    ASYNC,
    ERROR
}

• FORWARD : 다른 서블릿 혹은 JSP를 호출할 때 사용

• INCLUDE : 서블릿에서 다른 서블릿 혹은 JSP의 결과를 포함할 때 사용

• REQUEST : 클라이언트 요청

• ASYNC : 서블릿 비동기 호출

• ERROR : 오류 요청

 

다양한 옵션들이 있지만 예외 페이지의 경우 REQUEST와 ERROR로 구분을 하게 되는데, 정상적인 요청인 경우 디스 패쳐 타입이 REQUEST로 오게 된다(아무 설정을 하지 않는 다면 기본값이 REQUEST) 이러한 디스 패쳐 타입을 가지고 온 경우 필터는 이를 검증하는 절차를 거친다, 하지만 ERROR를 들고 오게 된다면 필터는 해당 요청에 대한 검증을 건너뛰게 된다

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public FilterRegistrationBean logFilter() {
    FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LogFilter());
        filterRegistrationBean.setOrder(1);
        filterRegistrationBean.addUrlPatterns("/*");
        filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, 
            DispatcherType.ERROR);
        return filterRegistrationBean;
    } 
}

해당 설정은 필터를 등록하는 과정에서 설정할 수 있으며, 위의 예시와 같이 REQUEST 와 ERROR를 같이 등록하게 되면 두 디스 패쳐 타입에 모두 필터가 적용이 된다 (설정하지 않으면 기본값은 REQUSET 즉 정상적인 요청만 적용)

 

Interceptor

서블릿이 제공하는 필터의 경우에는 디스패쳐 타입을 사용할 수 있었지만 Interceptor의 경우 스프링이 제공하기 때문에 디스 패쳐 타입을 사용할 수 없다, 이에 인터셉터의 경우에는 에러 요청들을 직접 제외 패턴을 등록하는 방식으로 사용한다

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogInterceptor())
                .order(1)
                .addPathPatterns("/**")
                .excludePathPatterns("/error/**");
}

만약 오류 페이지를 사용하는 패턴을 " / error / . . . " 처럼 설계했다면 위의 예시와 같이 해당 오류 요청에 대한 제외 패턴을 직접 등록하여 인터셉터가 오류 요청에 대하여는 작동하지 않도록 설정하는 것이다

 

Spring Boot의 Error Page

에러가 발생하게 된다면 에러 페이지가 호출되는 흐름과, 필터 그리고 인터셉터의 경우 어떻게 정상적인 요청과 오류 요청을 구분하는지까지 살펴보았다, 마지막으로 스프링 부트에서 에러 페이지를 호출하기 위하여 어떠한 작업이 필요한지 살펴보고자 한다

 

스프링 부트의 경우 ErrorMvcAutoConfiguration라는 클래스가 에러 페이지를 등록하고, BasicErrorController가 이를 매핑해주는 컨트롤러를 자동으로 등록한다, 이렇게 자동화 되어 있기 때문에 개발자는 아주 간편하게 보여주고자 하는 페이지만 만들어 넣어주면 된다

 

반환하고자 하는 View(에러페이지)는 우선순위가 있는데, 아래와 같다

//1. View Template
resources/templates/error/404.html
resources/templates/error/4xx.html

//2. Static Resource
resources/static/error/404.html
resources/static/error/4xx.html

//3. Default
resources/templates/error.html

위의 예시처럼 해당하는 에러페이지를 예시 경로에 넣어주게 되면 BasicErrorController가 자동으로 해당 페이지를 반환하여 클라이언트에게 제공하게 된다, 또한 4xx라고 작성할 경우 4xx대 에러가 발생하면 해당 에러 페이지를 호출하고 위의 예시와 같이 404 에러가 발생한다면 더욱 디테일한 404.html 뷰를 호출한다, 만약 아무런 템플릿을 찾지 못했다면 기본값인 뷰를 호출하게 된다

 

핵심정리

클라이언트가 잘못된 접근을 하거나 서버쪽에서 해결할 수 없는 에러가 발생한다면 클라이언트에게 오류 페이지가 뜨게 된다, 이때 조금 더 사용자 친화적인 에러 페이지를 제공하기 위하여 에러 페이지를 직접 등록하고 각각의 에러에 맞게 해당 에러 페이지를 제공하여야 한다

 

이처럼 웹 애플리케이션에서 오류가 발생하여 WAS까지 흘러가는 경우에는 WAS가 알아서 에러 페이지를 다시 호출하게 된다, 이때 공통 관심사를 처리하는 필터와 인터셉터의 경우 오류 요청에 대한 작업을 제외하기 위하여 필터는 디스패쳐 타입, 인터셉터는 패턴을 사용하여 제외하게 된다

 

스프링 부트를 사용할 시에 복잡한 설정을 할 필요 없이 자동으로 에러 페이지를 등록하고 뷰를 호출해주는 작업까지 해주기 때문에, 뷰를 호출하는 규칙에 맞춰 해당 에러페이지를 작성해주면 매우 간편하게 에러 페이지를 클라이언트에게 제공할 수 있다

 

 

더보기

개인 학습을 위해 작성되는 글입니다.

제가 잘못 알고 있는 점에 대한 지적 / 더 나은 방향에 대한 댓글을 환영합니다.

 

참조 링크:

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-2/dashboard

 

 

반응형
댓글