Spring/3.x

[펌]Spring MVC 시작하기

EnterKey 2016. 1. 14. 18:31
반응형



SpringMVC Getting Started

나는 이전까지 Spring 이 웹 애플리케이션에만 사용되는 줄 알았는데, 그게 아니고 Console 이나 GUI 등 다양한 곳에 쓰일 수 있는 경량 컨테이너 라고 한다. 

여러 곳을 찾아보니 Spring 은 AOP와 DI가 핵심이라는데, 이것들 보다는 Spring MVC 자체에 대해 사용법을 먼저 알아보고자 한다. 프로그래밍의 재미는, 만드는 것 이지 개념 학습이 아니기 때문이다. 


1. Architecture

View - Controller - Service - Dao - Model - DB 이런 구조로 데이터가 흘러다닌다. DAO(Data Access Object) 와 Service 의 차이점은 여기에 의하면, 

DAO : 단일 데이터 접근 / 갱신만 처리 
Service : 여러 DAO를 호출하여 여러번의 데이터 접근/갱신을 하며 이렇게 읽은 데이터에 대해 비즈니스 로직을 수행하고 하나의 트랜잭션으로 묶는다. 

Service 와 DAO 가 동일해지는 경우도 있는데, 이때는 비즈니스 로직이 단일 DB접근으로 끝나기 때문이다.


2. Spring MVC

Spring MVC 에 대해 알아야 할 사항은 모든 요청을 Dispatcher Servlet 이 받는 다는 것. 그래서 적절하게 처리할 Controller 를 URL 에 따라 선택하기 위해 Hanlder Mapping 을 이용하고, 선택된 Controller 는 요청을 처리하고 ModelAndView 를 돌려준다. Dispatcher Servlet 은 돌아오는View Name 을 바탕으로 View Resolver 를 호출해 View 를 얻고 여기에 Model 을 적용해 Response 를 만들어 Request 를 보냈던 Client 에게 돌려준다. 일단 그림을 먼저 보자. 

Spring MVC Flow

(출처 - http://www.mkyong.com/spring-mvc/spring-mvc-hello-world-example/

Node.js 나 Backbone.js 를 하셨던 분이라면 무리 없이 이해가 가능할거다. Controller 나 View 는 역할도 비슷하고, Backbone 이나 Node 의Router 가 Handler Mapping 인 것도 비스무리 하다. 이름만 다를 뿐. 조금 다른점은 View 를 묶어 View Resolver 가 관리 한다는 것이다. 하지만 사실 Backbone 에서도 수 많은 View 를 배열이나 기타 등등을 통해 관리한다는 점을 보면, 프로그래머가 구현했어야 하는 걸 Spring MVC가 해준다는 정도의 차이만 존재한다. 기능상으로는 별 차이가 없다. (아! 드디어 MVC 입문했다!)


3. Sample Code

Spring Tool Suite 로 SpringMVC 프로젝트 생성하면 나오는 샘플의 코드를 분석해 보자. import 구문은 제외한다. 

@Controller
public class HomeController {

    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home(Locale locale, Model model) {
        logger.info("Welcome home! The client locale is {}.", locale);

        Date date = new Date();
        DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

        String formattedDate = dateFormat.format(date);

        model.addAttribute("serverTime", formattedDate );

        return "home";
    }

}


중요한 부분은 @Controller 다. 원래는 XML 에 기술해야 하는것으로 아는데, 스프링 버전이 올라가면서 Annotation 만으로 해결이 가능해졌다. 
Controller 클래스 내부에는 각 URL 요청을 담당하는 메소드가 존재한다. @RequestMapping 어노테이션을 통해 등록하며, value 는 URL 경로를,method 는 HTTP Method 를 등록하면 된다. Home Controller 는 / 에 대한 요청밖에 없지만, User Controller 라면 /users/users/51041 등을 처리할 수 있는 여러 메소드가 있을 수 있다. 

Controller 의 @RequestMapping 메소드는 Model 이라는 파라미터를 받는데, 이건 return 문에서 지정한 Home 이라는 이름의 View 에 적용할Model Attribute 다. 이 serverTime 이란 값은, Backbone.js 를 사용하신 분이라면, Underscore Template 에 지정되는 <%= serverTime %> 정도로 이해하면 되실거다. 실제로 그러한가 View 를 보자. 

// src/main/webapp/WEB-INF/views/home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
    <title>Home</title>
</head>
<body>
<h1>
    Hello world!  
</h1>

<P>  The time on the server is ${serverTime}. </P>
</body>
</html>


그러하다. 그런데 만약, @RequestMapping 메소드에서 파라미터를 받는다면 어떻게 추출할까? Spring 에서 관리하는 예제인 Pet-Clinic 샘플을 보자. 

@RequestMapping(value = "/owners/{ownerId}/edit", method = RequestMethod.GET)
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
    Owner owner = this.clinicService.findOwnerById(ownerId);
    model.addAttribute(owner);
    return "owners/createOrUpdateOwnerForm";
}


이것도 별거 없다. @RequestMapping 에서 받은 {ownerId} 를 @PathVariable("onwerId") 로 처리하고 있다. 

스프링은 위의 예처럼 String 으로 리턴값을 통해 View 를 지정하는 것이 아니라, ModelAndView 를 리턴하여 View 를 지정할 수 있다. 

@RequestMapping("/owners/{ownerId}")
public ModelAndView showOwner(@PathVariable("ownerId") int ownerId) {
    ModelAndView mav = new ModelAndView("owners/ownerDetails");
    mav.addObject(this.clinicService.findOwnerById(ownerId));
    return mav;
}


String 을 리턴하는 형태와 똑같은 일을 한다. 두 가지 종류의 방법이 있을 뿐이다. 여기에 대해서 천조국 형님들도 똑같다 고 답변했다. 

마지막으로 @ModelAttribute 를 살펴보려 했으나 좀 복잡한것 같다. 누구는 생략가능하다고 하고, 누구는 @ModelAttribute 를 붙여야 할 경우가 따로 있다고 설명하기도 한다. 그래서 마지막으로 SpringMVC Sample의 디렉토리 구조에서 XML들을 좀 살펴보고 가자. 

Spring MVC Sample Directory

web.xml 은 root-context.xml 을 Root Spring Container로 지정해 모든 필터와 서블렛들이 공유하도록 한다. 또한 servlet-context 를 이용해DispatcherServlet 클래스가 Front-end Controller 로 동작할 수 있도록 해 준다. 다시 말해 요청을 처리할 수 있도록 해 준다는 뜻이다. 코드를 보자. 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>


root-context.xml의 내용은 아래와 같다. 옛날에는 무슨 역할을 했을지 모르겠지만, 내가 공부하는 이 시점(2014.02-03) 에는 별 내용이 없다. 아니면 샘플이 심플해서 별 내용이 없는 거겠지. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- Root Context: defines shared resources visible to all other web components -->
</beans>


마지막으로 servlet-context.xml 에는 CSS나 Image 등 각종 리소스가 있는 statis 폴더를 지정하고. View 의 파일 타입과 폴더를 지정한다. 역시 코드를 보자. 

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:beans="http://www.springframework.org/schema/beans"
    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">

    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <context:component-scan base-package="com.sample.sample" />
</beans:beans>


4. Summary

  1. @Controller : 컨트롤러 클래스 어노테이션
  2. @RequestMapping(value = "/", method = RequestMethod.GET) : HTTP Request 핸들러 메소드 어노테이션
  3. model.addAttribute("serverTime", formattedDate ); : View 에 적용할 Model Attribute 추가
  4. return "home"; : 돌려줄 View 이름 지정, ModelAndView 를 리턴해줄 수도 있다.
  5. @PathVariable : URL 요청에서 받은 파라미터 값을 추출할때 사용


References

  1. Valid
  2. SessionAttribute
  3. ModelAttribute

출처: http://anster.tistory.com/142


반응형