최근 넥스트 스텝의 만들면서 배우는 Spring을 수강하고 있다.
확실히 혼자 만들 때 보다 더 많은 것을 배운다고 느낀 게, 시작부터 톰캣을 냅다 구현해 버린다. 정확하겐 강의 자료로 구현해 주신 WAS를 이용해 클라이언트와 서블릿처럼 웹 요청/응답을 주고받는 기능을 구현한다.
요구사항으로 URL 별로 비지니스 로직이 담긴 서블릿을 호출하기도, 정적 리소스를 반환하기도 해야 했다.
해당 미션을 진행할 때 궁금한 점은 '기존 톰캣은 이용자가 구현한 서블릿과 정적 리소스 반환 중 어떤 것이 먼저 일어날까?'였다.
톰캣의 정적 리소스 반환
톰캣의 우선순위를 파악하기 위해 간단한 실험을 진행했다.
루트 경로의 서블릿을 등록 후, 존재하는 정적 리소스를 요청하면 어떻게 될까?
루트 경로 서블릿이 존재할 때 정적 리소스 요청
public class RootServlet extends HttpServlet {
@Override
protected void service(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
System.out.println("uri: " + req.getRequestURI());
super.service(req, resp);
}
}
// ...
private void setDispatcherServlet(final Context context) {
final Wrapper sw = this.tomcat.addServlet(context.getPath(), "rootServlet", new RootServlet());
sw.addMapping("/");
}
// ...
톰캣에 루트 경로로 간단한 서블릿을 등록시켜 주었다.
결과
결과는 루트 서블릿이 호출되었다.
이로써 구체적인 경로로 등록된 서블릿이 우선순위를 가진다는 것을 알 수 있다.
그럼 톰캣에서 정적 리소스 반환을 책임지는 객체는 무엇인가?
정적 리소스 반환 객체
이 부분에서는 크게 두 가지로 추측했다.
AbstractProcessor
구현체에서 서블릿을 찾지 못했다고 판단하면 정적 리소스 찾기- 정적 리소스 반환을 담당하는 기본 서블릿
이 내용을 확인하기 위해선 톰캣 코드를 좀 들여다보면 쉽게 알 수 있다.
일단 1번 내용은 Http11Proccessor
를 보면
서블릿의 service() 메서드 결과 값을 반환받지 않고 있다.
물론 반환 값 없이도 request, response 객체의 상태 변화를 확인하면 서블릿이 호출 됐는지는 충분히 알 수 있을 것이다.
해당 지점을 디버그 포인트 걸어두고 실행하면
Response의 status와 contentType 이 변했다!
특정 서블릿이 어떤 동작을 했다는 것을 짐작할 수 있다.
해당 지점의 코드부터 한 단계씩 step into 하면 결국
DefaultServlet
라는 서블릿에 도달한다. 정적 리소스를 찾고 적절하게 반환시켜 주는 역할을 맡는다.
이름부터 명확한 이 서블릿은 tomcat-core 라이브러리의 org.apache.catalina.servlets 패키지에 존재한다.
이로써 톰캣에선 정적 리소스를 어떻게 반환하는지 알 수 있다.
그럼 스프링에선 정적 리소스를 어떻게 반환할까? 스프링 설정으로 정적 리소스 요청 경로를 변경할 수 있는데 톰캣에서 제공되는 메서드인 걸까?
스프링의 정적 리소스 반환
(이 글은 스프링 부트의 기본 옵션인 톰캣을 기준으로 설명합니다.)
아까 보았듯이 톰캣은 DefaultServlet
보다 개발자가 등록한 구체적인 경로의 서블릿을 먼저 호출한다.
그럼 먼저 스프링의 DispatcherServlet
이 호출될 것이다.
요청을 처리하는 doDispatch() 메서드 전후를 비교하고자 한다.
이번에도 doDispatch() 전후로 상태가 변화는 것을 볼 수 있다. doDispatch() 내에서 정적 리소스를 반환한다는 이야기다.
diDispatch()에서 처리가 된다면 결국 HandlerMapping
에서 적절한 Handler
를 반환 한다는 이야기 이다.
위처럼 디버그 포인트를 찍으면
위처럼 ResourceHttpRequestHandler
가 반환 된 것을 볼 수 있다. 반환 해주는 HandlerMapping
은 SimpleUrlHandlerMapping
라는 구현체다.
이렇게 스프링 내부에서 정적 리소스까지 모두 처리하기 때문에 톰캣과 상관 없이 개별적인 설정 변경 까지 가능한 것이었다.