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 ๋™์ž‘ ๊ณผ์ •


  1. ServletContainer ๊ฐ€ ๋ฐ›์€ ์š”์ฒญ์ด HttpServletRequest ๋กœ ๊ฐ€๊ณต๋˜์–ดDispatcherServlet ์—๊ฒŒ ์ „๋‹ฌ๋œ๋‹ค. ์ด๋•Œ MultipartResolver LocaleResolver ThemeResolver ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ตฌํ˜„์ฒด๊ฐ€ ์š”์ฒญ์„ ๋ถ„์„ํ•œ๋‹ค.
  2. ์š”์ฒญ์„ HandlerMapping ์—๊ฒŒ ์œ„์ž„ํ•˜์—ฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  Handler(Controller) ๋ฅผ ํƒ์ƒ‰ํ•œ๋‹ค.
  3. HandlerAdapter ์—์„œ Handler ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” HandlerAdapter ๋ฅผ ํƒ์ƒ‰ํ•œ๋‹ค.
  4. HandlerAdapter ์— ์ •์˜๋œ Handler ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  5. Handler ๊ฐ€ viewName ํ˜น์€ ModelAndView ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  6. ViewResolver ์—๊ฒŒ viewName ํ˜น์€ ModelAndView ๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ํ•ด๋‹นํ•˜๋Š” View ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  7. DispatcherServlet ์€ View ์—๊ฒŒ Model ์„ ์ œ๊ณตํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ตํ•  ํ™”๋ฉด์„ ์ƒ์„ฑํ•œ๋‹ค.
  8. 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