Spring MVC(1) - DispatcherServlet, Handler mapping, Controller

MVC pattern

mvc 구성요소

model : 비즈니스 로직 실행, 데이터 처리, 결과 데이터 생성

view : user interface 생성, 출력화면 생성

Controller : request 처리, request/resposne 데이터 전달


DispatcherServlet : 클라이언트의 요청을 받아 컨트롤러에 전달 / 컨트롤러의 처리 결과를 뷰에 전달하여 응답 생성

HandlerMapping : 클라이언트의 요청 URL을 어떤 컨트롤러가 처리할지를 결정

HandlerAdapter : 컨트롤러 객체를 호출하고 결과를 ModelAndView 타입 객체로 변환하여 DispatcherServlet에 반환

ModelAndView : 컨트롤러의 처리 결과 및 뷰 정보를 포함하는 객체

ViewResolver : 컨트롤러의 처리 결과를 출력할 뷰를 결정하고 뷰 객체를 생성

요청 처리 흐름 과정

Web App개발

기본설정

DispathcerSevlet, HandlerMapping, ViewResolver -> 스프링에서 제공되는 구현 클래스들 이용

Application에서 구현해야하는 요소

-Model component : 비즈니스 로직 및 데이터 처리 수행 (Services, Business, DAO 클래스 포함)

-클라이언트로부터 웹 요청을 처리하는 컨트롤러 클래스들

-입출력 화면을 생성하는 뷰 페이지 (JSP, Thymeleaf 등)

 

DispatcherServlet

xml설정

Client로부터 request를 전달받는 servlet 객체(front controller) : WEB-INF/web.xml파일에 dispatcher를 등록한다.

<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>

위와 같은 설정을 통해 모든 요청을 dispatcherServlet이 받게 만들 수 있고 url-pattern에 정의된 /으로 인해 jsp를 제외한 모든 리퀘스트를 처리한다.

 

dispatcher-servlet.xml

url을 맵핑하기 위한 컨트롤러가 되는 클래스와 연결

<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
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan	base-package="com.example.test.web" /> <!-- controller 스캔 및 등록 -->

	<mvc:annotation-driven /> <!-- hanndler mapping -->

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- view resolver -->
		<!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
	    <property name="prefix" value="/WEB-INF/view/" />
		<property name="suffix" value=".jsp" />
	</bean>
</beans>

dispatcherservlet설정파일에는 핸들러맵핑, 컨트롤러, 뷰리졸버, 뷰 같은 빈을 설정하게 된다.

 

Java code기반으로 설정방법

위의 dispatcher-servlet.xml과 같은 설정임

@Configuration //@Controller가 적용된 클래스에 대해 bean생성
@EnableWebMvc //핸들러맵핑, 핸들러어댑터 등 필요한 bean생성
@ComponentScan(basePackageClasses={HelloController.class})
public class MvcConfig implements WebMvcConfigurer {

 

web.xml

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <init-param>
      <param-name>contextClass</param-name> <!-- JavaConfig calss를 이용하는 컨테이너 -->
      <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 <!-- javaConfig 클래스 -->     	
      </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

web.xml에는 한개 이상의 DispatcherServlet 설정이 가능하다.

DispatcherServlet 은 하나의 스프링 컨테이너를 생성한다. 

ContextConfigLocation의 init-parameter를 이용해서 다른 설정파일들을 지정가능하다.

 

여러 계층으로 구성된 Web application은 각계층에 속하는 bean들을 서로 다른 설정 파일로 관리하는 것이 좋다.

이때 사용되는 것이 ContextLoaderListner이다.

web.xml에서 계층별로 나눈 것들을 모두 로드할 때 사용하며, root application을 생성하는 클래스이다.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/serviceContext.xml 
        /WEB-INF/persistenceContext.xml
        classpath:common.xml
    </param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener
    </listener-class> <!-- 위 param-value의 파일들을 참조해서 root container를 생성한다 -->
</listener>
<!-- web application context -->
<servlet>
    <servlet-name>front</servlet-name> <!-- dispatcher servlet 1 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet>
    <servlet-name>rest</servlet-name> <!-- dispatcher servlet 2 -->
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

위 설정에서 front와 rest는 각각 별도의 webapplicationcontext를 생성하므로 각각의 설정 파일에서 생성한 빈을 공유할 수 없다.

동시에 필요한 빈이 있는 경우 contextLoaderListner를 사용하여 설정할 수 있는데, contextConfigLocation의 밸류값 파일들을 이용해 루트 컨테이너가 생성되고 dispatcherServlet들은 이 root를 부모로 하는 자식 컨텍스트가 된다.

 

Hnadler Mapping

request URL이 web.xml의 <servlet-mapping>의 <url-patter>에 매칭되면 @RequestMapping에 지정된 경로들과 비교해서 controller 및 method가 선택된다.

 

servlet mapping 설정에 무관하게 전체 경로를 설정하려면 

<bean id="handlerMapping"
	class="o.s.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
	p:alwaysUseFullPath="true" />

 

controller에 맵핑되지 못한 요청은 모두 deafault servlet으로 전달하여 처리하려고 한다면 -servlet.xml에 다음과 같은 코드를 추가한다.

<mvc:default-servlet-handler />

 

java config를 이용한다면 configureDefaultServletHandling()메소드 구현이 필요하다.

@Configuration
@EnableWebMvc
@ComponentScan(...)
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void configureDefaultServletHandling(
    	DefaultServletHandlerConfigurer configure) {
    		configurer.enable();
    }
    ...
}

이 외에 properties 객체를 통한 맵핑이 필요할 땐 SimpleUrlHandlerMapping

Bean이름을 url 맵핑으로 이용할 땐 BeanNameUrlHandlerMapping

controller의 클래스 명을 이용한 맵핑엔 ConrollerClassNameHandlerMapping을 이용한다.

 

Controller 구현

Mapping

@Controller // 이 class의 객체를 controller로 사용
public class HelloController {
    @RequestMapping("/hello.do") // "/hello.do" request를 처리할 method 정의
    public String hello(Model model) {
        model.addAttribute("greeting", "안녕하세요"); // model data
        return “hello”; // view 이름
    }
}

@RequestMapping을 클래스 명 앞에 붙이게 되면 각 메소드에 적용되는 URL의 기본 경로로 사용된다.

@Controller
@RequestMapping("/register") // 기본 경로 설정
public class RegisterController {
    @RequestMapping("/member") // 예: "/register/member" request URL과 matching
    public String handleMember() { ... }
    @RequestMapping("/event/*.do") // 예: "/register/event/concert.do" 와 matching
    public String handleEvent() { ... }
}

HTTP 전송 방식을 지정할 수 있다.

@RequestMapping(value="/newArticle.do", method=RequestMethod.GET)
public String form(){ ... } //get방식의 리퀘스트를 처리함
@RequestMapping(value="/newArticle.do", method=RequestMethod.POST)
public String submit(){ ... } //post방식의 리퀘스트를 처리함

@GetMapping == @RequestMapping(Method=RequestMethod.GET)

@PostMapping == @RequestMapping(Method=RequestMethod.POST)

 

parameter 추출

@Controller
public class RegisterController {
    @GetMapping("/register/step2")
    public String handleStep2(HttpServletRequest request, Model model) {
        String agree = request.getParameter("agree"); // request parameter 추출
        if (agree.equals("false")) { // 주의: agree가 null이면 오류 발생!
        	return "register/step1";
    	}
        model.addAttribute("registerRequest", new RegisterRequest());
        return "register/step2";
    }
}

HttpServletRequest 객체를 이용해서 agree라는 이름의 파라미터를 추출할 수 있다.

적용된 method parameter 타입으로 자동변환이 수행되지만 변환이 불가능할 경우 오류가 발생

required 속성을 false로 설정하면 파라미터가 존재하지 않을 때 null값을 할당함

defaultValue 속성을 통해 default 값을 지정 가능

 

URL template

URL을 확인하기 전에 대체해야하는 매개변수를 포함하는 URL을 지정하는 방법

@Controller
@RequestMapping("/members")
public class MemberController {
    @RequestMapping("/{memberId}/orders/{orderId}")
    public String memberOrderDetail (@PathVariable String memberId,
    				@PathVariable("orderId") int orderNum, Model model) {
        model.addAttribute("order", new OrderInfo(orderNum, memberId));
        return "member/orderDetail";
    }
}

URL 예 : /members/minsu/orders/12

 

 

'공부기록 > 스프링' 카테고리의 다른 글

스프링 view 구현 (jsp)  (0) 2023.05.23
스프링 HTTP Session 사용  (0) 2023.05.22
Spring MVC(2) - Form처리  (0) 2023.05.22
스프링의 DI  (0) 2023.04.20
스프링 컨테이너와 스프링 빈  (0) 2023.03.22
myoskin