@ControllerAdvice를 사용해 NoHandlerFoundException 캐치하기

김태훈·2023년 7월 31일
0
post-thumbnail

문제 상황

@ControllerAdvice를 사용해 404 에러 페이지를 커스텀하려 했습니다.
그러나 NoHandlerFoundException 가 @ControllerAdvice를 적용한 인스턴스에서 잡히지 않는 이슈가 발생했습니다.

static/error/404.html 경로에 html 파일을 위치시키는 방법으로 에러 페이지를 커스터마이징 할 수 있었지만, NoHandlerFoundException 이 잡히지 않는 이유가 궁금해 해결을 시도했습니다.

개요

아직 처리되지 않은 에러를 처리해주는 GlobalExceptionHandler 객체입니다. 해당 클래스는 공통 에러가 발생했을 때 커스텀한 에러 페이지를 반환하는 역할을 합니다.

package org.kimtaehoondev.jpcollector.web.controller;

import lombok.RequiredArgsConstructor;
import org.kimtaehoondev.jpcollector.config.AdminProperties;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.NoHandlerFoundException;

@ControllerAdvice
@RequiredArgsConstructor
public class GlobalExceptionHandler {
    private final AdminProperties properties;

    @ExceptionHandler(Exception.class)
    public String unexpected(Exception e, Model model) {
        model.addAttribute("adminEmail", properties.getEmail());
        return "view/error/500.html";
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public String notFound(Exception e) {
        return "view/error/404.html";
    }

}

해결

application.yml에 다음 설정이 필요합니다.

spring:
  mvc:
    throw-exception-if-no-handler-found: true
  web:
    resources:
      add-mappings: false    

원인 1

핸들러가 존재하지 않아도 스프링은 NoHandlerFoundException 을 발생시키지 않습니다. 해당 에러를 발생시키기 위해 "application.yml" 에 throw-exception-if-no-handler-found 을 true로 변경합니다.

  • DispatcherServlet.java

1285번쨰 줄을 확인하면 throwExceptionIfNoHandlerFound 라는 필드가 존재합니다. 해당 필드가 true일 때만 throw new NoHandlerFoundException 코드가 실행됩니다.
위 설정은 해당 필드를 true로 바꾸는 설정입니다.

원인 2

다음 설정은 정적 리소스들에 대한 매핑을 끄는 설정입니다.
스프링은 존재하지 않는 핸들러가 들어왔을 때 자동으로 404 기본 에러 페이지를 매핑해줍니다.
따라서, 에러가 발생해도 스프링이 매핑을 하지 못하도록 정적 리소스들에 대한 설정을 꺼야 합니다.

새로운 문제 - css 적용되지 않음

spring:
  web:
    resources:
      add-mappings: false  

404 에러 페이지는 커스텀한 화면으로 출력되는 걸 확인했지만, css가 적용되지 않는 이슈를 확인했습니다.
앞서 정적 리소스들의 매핑을 껐으니, 필요한 정적 리소스들을 수동으로 매핑해줘야 합니다.
WebMvcConfigurer에 /static 경로로 들어왔을 때 classpath:/static/으로 매핑되도록 설정해줬습니다.

package org.kimtaehoondev.jpcollector.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

public class StaticConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/");
    }
}
profile
작은 지식 모아모아

2개의 댓글

comment-user-thumbnail
2023년 7월 31일

좋은 글 감사합니다. 자주 올게요 :)

1개의 답글