
Spring MVC는 모델-뷰-컨트롤러 (Model-View-Controller, MVC) 패턴을 기반으로 하는 웹 애플리케이션 프레임워크이다. Spring 프레임워크의 일부로, 이걸 쓰면 웹 애플리케이션을 MVC(Model, View, Controller) 아키텍처에 따라 깔끔하게 나눠서 개발할 수 있다. 그럼 여기서 우선 Model, View, Controller에 대해 알아보자.
화면에 보여줄 데이터를 담는 용기(컨테이너) 역할로 이해하면 된다. model.addAttribute() 등을 통해 뷰에서 사용할 데이터를 넣어두고, 나중에 뷰 엔진에서 참조한다.
전통적인 MVC 이론에서는 “모든 도메인 로직과 데이터”를 포괄하는 개념이지만, 스프링 MVC에서는 뷰에 전달할 데이터를 담는 객체 정도로 쓰인다고 생각하면 된다.
사용자에게 보여지는 화면을 담당한다. JSP, Thymeleaf 같은 템플릿 엔진을 사용해서 데이터를 시각적으로 보여주게끔 만드는 역할이다.
요청을 받아서 필요한 서비스를 호출하여, 비즈니스 로직 (또는 도메인 로직)을 수행하도록 지시한다. 처리가 끝나면 뷰 (View)에 넘길 데이터 (스프링에서 Model 객체)를 준비하고, 어떤 뷰를 사용할지(뷰 이름) 결정한다.
즉, “클라이언트 요청 진입점”이자, 처리 흐름 제어를 담당한다고 볼 수 있다.
해당 구조로 이루어진 Spring MVC는 Spring의 핵심기능인 의존성 주입 (DI)과 주요 프로그래밍 모델을 활용하여 웹 애플리케이션을 구축할 수 있는 강력한 구성 요소를 제공한다.
다음으로 MVC 디자인 패턴의 실행흐름에 대해서 알아보자.
본 그림에 나타난 과정을 순서대로 설명하면 다음과 같다.
1. 클라이언트 요청 (Incoming request)
DispatcherServlet이 이 요청을 제일 먼저 받는다.2. Front Controller가 요청을 받음
“Front Controller”가 하는 역할이 바로 공통 진입점이다. 스프링에선 이게 DispatcherServlet이 되고, 모든 요청이 먼저 여기로 모인다.3. Controller에 요청 위임 (Delegate request)
Front Controller는 “이 요청을 어느 컨트롤러가 처리해야 하지?"에 대해 판단한 다음, 그 요청 처리를 적절한 Controller에게 넘겨준다. 즉, 핸들러 매핑 과정을 거쳐서 적합한 컨트롤러를 찾아준다.4. Controller가 요청 처리 (Handle request) + Model 생성
Controller는 직접 혹은 Service 레이어 통해서 비즈니스 로직을 수행하거나, 필요한 데이터를 조회한다. 그리고 그 결과를 담기 위해 Model 객체를 만든다.5. Controller가 Model과 함께 View 렌더링* 위임 (Delegate rendering)
Controller는 작업이 끝나면 “어떤 뷰(View)로 결과를 보여줄지” 결정한다. 그리고 View 렌더링에 필요한 Model 데이터를 같이 Front Controller (DispatcherServlet)에게 넘겨준다.※ 여기서 잠깐! 렌더링 (Rendering)이란?
렌더링은 백엔드에서 받은 데이터와 뷰 (예: HTML 템플릿)를 합쳐서 사용자에게 보여질 완성된 화면을 만드는 과정이다. 예를 들어, 스프링에서 데이터를 DB에서 가져오고 그 데이터를 뷰 템플릿에 넣어 최종 HTML을 만들어내는 과정을 렌더링이라고 할 수 있다.
6. View Template이 응답 화면 렌더링 (Render response)
Thymeleaf, JSP 등)이 넘겨받은 Model 데이터를 이용해서 화면 (HTML 등)을 만든 다음, 최종 결과를 완성한다.7. Front Controller로 제어 반환 (Return control)
Front Controller (DispatcherServlet)는 뷰로부터 렌더링된 결과를 받는다.8. 최종 응답(Return response)
Front Controller (DispatcherServlet)가 클라이언트한테 최종 결과 (HTML, JSON 등)를 응답으로 보내며 한 사이클이 끝나게 된다.정리하자면, 요청 → (Front Controller) → (Controller) → Model 생성 & 로직 처리 → (View 선택 & 렌더링) → (Front Controller) → 응답 이렇게 돌아간다고 보면 된다. 스프링 부트에서는 이 모든 과정을 거의 자동화해주므로, 개발자는 컨트롤러랑 뷰만 잘 정의해두면 편하게 MVC 구조를 구현할 수 있다.
다음으로 Spring MVC의 핵심 구성 요소인 DispatcherServlet, ModelAndView, ViewResolver에 대해서 알아보자.
DispatcherServlet은 모든 HTTP 요청이 제일 먼저 들어오는 "입구" 역할을 한다.
클라이언트 (브라우저)에서 들어오는 모든 요청을 받아서, 그 요청을 처리할 적절한 컨트롤러에 전달한다. 즉, 중앙 관리자로서 요청을 분배하는 역할이라 할 수 있다.
쉽게 비유하면 회사의 리셉션 같은 존재이다. 방문객이 오면 누가 응대할지 안내해주는 것과 비슷하다.
ModelAndView는 컨트롤러가 처리한 결과 (데이터와 뷰 이름)를 담는 그릇이다.
컨트롤러에서 로직을 수행한 후, 사용자에게 보여줄 데이터를 담은 모델과 어떤 뷰 (페이지)를 보여줄지 결정한 정보를 함께 전달해주는 역할을 한다.
쉽게 비유하면 음식점에서 주문 후, 종업원이 요리와 함께 테이블에 가져다 주는 음식 상자 같은 존재라고 볼 수 있다.
ViewResolver는 뷰 이름을 실제 화면으로 보여줄 템플릿 파일로 "해석"해주는 역할을 한다.
컨트롤러에서 "home" 같은 뷰 이름을 반환하면, ViewResolver가 그 이름에 맞는 실제 JSP, Thymeleaf, 또는 다른 템플릿 파일을 찾아서 클라이언트에게 보여준다.
비유하면 뷰 이름을 주소로 바꿔주는 네비게이터 같은 존재이다. "home"이라고 하면, 실제 파일 경로 (예: /WEB-INF/views/home.jsp)를 찾아주는 역할이다.
지금까지 공부한 내용을 바탕으로 다음 Spring MVC의 요청 처리 과정을 마지막으로 정리해보자.
1. 클라이언트 → DispatcherServlet
URL로 요청을 보낸다.MVC의 핵심인 DispatcherServlet (Front Controller)이 가장 먼저 받는다.2. DispatcherServlet → HandlerMapping
DispatcherServlet은 들어온 요청을 처리할 Controller를 찾기 위해, HandlerMapping에게 "어떤 컨트롤러가 이 요청을 처리해야 해?"라고 물어본다.3. DispatcherServlet → HandlerAdapter
HandlerMapping이 찾아준 컨트롤러를 실행할 수 있도록, DispatcherServlet은 HandlerAdapter를 사용해 실제 컨트롤러 호출 로직 (메소드 파라미터 바인딩 등)을 준비한다.4. HandlerAdapter → Controller
HandlerAdapter가 실제로 Controller를 호출한다.5. Controller → Service
Controller는 비즈니스 로직 처리를 위해 Service 계층을 호출한다.Service에 넘겨준다.6. Service → Repository
Service는 DB나 외부 API 등 데이터 처리가 필요할 때 Repository (DAO)를 호출해 실제 데이터 접근을 수행한다.7. Repository → Database
Repository는 Database와 통신하여 필요한 데이터를 조회, 저장, 수정, 삭제 등의 작업을 한다.8. Database → Repository → Service
Database에서 조회/처리된 결과가 다시 Repository로 돌아오고, Service가 결과를 받아 비즈니스 로직에 맞게 추가 가공을 한다.9. Service → Controller
Service가 Controller에게 반환한다.10. Controller → Model
Controller는 받아온 결과 데이터를 Model 객체에 담아둔다.Model은 뷰에 전달할 데이터를 담는 역할을 한다.11. Controller → DispatcherServlet
Controller는 어떤 뷰를 사용할지 (뷰 이름 등) 결정하고, Model 데이터를 함께 DispatcherServlet에 반환한다. (대개 ModelAndView 형태).12. DispatcherServlet → ViewResolver
DispatcherServlet은 뷰 이름을 ViewResolver에 넘겨주어, 실제 뷰 (템플릿 파일 JSP, Thymeleaf 등)를 찾는다.13. ViewResolver → View
ViewResolver가 찾은 뷰 (예: home.jsp 또는 index.html 등)에 Model 데이터를 넘겨 화면을 렌더링한다.14. View → DispatcherServlet → 클라이언트
HTML, JSON 등)가 DispatcherServlet을 거쳐 클라이언트에게 응답 (Response)으로 돌아간다.데이터 모델은 컨트롤러와 뷰 사이에서 데이터를 전달하는 컨테이너 역할을 한다. Spring MVC에서는 주로
Model, ModelMap, 또는 ModelAndView 객체를 사용하여 뷰에 데이터를 전달한다.
Spring MVC의 Model 인터페이스로 컨트롤러 메서드에 매개변수로 선언하면, 실행 시, 스프링이 적절한 구현체 (예: BindingAwareModelMap)를 자동으로 주입해준다.
주로 뷰에 전달할 데이터를 key-value 쌍 형태로 담는 역할을 한다.
데이터를 key-value 형태로 담는 역할을 하는 메서드는 addAttribute()가 있다.
예를 들어, model.addAttribute("name", "John") 하면 "name"이라는 key로 "John"이라는 value를 저장한다.
사용 예시는 다음과 같다.
✍️ 작성
@GetMapping("/welcome")
public String welcome(Model model) {
model.addAttribute("message", "안녕, 환영해!");
return "welcome";
}
※ 여기서 반환 값은 뷰 이름을 의미한다.
Model 인터페이스의 구현체 중 하나인 클래스로 Map처럼 동작하기 때문에, 데이터를 담고 꺼낼 때 편리하게 사용 가능하다.
Model과 거의 같은 역할을 하지만, Map 기능을 그대로 활용할 수 있다는 장점이 있다.
✍️ 작성
@GetMapping("/welcome")
public String welcome(ModelMap modelMap) {
modelMap.addAttribute("message", "안녕, 환영해!");
// 혹은 modelMap.put("message", "안녕, 환영해!");
return "welcome";
}
데이터 (Model)와 뷰 (View) 이름을 한 번에 담을 수 있는 클래스로, 컨트롤러 메서드에서 뷰 이름과 모델 데이터를 동시에 반환할 때 사용한다.
ModelAndView에서 사용되는 메서드로 setViewName(), addObject() 메서드가 있다.
setViewName 메서드는 ModelAndView 객체에서 뷰의 이름을 설정해주는 메서드이다. 이 이름을 기반으로 스프링의 View Resolver가 실제 템플릿 파일 (예: welcome.html)을 찾아서 렌더링한다.
addObject() 메서드는 데이터를 key-value 형태로 담는 역할을 하는데 예를 들어, modelAndView.addObject("name", "John") 하면 `"name" 키 (key)에 "John" 값 (value)을 저장해서 뷰에 전달하게 된다.
✍️ 작성
@GetMapping("/welcome")
public ModelAndView welcome() {
ModelAndView mav = new ModelAndView();
mav.setViewName("welcome"); // 렌더링할 뷰 이름
// ModelAndView mav = new ModelAndView("welcome"); 혹은 이렇게 생성자로 대체할 수 있다.
mav.addObject("message", "안녕, 환영해!");
return mav;
}
멋쟁이사자처럼 강의자료