지난번 글에 이어서 웹어플리케이션을 설정하겠습니다.
1. web.xml 설정
모든 설정이 자동으로 되어있어 별다른 설정이 필요없는 스프링 부트와 달리 레거시나 메이븐 프로젝트의 경우 웹어플리케이션을 설정해야합니다.
우선 web.xml 파일을 작성하겠습니다. web.xml 파일은 클라이언트의 요청 경로와 요청을 서블릿 사이의 매핑을 xml을 통해 정의하는 파일입니다. sevlet 태그를 통해 서블릿을 선언하고 servlet-mapping 태그에 매핑할 경로를 정의합니다.
/src/main/webapp/WEB-INF/web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app> <display-name>Mission Web Application</display-name> <servlet> <servlet-name>hello</servlet-name> <servlet-class>kr.kro.rubisco.work.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app> |
hello라는 이름의 서블릿을 정의하는데, 해당 서블릿의 타입(클래스)는 kr.kro.rubisco.work.TestServlet 입니다. hello 서블릿은 /hello라는 클라이언트의 요청에 매핑됩니다.
이제 kr.kro.rubisco.work 패키지에 TestServlet 이라는 서블릿 클래스를 작성하면 됩니다.
처음에는 소스 경로가 없는데, /src/main 폴더에 java 폴더를 생성하면 소스경로가 생깁니다.
해당 소스경로에 오른쪽 클릭을 하여 패키지를 생성합니다.
해당 패키지에 TestServlet 클래스를 생성하여 다음과 같이 작성합니다.
/kr.kro.rubisco.TestServlet.java
package kr.kro.rubisco.work;
import java.io.IOException;
import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet;
public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L;
public void init(ServletConfig config) throws ServletException { System.out.println("init() 실행됨!"); }
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException { System.out.println("service() 실행됨!"); } } |
서블릿이기때문에 웹요청에 대한 서블릿인 HttpServlet을 상속받아 구현합니다.
http://localhost:8080/work/hello 경로로 이동하면 hello 서블릿으로 매핑되어 서비스 처리를 하는 것을 볼 수 있습니다.
init 메소드는 톰캣서버가 실행될 때 최초 한번 실행되는 초기화 메소드이며, service 메소드는 클라이언트 요청이 있을때마다 호출되는 메소드입니다.
HttpServlet에 다양한 메소드가 있는데 일단 생략하겠습니다.
스프링 프레임워크의 DispatcherServlet 역시 HttpServlet을 상속받은 서블릿입니다. 즉, 모든 요청에 대하여 DispatcherServlet에 매핑되도록 하면 스프링 컨테이너를 통해 클라이언트 요청을 제어할 수 있게 됩니다.
다음과 같이 web.xml을 작성해주세요.
/src/main/webapp/WEB-INF/web.xml
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Mission Web Application</display-name>
<!-- 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> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter> <filter-name>httpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 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> |
레거시 프로젝트에서 그대로 가져왔습니다.
display-name 태그는 어플리케이션의 이름을 나타냅니다.
context-param은 전역변수에 해당하는 Root WebApplicationContext를 설정합니다. 컨텍스트는 쉽게 말하자면 빈(bean)이 담겨있는 컨테이너라고 보시면 됩니다. ServletContext는 크게 2가지로 형태로 존재하는데, 그 중 하나가 전역변수에 해당하는 Root WebApplicationContext 입니다.
서블릿이 로드될 때 아래에서 설명할 ContextLoaderListener에 의하여 Root WebApplicationContext가 생성되는데, 이때 ContextLoaderListener는 context-param의 contextConfigLocation 파라미터에 지정된 설정파일에 따라 빈을 로드하여 컨텍스트를 생성하고 ServletContext에 저장합니다. contextConfigLocation이 설정되지 않으면 기본값으로 /WEB-INF/applicationContext.xml 경로파일을 찾으며, 해당 컨텍스트 파일에는 보통 Service나 DAO 객체가 정의됩니다.
fliter는 지난번 글에서 자세히 설명했으므로 생략합니다. 인코딩 필터와 히든 메소드 필터를 추가합니다.
listener는 특정 이벤트에 대하여 호출되어 처리하는 객체입니다. ContextLoaderListener의 경우 웹서비스가 시작되거나 종료될 때 호출되며, 이때 전역변수에 해당하는 Root WebApplicationContext를 생성하거나 제거하는 역할을 합니다. 위에서 말한것과 같이 리스너에 의해 생성된 Root WebApplicationContext는 ServletContext에 저장됩니다.
servlet 태그 아래 init-param 태그는 지역변수에 해당하는 Child WebApplicationContext를 설정합니다. 서블릿에 의해 생성되며, 루트 컨텍스트와 마찬가지로 contextConfigLocation 파라미터에 지정된 설정파일에 따라 빈을 로드하여 컨텍스트를 생성하고 ServletContext에 저장합니다. contextConfigLocation이 설정되지 않으면 기본값으로 /WEB-INF/[서블릿 이름]-servlet.xml 경로파일을 찾으며, 해당 컨텍스트 파일에는 보통 Controller나 ViewResorver 객체가 정의됩니다.
Child WebApplicationContext에서는 ContextLoaderListener에 의해 생성된 Root WebApplicationContext에 접근이 가능합니다. 하지만 그 반대는 불가합니다. 즉, Controller 또는 ViewResolver에서는 Service나 DAO를 참조할 수 있지만, Service에서 Controller는 참조하지 못합니다. SevletContext에 저장된 빈들은 ServletConfig의 getinitParameter 메소드를 통해 불러올 수 있습니다. 위에서 작성한 TestServlet의 init 메소드를 참고하세요.
마지막으로 load-on-startup 태그는 톰캣서버가 실행될 때 DispatcherServlet의 초기화 여부와 순서를 나타냅니다. 서블릿은 최초 클라이언트의 요청에 대하여 한번 초기화되는데, 그렇기때문에 최초 요청에 대하여 처리속도가 느릴 수 있습니다. 이때 해당 태그를 양수로 입력하면 클라이언트의 최초 요청이 아니라 톰캣서버가 실행되는 시점에서 서블릿이 초기화되며, 숫자는 초기화되는 우선순위를 나타냅니다.
2. root-context.xml 설정
root-context.xml 파일은 context-param에서 정의되는 Root WebApplicationContext의 설정파일입니다. 위에서 말했듯이 해당 컨텍스트는 전역변수와 같아서 모든 ServletContext에서 참조가능합니다.
/src/main/webapp/WEB-INF/spring/root-context.xml
DAO나 Service 등 모든 서블릿에서 접근가능한 빈이 등록되는데 DB연결은 나중에 하도록 하고 우선 context 네임스페이스의 component-scan을 설정합니다. 속성으로 base-package를 입력하는데, 해당 값에 등록된 패키지를 스캔하여 @Component 어노테이션이 붙은 클래스를 자동으로 빈(bean)으로 등록하는 역할을 합니다. kr.kro.rubisco.work.service 패키지에 서비스를 작성할 예정이므로 미리 해당 패키지를 생성해둡시다.
3. sevlet-context.xml 설정
servlet-context.xml 파일은 servlet의 init-param에서 정의되는 Child WebApplicationContext의 설정파일입니다. 위에서 말했듯이 해당 컨텍스트는 지역변수와 같아서 해당 ServletContext에서만 참조가능합니다.
/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml
네임스페이스로 mvc, context, beans를 설정하고, 빈으로 InternalResourceViewResolver을 등록했습니다. 해당 resolver는 DispatcherServlet의 기본 뷰 리졸버로, JSP를 뷰로 사용할 때 쓰입니다. 프로퍼티로 prefix와 suffix를 가지며, 클라이언트의 요청으로부터 접두어(prefix)와 접미어(suffix)를 붙여 실제 리소스 경로를 찾을수 있도록 돕습니다. 즉, /hello 라는 ViewName을 통해 /WEB-INF/views/hello.jsp 파일을 찾게 됩니다. prefix를 변경했기때문에 기존에 생성되어있던 index.jsp 파일을 /WEB-INF/views 경로로 이동시켜주세요.
서비스와 마찬가지로 context 네임스페이스의 component-scan을 설정하여 kr.kro.rubisco.work.controller 패키지를 스캔하도록 합니다. 해당 패키지에는 컨트롤러를 작성할 예정이므로 미리 패키지를 생성해둡시다.
annotation-driven 태그는 어노테이션을 통해 Controller 호출이나 bean 객체 등록 등의 매핑작업을 편리하게 할 수 있도록 해줍니다.
resources 태그는 정적 자원의 경로를 설정합니다. 클라이언트가 /resources/** 경로로 요청하면 /resources/ 경로에서 정적 자원을 가져옵니다.
4. Controller 작성
스프링 MVC의 설정은 끝이며, 여기서부터는 기존 레거시 프로젝트와 동일하게 진행하면 됩니다. 톰캣서버에서 루트 경로를 /으로 변경하고 다음 컨트롤러와 뷰를 작성 후 http://localhost:8080/hello으로 이동해보세요.
/kr/kro/rubisco/controller/WorkController.java
package kr.kro.rubisco.work;
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping;
@Controller public class WorkController {
@GetMapping("/hello") public void getHelloView() {} } |
/src/main/webapp/WEB-INF/views/hello.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>안녕하세요?</title> </head> <body> <h1>이클립스를 통한 스프링 MVC 설정</h1> </body> </html> |
|