MVC Pattern

  • Model: Business logic 실행, 데이터 처리, 결과 데이터 생성
  • View: User interface 생성(presentation logic 실행), 출력 화면 생성
  • Controller: Request 처리, request/response 데이터 전달

 

Spring MVC 주요 구성요소

  • DispatcherServlet: Client의 요청(request)을 받아 Contoller에게 전달. Controller의 처리 결과를 View에 전달하여 응답(response)을 생성하도록 함(front controller)
  • HandlerMapping: Client의 요청 URL을 어떤 Controller가 처리할지를 결정
  • HandlerAdapter: DispatcherServlet과 Controller사이에서 요청과 응답 결과를 적절한 형식으로 변환
  • Controller: Client의 요청을 처리한 후 그 결과를 DispatcherServlet에게 전달
  • ModelAndView: Controller가 처리한 결과 및 View 선택에 필요한 정보를 포함하는 객체
  • ViewResolver: Contoller의 처리 결과를 출력할 View를 결정. View 객체 생성
  • View: Controller의 처리 결과를 출력할 화면 정의

 

Spring MVC 요청 처리 흐름

 

  • Controller와 출력 화면은 개발자가 직접 구현하고 스프링 빈으로 등록해야 함
  • DispatcherServlet은 모든 연결을 담당
    • 브라우저로부터 요청이 들어오면 요청을 처리하기 위한 컨트롤러 객체를 검색
    • 단, 직접 검색이 아닌 HandlerMapping이라는 빈 객체에 컨트롤러 검색을 요청
  • HandlerMapping은 클라이언트의 요청 경로를 이용해서 이를 처리할 컨트롤러 빈 객체를 DispatcherServlet에 전달
    • ex. 웹 요청 경로가 '/hello'라면 등록된 컨트롤러 빈 중에서 '/hello'요청 경로를 처리할 컨트롤러를 리턴
  • DispatcherServlet은 HandlerMapping이 찾아준 컨트롤러 객체를 처리할 수 있는 HandlerAdapter 빈에게 요청 처리를 위임
    • HandlerAdapter 빈은 @Controller, Controller 인터페이스, HttpRequestHandler 인터페이스를 동일한 방식으로 처리하기 위해 중간에 사용되는 것
    • HandlerAdapter는 컨트롤러의 알맞은 메소드를 호출해서 요청을 처리하고 그 결과를 DispatcherServlet에 리턴
    • 이때 HandlerAdpater는 컨트롤러의 처리 결과를 ModelAndView라는 객체로 변환해서 리턴
  • HandlerAdapter로부터 컨트롤러의 요청 결과를 ModelAndView로 받으면 DispatcherServlet은 결과를 보여줄 뷰를 찾기 위해 ViewResolver 빈 객체를 사용
    • ModelAndView는 컨트롤러가 리턴한 뷰 이름을 담고 있는데 ViewResolver는 이 뷰 이름에 해당하는 View 객체를 찾거나 생성해서 리턴
    • 응답을 생성하기 위해 JSP를 사용하는 ViewResolver는 매번 새로운 View 객체를 생성해서 DispatcherServlet에 리턴
  • DispatcherServlet은 ViewResolver가 리턴한 View 객체에게 응답 결과 생성을 요청
    • JSP를 사용하는 경우 View 객체는 JSP를 실행함으로써 웹 브라우저에게 전송할 응답 결과를 생성 

기본 설정

Step1: DispatcherServlet 설정

  • Client로부터 request를 전달받는 servlet 객체 (front contoller)
  • Web application 설정 파일 (WEB-INF/web.xml)에 설정
  • 내부적으로 Spring container(WebApplicationContext)를 생성
<web-app ... >
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
    <!-- <url-pattern>*.do</url-pattern> -->
  </servlet-mapping>
  …
</web-app>
  • 3행: dispatcherServlet 이름
  • 8행: JSP를 제외한 모든 request들을 처리
  • 9행: URL이 .do로 끝나는 request들을 처리

Step2: HandlerMapping 설정

  • Request를 처리할 handler (controller 및 method) 선택
  • 일반적으로 Spring에서 제공되는 구현 클래스를 이용
    • o.s.web.servlet.handler.BeanNameUrlHandlerMapping
    • o.s.web.servlet.handler.RequestMappingHandlerMapping
  • DispatcherServlet이 사용하는 Spring 설정 파일에 bean 등록
<beans>
  <bean id="defaultHandlerMapping" 
    class="o.s.web.servlet.handler.BeanNameUrlHandlerMapping" />
  <bean id="handlerMapping" 
    class="o.s.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
  ...
</beans>
  • 5행: annotation 기반 handler mapping. <bean> 설정 대신 <mvc:annotation-driven /> 이용 가능

 

Step3: Controller 구현 및 설정

  • Annotation 기반 controller
  • Spring에서 제공되는 인터페이스나 클래스를 이용하지 않고 POJO 클래스로 정의
  • @Controller annotation을 클래스에 적용 (자동 bean scan 대상)
  • @RequestMapping annotation을 이용해서 사용자 정의 메소드를 특정 request URL에 대한 handler method로 지정
package com.example.helloworld.web;

import java.util.Calendar;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HelloController {

	@RequestMapping("/hello.do")
	public ModelAndView hello() {
    @RequestParam(value="name", required=false) String name) {
    String message = getGreeting() + name; // request 처리
    ModelAndView mav = new ModelAndView("hello"); // view 이름: "hello"
    mav.addObject("greeting", message); // model 객체 이름: “greeting”
    return mav;
	}

	private String getGreeting() { // business method
		int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
		if (hour >= 6 && hour <= 11) {
			return "Good morning!";
		} else if (hour > 11 && hour <= 14) {
			return "Did you have lunch?";
		} else if (hour > 14 && hour <= 18) {
			return "Good afternoon!";
		} else if (hour > 18 && hour <= 24) {
			return "Good evening!";
		}
		return "Hello!";
	}

}
  • Spring 설정 파일 (dispatcher-servlet.xml)
  • 패키지에 있는 @Controller 적용 클래스의 객체를 Spring bean으로 자동 생성
<context:component-scan base-package=“com.example.helloworld.web”/>

 

Step4: ViewResolver 설정

  • 논리적인 view 이름에 대해 물리적인 view 객체 생성
  • 일반적으로 Spring에서 제공되는 구현 클래스 이용
    • o.s.web.servlet.view.InternalResourceViewResolver
  • Spring 설정 파일에 bean으로 등록
<bean id="viewResolver" 
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="viewClass" values="org.springframework.web.servlet.view.JstlView">
  <property name="prefix" value="/WEB-INF/view/"/>
  <property name="suffix" value=".jsp"/>
</bean>
  • 1행: view 이름에 prefix와 suffix를 붙여 물리적인 view로 사용할 대상 결정
    • "hello" → "/WEB-INF/view/hello.jsp"

 

Step5: View 구현

  • JSP 등을 사용하여 View page 작성
<%@ page contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>인사</title>
  </head>
  <body>
    인사말: <b>${greeting}</b>
  </body>
</html>
  • 9행: HelloController에서 ModelAndView 객체에 저장한 모델 객체 참조 (이름: "greeting"). 모델 객체는 HttpServletRequest 객체의 속성을 통해 JSP page에 전달되고 참조됨

 

Spring 설정 파일 (disparcher-servlet.xml)

<beans xmlns="http://www.springframework.org/schema/beans" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc.xsd ...">
  <context:component-scan base-package="com.example.helloworld.web"/> 
  <mvc:annotation-driven /> 
  <bean id="viewResolver"
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"/>
    <property name="suffix" value=".jsp"/>
  </bean>
</beans>
  • 7행: 패키지 내의 controller 객체들을 bean으로 등록
  • 8행: annotation 기반 HandlerMapping 객체 생성 
  • 9행: ViewResolver 객체 생성

 

Java code 기반 설정 방법

Java Config 클래스를 이용한 설정

  • @ComponentScan
    • @Controller가 적용된 클래스에 대해 자동 bean 생성
  • @EnableWebMvc
    • SpringMVC 기본 설정 수행: RequestMappingHandlerMapping, RequestMappingHandlerAdapter 등 필요 한 bean 생성
@Configuration // JavaConfig
@EnableWebMvc // HandlerMapping 등 MVC 구조에 필요한 기본 객체들 생성
@ComponentScan(basePackages={"com.example.helloworld.web"}) 
public class MvcConfig {
  ...
}

동일

<beans ...>
  <context:component-scan base-package="com.example.helloworld.web"/>
  <mvc:annotation-driven />
  ...
</beans>
  • Spring MVC의 기본 설정을 변경(customize)하기 위해 WebMvcConfigurer 인터페이스 구현
    • 필요한 메소드만 선택적으로 구현
@Configuration
@EnableWebMvc
@ComponentScan(basePackages={"com.example.helloworld.web"}) 
  public class MvcConfig implements WebMvcConfigurer {
  // View Resolver 설정
  @Override
  public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp("/WEB-INF/view/", ".jsp");
  }
  ...
}
  • 8행: InternalResourceViewResolver bean을 생성하고 prefix와 suffix 값 설정

DispatcherServlet 설정 (web.xml)

  • contextClass, contextConfigLocation init-parameter 설정
<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextClass</param-name>
    <param-value>
    org.springframework.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
  </init-param>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      com.example.helloworld.config.MvcConfig
    </param-value>
  </init-param>
</servlet>
<servlet-mapping> … </servlet-mapping>
  • 7행: JavaConfig class를 이용하는 container
  • 13행: JavaConfig class

 

'프로그래밍 > Spring' 카테고리의 다른 글

[Spring] Auto-wiring  (0) 2022.04.25
[Spring] DI - XML 기반 설정  (0) 2022.04.24
[Spring] Spring MVC(1)  (0) 2022.04.17
[Spring] Spring DI  (0) 2022.04.01
복사했습니다!