본문 바로가기

Programing/JVM(Java, Kotlin)

[Spring] Spring 3.0 시작 - Hello World 동작원리

지난 시간에 소스코드를 만들어보았다.
MVC 패턴에 대해서는 따로 언급을 하지 않을 예정이니 처음 듣는다면 인터넷에서 검색해서 공부하자.
(위키백과 - 한글 / 영문)


브라우저 관점

철처하게 스프링프레임워크를 떠나서 브라우저 관점에서 보자.
지난 시간에 봤던 아래 페이지는 웹브라우저의 주소로 부터 시작한다.


HTTP GET ( 호스트 - localhost / 포트 - 8080 / 경로 - /HelloWorld/ )

- 클라이언트(브라우저) 요청

GET /HelloWorld/ HTTP/1.1

Accept: text/html, application/xhtml+xml, */*

Accept-Language: ko

User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0)

Accept-Encoding: gzip, deflate

Host: 10.17.29.20:8080

Connection: Keep-Alive

- 서버 응답

HTTP/1.1 200 OK

Server: Apache-Coyote/1.1

Content-Type: text/html;charset=UTF-8

Content-Language: ko

Content-Length: 183

Date: Thu, 13 Sep 2012 02:46:54 GMT


<html>

<head>

<title>Home</title>

</head>

<body>

<h1>

Hello world!

</h1>


<P> The time on the server is 2012년 9월 13일 (목) 오후 3시 52분 28초. </P>

</body>

</html>


서버관점

브라우저 관점에서는 서버란 HTML 페이지를 반환해주는 웹서버(TCP 서버)일 뿐이다.

이제 서버, 스프링 프레임워크에서 어떻게 요청을 처리하는지 따라가보자.

기본적으로 자바의 서블릿에 대해서는 알고 있는 것이 이해하기 편할 것이다.


웹 설정 파일(Web Configuration file)

서블릿은 기본적으로 web.xml 이라는 설정파일을 가지고 있다.

참고로 src/main/webapp/WEB-INF/web.xml 에 위치한다.


열어보면 아래와 같은 부분이 있을 것이다.

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

서블릿 이름은 appServlet이고, 서블릿 클래스가 'org.springframework.web.servlet.DispatcherServlet'로 지정이 되어 있다.


디스패처 서블릿(Dispatcher Servlet)

디스패처 서블릿은 클라이언트 요청을 핸들링하기 위해 앞단에서 컨트롤 하기 위해 존재한다.
클라이언트로 부터 오는 URL이 무엇이되던, 이 서블릿은 컨트롤러에 요청 객체로 넘겨주기 전에 클라이언트의 요청을 가로챌 것이다. 참고로 DispatcherServlet은 스프링라이브러리인 spring-webmvc-3.1.0.RELEASE.jar 안에 있다.

이 클래스는 아래와 같은 시그너처로 되어 있다.

public class DispatcherServlet extends FrameworkServlet

* FrameworkServlet - org.springframework.web.servlet.FrameworkServlet : 스프링의 웹 프레임워크를 위한 기본 서블릿이다. 전 솔루션에 걸쳐 자바빈 기반의 스프링 애플리케이션 컨텍스트를 이용하여 통합을 제공한다.


초기화 파라메터(init-param; initial parameter)

init-param은 초기화 파라메터를 설정하는 부분인데, 컨텍스트 설정 위치가 '/WEB-INF/spring/appServlet/servlet-context.xml'로 되어 있음을 알 수 있다.


servlet-context.xml

<annotation-driven />

<resources mapping="/resources/**" location="/resources/" />

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

맨위의 annotation-driven은 어노테이션 기반을 사용하겠다고 설정하는 것이다. 컨트롤러 작성을 xml 상에 일일이 <bean> 태그를 이용해서 스프링 빈으로 등록할 수 있으나 클래스에 @Controller 어노테이션을 붙이면 해당 클래스를 웹 요청을 처리하는 컨트롤러로 사용할 수 있다. (스프링 2.5부터 지원)


resources 태그는 HTTP GET으로 들어온 /resources/~ 형태의 요청(보통 정적 파일)을 웹어플리케이션루트/resources 디렉토리로 지정을 하겠다는 것이다.


웹 페이지에서 최종형태는 HTML 같은(PDF나 엑셀파일이 될 수 도 있다)형태의 뷰이다. 기본적으로 템플릿에는 jsp 페이지로 되어 있다. 컨트롤러는 ViewResolver를 통해 View 객체를 리턴하게 되는데 위에서는 'InternalResourceViewResolver'가 지정되어 있다.


/HelloWorld/ 요청은 어디로?

앞에서 브라우저 주소에 입력한 'http://localhost:8080/HelloWorld/'는
localhost의 8080포트로 GET /HelloWorld/ HTTP/1.1 요청을 한다고 봤다.
서블릿은  /HelloWorld를 보고 HellowWorld쪽으로 넘기고 HelloWorld 프로젝트 관점에서는 / 라는 리소스의 요청이 있는 것이다. 이 요청은 HomeController 클래스가 담당하도록 템플릿이 만들어준다.



코드는 아래와 같다. (로거부분은 제외 시켰다.)

@Controller

public class HomeController {

        @RequestMapping(value = "/", method = RequestMethod.GET)

        public String home(Locale locale, Model model) {

               Date date = new Date();

               DateFormat dateFormat = DateFormat.getDateTimeInstance(
                                                    DateFormat.LONG, DateFormat.LONG, locale);

               String formattedDate = dateFormat.format(date);

               model.addAttribute("serverTime", formattedDate );

               return "home";

        }

}

servlet-context.xml에서 annotation-driven에서 언급되었듯이 HomeController 클래스에 @Controller라는 어노테이션이 붙어있다. 어노테이션이 붙어있으면 서버가 시작할 때 RequestMappingHandlerMapping에 의해서 자동으로 등록이 된다.

INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String com.tistory.namocom.HomeController.home(java.util.Locale,org.springframework.ui.Model)


HomeController에는 home이라는 메소드가 있는데 @RequestMapping이라는 어노테이션이 붙어있다. 요청처리를 매핑하기 위한 어노테이션인데, HTTP GET으로 "/"에 대한 처리를 담당한다고 보면 된다.


브라우저에서 요청한 http://localhost:8080/HelloWorld/는 결국 HomeController.home 이라는 메소드로 들어오게 되는 것이다.

이 home 메소드에서 해주는 일은 단순하다.
 - java.text.DateFormat 클래스에서 현재 날짜와 시간을 해당 로케일에 맞게 가져온 후에

 - 문자열로 만들어서

 - serverTime라는 속성에 넣은 후

 - home 이라는 뷰에 넘기게 되어 있다.


그럼 home은 어디로 넘어가는가?

아까 servlet-context.xml에서 InternalResourceViewResolver 가 지정되어 있음을 기억해보자.

prefix(접두사)가 /WEB-INF/views/ 였고, suffix(접미사)가 .jsp 였다.

결국 home이라는 뷰 이름은 /WEB-INF/views/home.jsp 으로 바뀌게 된다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<html>

<head>

        <title>Home</title>

</head>

<body>

<h1>

        Hello world! 

</h1>

 

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

</body>

</html>


JSP는 HTML을 만들기 위한 템플릿일 뿐이다.

결국은 아래와 같은 HTML이 동적으로 만들어진다. 어디가 차이인지 알겠는가?
바로 ${serverTime} 부분이 2012년 9월 13일 (목) 오후 3시 52분 28초 와 같은 문자열로 대체가 된 것이다.

컨트롤러에서 model.addAttribute("serverTime", formattedDate ); 에서 모델의 애트리뷰트 이름이 serverTime이었다.

<html>

<head>

        <title>Home</title>

</head>

<body>

<h1>

        Hello world! 

</h1>

 

<P>  The time on the server is 2012 9 13 () 오후 3 52 28. </P>

</body>

</html>


전체적인 그림

웹브라우저에서 시작된 요청부터, 결과까지 개념적으로 그림으로 만들어보았다.



지난번에 깨졌던 한글은?

이번에 보면 한글이 다 잘 나오고 있는데 어떻게 해결을 한 것일까?

결론은 간단하다. jsp에서 페이지 인코딩을 UTF-8로 하도록 조정을 해주었다.

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<%@ page session="false" pageEncoding="UTF-8"%>

<html>

<head>

        <title>Home</title>

</head>

<body>

<h1>

        Hello world! 

</h1>

 

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

</body>

</html>

참고로 본인은 개인적으로 프로젝트의 인코딩 형식을 UTF-8로 하고 있다. 다국어 지원을 위해서 말이다.
스프링에서도 기본적으로 다국어 지원을 위한 로케일을 지원하고 있지 않은가?

 - 위에서 home 메소드에 첫 번째 인자가 Locale locale 이다!


이상으로 HelloWorld 예제 분석을 마쳐본다.

스프링에 대해 더 자세히 알고 싶으면 스프링 홈페이지에서 Document를 읽거나 Introduction to Spring MVC Framework를 읽어보면 도움이 될 것 같다.