Dispatcher Servlet
DispatcherServlet ์ด๋ Presentation Layer ์ ๋ฉด์์ ๋ชจ๋ HTTP ์์ฒญ์ ์ค์์ง์ค์์ผ๋ก ์ฒ๋ฆฌํ๋ Front Controller ํจํด์ ์ ์ฉํ Servlet ์ด๋ค.
ServletContainer ๊ฐ ๋ฐ์ ํด๋ผ์ด์ธํธ ์์ฒญ์ ๊ณตํต์ ์ธ ์์
์ DispatcherServlet ์ด ์ฒ๋ฆฌํ๊ณ ๊ทธ ์ธ ์์
์ ์ ์ ํ Controller ์๊ฒ ์์ํ๋ค.
์ฆ, Controller ๋ก ํฅํ๋ ๋ชจ๋ ์น ์์ฒญ์ ์ง์ ์ ์ผ๋ก์จ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ตํ๋ค.
Front Controller ํจํด

Front Controller ํจํด์ ๋ชจ๋ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ํ๋์ ๋ํ Controller ๋ฅผ ๋ฐฐ์นํ๋ ํจํด์ ์๋ฏธํ๋ค.
Front Controller ๋ ๋ชจ๋ ์์ฒญ์ ์ง์
์ ์ด ๋์ด ์์ฒญ์ ์ผ๊ด์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ค. Spring ์์๋ DispatcherServlet ์ด Front Controller ์ญํ ์ ํ๋ค.
Dispatcher Servlet ๋์ ๊ณผ์

ServletContainer๊ฐ ๋ฐ์ ์์ฒญ์ดHttpServletRequest๋ก ๊ฐ๊ณต๋์ดDispatcherServlet์๊ฒ ์ ๋ฌ๋๋ค. ์ด๋MultipartResolverLocaleResolverThemeResolver์ธํฐํ์ด์ค์ ๊ตฌํ์ฒด๊ฐ ์์ฒญ์ ๋ถ์ํ๋ค.- ์์ฒญ์
HandlerMapping์๊ฒ ์์ํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌํ Handler(Controller) ๋ฅผ ํ์ํ๋ค. HandlerAdapter์์ Handler ๋ฅผ ์ฒ๋ฆฌํ ์ ์๋HandlerAdapter๋ฅผ ํ์ํ๋ค.HandlerAdapter์ ์ ์๋ Handler ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ๋ค.- Handler ๊ฐ
viewNameํน์ModelAndView๋ฅผ ๋ฐํํ๋ค. ViewResolver์๊ฒviewNameํน์ModelAndView๋ฅผ ์ ๋ฌํ์ฌ ํด๋นํ๋View๋ฐํํ๋ค.DispatcherServlet์View์๊ฒModel์ ์ ๊ณตํ์ฌ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ตํ ํ๋ฉด์ ์์ฑํ๋ค.DispatcherServlet์View์ ๊ฒฐ๊ณผ๋ฅผHttpServletResponse๊ฐ์ฒด๋ฅผ ํตํด ํด๋ผ์ด์ธํธ์๊ฒ ์๋ตํ๋ค.
์ฝ๋ ์์์ ์ดํด๋ณด๊ธฐ
// DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
doDispatch(request, response);
}
// ...
}ServletContainer ๊ฐ HttpServletRequest ์ HttpServletResponse ๋ฅผ ์์ฑํ์ฌ DispatcherServlet ์๊ฒ ์ ๋ฌํ ๋ doService() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
์ด๋ Handler ๋ฅผ ์ฐพ๊ณ ์์ฒญ์ ์์ํ๋ ๋ฑ์ ์ญํ ์ ํ๋ doDispatch() ๋ฉ์๋๋ก ์ฐ๊ฒฐ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
mappedHandler = getHandler(processedRequest);
}
}
// ...
}
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}๋จผ์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ Handler ๋ฅผ ์ฐพ๊ธฐ ์ํด getHandler() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
}
}
// ...
}
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
// ...
}์ฐพ์ Handler ๋ฅผ ์คํํ๊ธฐ ์ํ HandlerAdapter ๋ฅผ ์ฐพ๊ธฐ ์ํ getHandlerAdapter() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
}
}
// ...
}
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}Handler ๋ฅผ ์คํํ๊ธฐ ์ applyPrehandle() ๋ฉ์๋๋ฅผ ํตํด Interceptor ์ preHandle() ๋ฉ์๋๋ฅผ ์คํํ๋ค. ์ด๋ HandlerInterceptor ๋ฅผ ๊ตฌํํ ๋น์ด ํธ์ถ๋๋ค. ์ฃผ๋ก ์ธ์ฆ์ ์ํด ๋ง์ด ์ฌ์ฉํ๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
// ...
}
// HandlerAdapter.java
public interface HandlerAdapter {
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}Interceptor ๋ฅผ ํต๊ณผํ ํ Handler ์ ๋ฉ์๋๋ฅผ ์คํํ๊ธฐ ์ํด HandlerAdapter ์ handle() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
try {
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
}
// ...
}
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}Handler ๊ฐ ์คํ๋ ์ดํ Interceptor ์ postHandle() ์ด ์คํ๋๋ค. ์ด๋ Handler ๊ฐ ๋ฐํํ ModelAndView ์ ๋ํ ํ์ฒ๋ฆฌ๋ฅผ applyPostHandle() ๋ฉ์๋๊ฐ ์ํํ๋๋ฐ, @Controller ์ ๊ฒฝ์ฐ ModelAndView ๋ฅผ ๋ฐํํ๊ณ @RestController ์ ๊ฒฝ์ฐ null ์ ๋ฐํํ์ฌ View ๊ฐ ๋ ๋๋ง๋๋ ๊ณผ์ ์ ์๋ตํ๊ฒ ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
try {
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// ...
}
private void processDispatchResult(
HttpServletRequest request,
HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler,
@Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// ...
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// ...
}
protected void render(
ModelAndView mv,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
// ...
View view;
String viewName = mv.getViewName();
if (viewName != null) {
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// ...
}
// ...
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
// ...
}
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
// View.java
public interface View {
void render(
@Nullable Map<String, ?> model,
HttpServletRequest request,
HttpServletResponse response) throws Exception;
}์ฒ๋ฆฌ๋ ModelAndView ๋ processDispatchResult() ๋ผ๋ ๋ฉ์๋๋ฅผ ํตํด ๋ ๋๋ง๋๋ค.
ํด๋น ๋ฉ์๋ ๋ด๋ถ์์ render() ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ฐ ์ด๋ viewName ์ View ๊ฐ์ฒด๋ก ๋ณํํ๋ resolveViewName() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค.
์ดํ ๋ฐํ๋ฐ์ View ๊ฐ์ฒด์ render() ๋ฉ์๋ ํธ์ถ์ ํตํด View ์ Model ์ ๋ ๋๋งํ๋ ๊ณผ์ ์ ๊ฑฐ์น๊ฒ๋๋ค.
// DispatcherServlet.java
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
// ...
}
}๋ง์ง๋ง์ผ๋ก Interceptor ์ triggerAfterCompletion() ๋ฉ์๋๋ฅผ ์คํํ๋ฉด ์์ฒญ์ ๋ํ ์ฒ๋ฆฌ๊ฐ ์ ๋ถ ๋ง๋ฌด๋ฆฌ๋๋ค.
์ ๋ฆฌ

DispatcherServlet ์ ๊ฐ๋จํ ์์๋ณด๊ณ ์ค์ ๋ด๋ถ ๋์ ๊ณผ์ ์ ์ดํด๋ดค๋ค. ์ ์ฒด์ ์ธ ํ๋ฆ์ ์ ๋ฆฌํ๋ฉด ์ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
References
- https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet-context-hierarchy
- https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-servlet
- https://tecoble.techcourse.co.kr/post/2021-06-25-dispatcherservlet-part-1/
- https://tecoble.techcourse.co.kr/post/2021-07-15-dispatcherservlet-part-2/