본문 바로가기

끄적끄적

[Spring Boot] 404에러 핸들링과 .jsp .html 동시 사용

[Spring Boot] 404에러 핸들링과 .jsp .html 동시 사용

목차
- 상황
- 원인과 해결방법
-- ResourceHandler 등록

상황

api 서버 View단으로 jsp와 html을 동시에 사용해야했다. 이 를 위해 기본으로 jsp파일을 사용하도록 하고, ResourceHandler에 .html을 매핑해줬는데, DispatcherServlet에 404 에러 핸들링 설정을 추가하면서 ResourceHandler가 매핑되지 않고 html 파일을 불러오지못하는 문제가 발생했다.

원인과 해결법

Spring MVC는 우선, DispatcherServlet을 통해, 들어온 url과 매핑된 Handler를 찾고, 없다면, ResourceHandler를 통해 등록된 파일을 (직접) 찾는다.
기존에는 .html파일을 찾아주기위해, 기본으로 매핑되는 ResourceHandler에 아래와 같은 설정을 추가해줬다.

	<servlet>
	    <servlet-name>htmlServlet</servlet-name>
	    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
	    <load-on-startup>2</load-on-startup>
	</servlet>
	 
	<servlet-mapping>
	    <servlet-name>htmlServlet</servlet-name>
	    <url-pattern>*.html</url-pattern>
	</servlet-mapping>


이제, 404 에러처리를 위해 DispatcherServlet을 세팅하는 코드를 보고, 기존 ResourceHandler설정이 무시되는 원인을 찾아보자.

@SpringBootApplication
public class ApiApplication extends SpringBootServletInitializer{

	public static void main(String[] args) {
		ApplicationContext applicationContext = SpringApplication.run(ApiApplication.class, args);
		DispatcherServlet dispatcherServlet = (DispatcherServlet)applicationContext.getBean("dispatcherServlet");
		dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
	}

}

DispatcherServlet에서 url과 매핑된 Handler가 없다면, 예외를 반환하도록 설정해야한다.
이를 위해서 우선, 스프링 IoC컨테이너인 ApplicationContext에서 DispatcherServlet을 가져오자.

DispatcherServlet dispatcherServlet = (DispatcherServlet)applicationContext.getBean("dispatcherServlet");

이제 가져온 dispatcherServlet에 예외반환을 true로 설정한다.

dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);

마지막으로, 기본적으로 등록되는 ResourceHandler를 매핑하지 않아야한다.
이 설정을 추가하지 않으면 DispatcherServlet에서 url에 매핑된 Handler를 찾을수 없을시, ResourceHandler로 넘어가게 되어서 핸들링을 해줄 수 없다.
application.properties)

spring.resources.add-mappings=false


이제, ControllerAdice어노테이션이 붙은 클래스에서 ExceptionHandler를 통해 에러를 핸들링 해줄수 있다.

@ExceptionHandler({NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class})
public ResponseEntity<ErrorDTO> dispatcherServletExceptionHandler(Exception e){
	/*...*/
}

여기까지 읽었다면 알겠지만, 원인은 위의 properties설정에 있다. 에러 처리를 위해서 ResourceHandler를 매핑하지 않는것이 문제였다.

해결방법은 간단한데, 기본적으로 등록되는 ResourceHandler말고, 새로운 커스텀 ResourceHandler를 만들어, 추가 시키면된다.

ResourceHandler 등록

커스텀 ResourceHandler를 등록해보자.

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer{
	
	/*...*/
	
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry){
		resourceHandlerRegistry.addResourceHandler("/restdocs.html")
			.addResourceLocations("classpath:/html/docs/restdocs.html");
	}

}

우선, ResourceHandler를 등록할 url을 정하고 ResourceHandler를 추가해준다.

resourceHandlerRegistry.addResourceHandler("/restdocs.html")

이제 ResourceHandler가 호출되면 파일을 찾을 디렉토리를 설정해준다.
아래 코드는 "classpath:/html/docs/"만 해줘도 입력 url에 맞는 파일을 찾아주지만, 나는 restdocs만 찾으면 되므로 저렇게 강제시켰다.

.addResourceLocations("classpath:/html/docs/restdocs.html");

이 외에도 캐시와 같은 여러가지 설정을 해줄수있는데, 자세한 내용은 아래 문서를 참고하자.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.html

ResourceHandlerRegistry (Spring Framework 5.3.16 API)

Stores registrations of resource handlers for serving static resources such as images, css files and others through Spring MVC including setting cache headers optimized for efficient loading in a web browser. Resources can be served out of locations under

docs.spring.io


https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.html

ResourceHandlerRegistration (Spring Framework 5.3.16 API)

Add one or more resource locations from which to serve static content. Each location must point to a valid directory. Multiple locations may be specified as a comma-separated list, and the locations will be checked for a given resource in the order specifi

docs.spring.io