[Spring] 스프링 프레임워크 MVC 처리 프로세스 - DispatcherServlet

스프링 웹 애플리케이션을 개발하다 보면 컨트롤러만 만들고 어노테이션만 붙이다 보면 “요청이 들어와서 응답이 나갈 때까지, 스프링 내부에서는 어떤 일이 일어날까?”가 궁금해지죠. 이번 글에서는 Spring MVC의 전체 요청 처리 프로세스를 한 번 정리해보려고 합니다.

 

Spring MVC란 무엇인가?

Spring MVC는 이름 그대로 Model, View, Controller 구조로 웹 애플리케이션을 구성하는 웹 프레임워크입니다. 클라이언트의 HTTP 요청을 받아서 컨트롤러로 전달하고, 비즈니스 로직을 수행한 뒤 View(화면)를 렌더링해서 응답을 돌려주는 일을 담당합니다. 우리가 주로 작성하는 것은 @Controller, @RestController 같은 컨트롤러 코드이지만, 그 뒤에서는 DispatcherServlet, HandlerMapping, ViewResolver 같은 여러 컴포넌트들이 함께 동작합니다.

 

핵심 컴포넌트 정리

  • DispatcherServlet : 모든 요청의 입구이자 출구. Spring MVC의 프론트 컨트롤러 역할.
  • HandlerMapping : 어떤 URL을 어떤 컨트롤러 메서드가 처리할지 찾아주는 매핑 정보.
  • HandlerAdapter : 찾은 컨트롤러를 실제로 호출해 주는 어댑터.
  • Controller : 요청을 받아 Service를 호출하고, 모델 데이터와 뷰 이름을 반환.
  • Service / Repository : 비즈니스 로직, DB 접근을 담당하는 레이어.
  • ViewResolver : 컨트롤러가 넘겨준 뷰 이름을 실제 View 파일로 찾아주는 역할.
  • View (JSP/Thymeleaf 등) : 최종적으로 HTML을 만들어서 브라우저에 전달하는 화면 부분.

각 컴포넌트가 역할을 나눠 가지기 때문에, 컨트롤러 코드에서는 “무엇을 할지”에만 집중할 수 있고 “요청이 어떻게 들어왔는지, 뷰가 어떻게 렌더링되는지” 같은 부분은 Spring MVC가 대신 처리해 줍니다.

 

Spring MVC 요청 처리 흐름 한눈에 보기

 

  1. Client → DispatcherServlet (요청 전송) : 브라우저(클라이언트)가 /users/list 같은 URL로 요청을 보냅니다. 이 요청은 Front Controller 패턴의 핵심 요소인 DispatcherServlet이 가장 먼저 받습니다.
  2. DispatcherServlet → HandlerMapping (핸들러 조회) : DispatcherServlet은 요청 URL을 보고 “어떤 Controller가 이 요청을 처리해야 하지?”를 판단하기 위해 HandlerMapping에게 확인합니다.
  3. HandlerMapping → DispatcherServlet (핸들러 반환) : HandlerMapping은 URL 패턴에 맞는 Controller 메서드 정보(Handler)를 찾아서 돌려줍니다. 
  4. DispatcherServlet → Controller (핸들러 호출) : DispatcherServlet은 이제 찾은 Controller 메서드를 실행하고 Controller는 비즈니스 로직을 처리한 뒤 Model + View 이름을 반환합니다.
  5. DispatcherServlet → ViewResolver (View 이름으로 실제 View 조회) : Controller가 반환한 "user/list"와 같은 뷰 논리 이름(logical view name)을 DispatcherServlet이 ViewResolver로 전달합니다. ViewResolver는 실제 View 파일 위치(템플릿 파일)를 결정합니다.
  6. ViewResolver → DispatcherServlet / DispatcherServlet → View (렌더링 준비) : ViewResolver가 알맞은 View 객체(JSP, Thymeleaf 등)를 반환하고, DispatcherServlet은 View에게 모델 데이터를 전달하며 렌더링을 요청합니다.
  7. View → Client (응답 반환) : View는 HTML 등 렌더링된 결과를 HTTP Response로 만들어 클라이언트에게 보냅니다.

 

요청 하나가 처리되는 실제 흐름

조금 더 구체적으로, /members URL로 GET 요청이 들어온다고 가정해 보겠습니다.

  1. 클라이언트 요청 브라우저에서 GET /members 요청을 전송합니다.
  2. DispatcherServlet이 요청 수신 웹 애플리케이션의 web.xml 또는 스프링 부트 자동 설정에 의해 모든 요청이 먼저 DispatcherServlet으로 전달됩니다.
  3. HandlerMapping으로 컨트롤러 탐색 HandlerMapping이 현재 URL(/members)과 HTTP 메서드(GET)에 매핑된 컨트롤러 메서드를 찾아냅니다. (예: MemberController.list())
  4. HandlerAdapter로 컨트롤러 호출 찾은 컨트롤러가 @RequestMapping 기반인지, @Controller인지 등에 따라 알맞은 HandlerAdapter가 선택되고, 이 어댑터가 컨트롤러 메서드를 실제로 호출합니다.
  5. Controller → Service → Repository 실행 컨트롤러는 서비스/레포지토리를 호출해 비즈니스 로직과 DB 조회 등을 수행하고 결과를 Model에 담아 반환합니다.
  6. 뷰 이름 & 모델 반환 컨트롤러는 문자열 형태의 뷰 이름(예: "members/list")과 모델 데이터를 DispatcherServlet에 돌려줍니다.
  7. ViewResolver가 View 선택 ViewResolver"members/list"라는 뷰 이름을 보고 실제 JSP/Thymeleaf 파일 위치를 결정합니다. (예: /WEB-INF/views/members/list.jsp)
  8. View 렌더링 & 응답 반환 선택된 View가 모델 데이터를 이용해 HTML을 렌더링하고, 최종 결과가 HTTP 응답으로 클라이언트에게 전달됩니다.

 

 예제 : 간단한 Spring MVC 컨트롤러 

위 흐름이 실제 코드에서 어떻게 보이는지, 간단한 예제로 살펴보겠습니다.

@Controller
@RequestMapping("/members")
public class MemberController {

    private final MemberService memberService;

    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }

    // 회원 목록 조회
    @GetMapping
    public String list(Model model) {
        List<Member> members = memberService.findAll();
        model.addAttribute("members", members);
        return "members/list"; // View 이름
    }
}

이 코드는 단순히 서비스 호출 + 모델 데이터 저장 + 뷰 이름 반환만 하고 있을 뿐이지만, 실제로는 그 앞뒤에서 DispatcherServlet, HandlerMapping, ViewResolver 등이 모두 함께 움직이면서 하나의 HTTP 요청을 완성된 HTML 응답으로 만들어줍니다.

 

Spring MVC의 장점

  • 역할 분리 : 요청 분배, 비즈니스 로직, 뷰 렌더링 역할이 명확히 나뉘어 있습니다.
  • 유연한 뷰 기술 : JSP, Thymeleaf, Freemarker 등 다양한 뷰 엔진과 연동 가능합니다.
  • 강력한 확장성 : HandlerInterceptor, AOP, 필터 등과 결합해 부가 기능을 쉽게 확장할 수 있습니다.
  • 테스트 용이성 : 컨트롤러/서비스/레포지토리를 계층별로 나눠 테스트하기 편리합니다.

특히 DispatcherServlet을 중심으로 한 일관된 처리 흐름 덕분에, 요청이 어디서 어떻게 처리되는지 한 번 구조를 이해해 두면 복잡한 서비스도 훨씬 수월하게 관리할 수 있습니다.