-
Chapter 5. 스프링 MVC 기본구조코드로 배우는 스프링 웹 프로젝트_eclipse 2021. 4. 22. 00:24
MVC
스프링 MVC는 스프링의 서브 프로젝트입니다. Spring Framework라는 메인 프로젝트 외에도 여러 종류의 프로젝트가 존재하는데 스프링 MVC역시 이러한 프로젝트 중 일부입니다.
스프링은 하나의 기능을 위해서만 만들어진 프레임워크가 아니라 '코어'라고 할 수 있는 프레임워크에 여러 서브 프로젝트를 결합해서 다양한 상황에 대처할 수 있도록 개발되었습니다.
Spring Legacy Project로 생성한 예제와 스프링 MVC가 서브 프로젝트 이므로 구성 방식이나 설정 역시 조금 다르다고 할 수 있다.
위 책에서의 예제의 구조는 위 방식을 구성하게 됩니다.
스프링 MVC 프로젝트의 내부 구조
스프링 MVC 프로젝트를 구성해서 사용한다는 의미는 내부적으로 root-context.xml로 사용하는 일반 Java 영역과 Servlet-context.xml로 설정하는 Web 관련 영역을 연동해서 구동하게 됩니다.
바깥 쪽에 있는 WebApplicationContext라는 존재는 기존의 구조에 MVC 설정을 포함하는 구조로 만들어 집니다. 스프링은 원래 목적 자체가 웹 애플리케이션을 목적으로 나온 프레임워크가 아니기 때문에 달라지는 영역에 대해서 완전히 불린하고 연동하는 방식으로 구현
XML 설정을 이용&JAVA 설정 공통
Eclips에서 Spring Leagacy Project를 이용해서 새로운 프로젝트를 생성합니다.
- ex01
- 프로젝트 : Spring MVC Project
- 패키지명 : org.mido.controller
프로젝트를 생성할 때 말 했던 것처럼 생성된 프로젝트는 스프링 버전이 3버전이므로 pom.xml에서 스프링 5.0.7.RELEASE 버전으로 변경합니다.
<properties> <java-version>1.6</java-version> <org.springframework-version>5.0.7.RELEASE</org.springframework-version> <org.aspectj-version>1.6.10</org.aspectj-version> <org.slf4j-version>1.6.6</org.slf4j-version> </properties>
Lombok 라이브러리 추가
<!--lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.0</version> </dependency>
Sprint-Test 라이브러리 추가
<!-- spring-test --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.7.RELEASE</version> </dependency>
Spring Legacy Project로 생성된 프로젝트는 서블릿 버전이 2.5 버전을 사용하지만 Java 설정 등을 이용할려면 서블릿 버전을 3.0 이상을 사용하는 것이 좋습니다.
그래서 우리는 pom.xml에서 2.5버전을 삭제하고 3.1.0 버전을 추가합니다.
<!--Servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
Maven의 컴파일 옵션은 1.8 버전으로 변경한 후 update project
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin>
이제 우리는 정상적으로 실행이 되는지 확인하겠습니다.
정상 실행됨을 확인할 수 있습니다. URL을 보면 controller로 들어간 것이 아니인데 왜 이런 것으로 나왔나 하면 Tomcat>Modules 메뉴를 이용해서 '/' 경로로 프로젝트가 실행될 수 있도록 처리
Java 설정을 이용하는 경우
우리는 약속한대로 Java 설정을 이용하는 프로젝트는 jex01로 생성하고 web.xml, servlet-context, root-context.xml를 제거합니다.
앞서 말한듯이 web.xml을 사용하는 기본 설정이 잡혀있는 pom.xml에서 web.xml이 없다는 설정을 추가해야하기 때문에 아래의 코드를 추가합니다.
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.2.0</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin>
java 설정을 이용하는 경우 root-context.xml이 없기에 src>main>java에 org.mido.config 패키지를 만들어 WebConfig.class와 RootConfig.class를 선언하도록 하겠다.
RootConfig
package org.mido.config; import org.springframework.context.annotation.Configuration; @Configuration public class RootConfig { }
WebConfig
package org.mido.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer{ @Override protected Class<?>[] getRootConfigClasses() { return new Class[] {RootConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return null; } @Override protected String[] getServletMappings() { return null; } }
RootConfig 클래스는 지금은 별다른 역할이 없으므로 내용 없이 작성합니다. WebConfig는 AbstractAnnotationConfigDispathcerServletInitializer를 상속합니다.
Spring MVC를 이용하는 경우에는 servlet-context.xml을 대신하는 별도의 ServletConfig를 작성합니다.
ServletConfig 클래스를 작성하는 방식은 2가지가 있습니다.
- @EnableWebMvc 어노테이션과 WebMvcConfigurer 인터페이스를 구현하는 방식(과거)에는 WebMvcConfigureAdapter 추상 클래스를 사용했으나, 스프링 5.0 버전부터는 Deprecated 되었으므로 주의합니다.
- @Configuration과 WebMvcConfigurationSupport 클래스를 상속하는 방식 - 일반 @Configuration 우선 순위가 구분되지 않는 경우에 사용
아래의 예제는 @EnableWebMvc를 사용합니다.
ServletConfig
package org.mido.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; @EnableWebMvc @ComponentScan(basePackages = {"org.mido.controller"}) public class ServletConfig implements WebMvcConfigurer{ @Override public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver bean = new InternalResourceViewResolver(); bean.setViewClass(JstlView.class); bean.setPrefix("/WEB-INF/views/"); bean.setSuffix(".jsp"); registry.viewResolver(bean); } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); } }
WebMvcConfigurer는 스프링 MVC와 관련된 설정을 메서드로 오버라이드하는 형태를 이용할 때 사용합니다. ServletConfig 클래스 역시 @ComponentScan을 이용해서 다른 패키지에 저장된 빈을 인식할 수 있습니다.
(*WebMvcConfigurer 인터페이스에서 추상메서드의 구현 에러를 발생시키지 않아 직접 만들었습니다.)
작성된 ServletConfig 클래스가 정상적으로 실행될려면 WebConfig의 설정은 아래와 같이 ServletConfig를 이용하고, 스프링 MVC의 기본 경로도 '/'로 변경되어야 합니다.
WebConfig
package org.mido.config; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer{ @Override protected Class<?>[] getRootConfigClasses() { return new Class[] {RootConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[] {ServletConfig.class}; } @Override protected String[] getServletMappings() { return new String[] {"/"}; } }
설정 이후의 작업은 XML 방식과 동일하게 Tomcat에서 실행하면 됩니다.
정상 출력됨을 알 수 있다.
예제 프로젝트의 로딩 구조
프로젝트가 정상적으로 실행되었다면 서버 구동시 약간의 로그가 기록되는 것을 확인할 수 있습니다. 이 로그를 이용해서 어떤 과정을 통해서 프로젝트가 실행되는지 엿볼 수 있습니다. 프로젝트 구동시 관여하는 XML은 web.xml, root-context.xml, servlet-context.xml 파일입니다. 이 파일들 중 web.xml은 Tomcat 구동과 관련된 설정이고 나머지 두 파일은 스프링과 관련된 설정입니다.
1. 프로젝트의 구동은 web.xml에서 시작합니다. web.xml 상단에 있는 Context Listener가 등록 되어 있고 이것이 가장 먼저 구동됩니다.
<!-- 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>
<context-param>에는 root-context.xml의 경로가 설정되어 있고 <listener>에는 스프링 MVC의 ContextLoaderListener가 등록되어 있는 것을 볼 수 있습니다. ContextLoadListener는 해당 웹 애플리케이션 구동 시 같이 동작하므로 해당 프로젝트를 실행하면 다음과 같이 가장 먼저 로그로 기록됩니다.
2. root-context.xml이 처리되면 파일에 있는 Bean 설정들이 동작합니다. root-context.xml에 정의된 Bean들은 스프링 영역(context)안에 생성되고, 객체들 간의 의존성이 처리됩니다. root-context.xml이 처리된 후에는 스프링 MVC에서 사용하는 DispatcherServlet이라는 서블릿과 관련된 설정이 동작합니다.
<!-- 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>
org.springframework.web.servlet.DispatcherServlet 클래스는 스프링 MVC의 구조에서 가장 핵심적인 역할을 하는 클래스입니다. 내부적으로 웹 관련 처리의 준비작업을 진행하는데 이때 사용하는 파일이 servlet-context.xml입니다. DispatcherServlet에서 XmlWebApplicationContext를 이용해서 servlet-context.xml을 로딩하고 해석합니다. 이 과정에서 등록된 Bean은 기존에 만들어진 Bean과 연동하게 됩니다.
스프링 MVC 기본사상
Java를 이용해서 웹 어플리케이션을 제작을 배우는 경우 Servlet/JSP를 활용해 제작하는 방식을 먼저 배우게 됩니다. 이후 모델2라는 방식을 배우게 됩니다. 스프링 MVC의 경우 이러한 부분은 개발자들에게 보여주지 않고 개발자들이 자신이 필요한 부분만을 집중해서 개발할 수 있는 구조로 만들어져 있습니다.
웹 프로그래밍에서 중요한 두 객체가 존재합니다. 바로 Request / Response 일 것입니다. Servlet/JSP에서는 HttpServletRequest/HttpServletResponse타입의 객체를 이용해 브라우저에서 전송한 정보를 처리하는 방식입니다. 스프링 MVC의 경우 이 위에 하나의 계층을 더하게 됩니다.
Servlet/JSP 위에 Spring MVC 계층이 생기면서 Spring MVC가 내부적으로 Servlet/JSP를 처리하면서 기존의 Servlet/JSP의 API를 사용할 필요성이 현저하게 줄어들게 되었습니다. 스프링은 중간 역할을 수행하기 때문에 이러한 코드를 작성하지 않고도 원하는 기능을 구현할 수 있게 되었습니다.
개발자의 코드는 스프링 MVC에서 동작하기 때문에 어노테이션과 XML등의 설정만으로도 개발이 가능하게 되었습니다.
모델2와 스프링MVC
위에서 말한대로 스프링 MVC는 내부적으로 Servlet API를 활욯합니다. 스프링 MVC는 모델 2라는 방식으로 처리되는 구조이므로 우리는 모델 2가 무엇인지 궁금해할 필요가 있습니다. 모델 2방식은 '로직과 화면을 분리'한다라고 이해하면 됩니다.
모델 2방식에서 사용자의 Request는 특별한 상황이 아닌 이상 먼저 Controller를 호출하게 됩니다. 이렇게 설계하는이유는 나중에 View를 교체하더라도 사용자가 호출하는 URL 자체에 변화가 없게 만들어 주기 때문입니다.
컨트롤러는 데이터를 처리하여 데이터(Model)을 처리하고 Response할 때 필요한 데이터(Model)을 View로 전달하게 됩니다.
https://devpad.tistory.com/24 ① 사용자의 Request는 Front-Controller인 DispatcherServlet을 통해 처리합니다. web.xml을 보면 모든 Request가 이러한 처리를 하도록 설정되어 있습니다.
<!-- 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>
②,③ HandlerMapping은 Request의 처리를 담당하는 컨트롤러를 찾기 위해서 존재합니다. 적절한 컨트롤러가 찾아진다면 HandlerAdapter를 이용해서 해당 컨트롤러를 동작시킵니다.
④ Controller는 개발자가 작성하는 클래스로 실제 Request를 처리하는 로직을 작성하게 됩니다. 이 때 View에 전달해야하는 데이터는 주로 Model 이라는 객체에 담아서 전달합니다. Controller는 다양한 타입의 결과를 반환하는데 이런 처리는 ViewResolver를 사용합니다.
⑤⑥ ViewResolver는 Controller가 반환한 결과를 어떤 View를 통해서 처리하는 것이 좋을지 해석하는 역할입니다. 이에 관한 설정은 servlet-context.xml에 있습니다.
<!-- 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>
⑦⑧ View는 실제로 응답 보내야 하는 데이터를 JSP 등을 이용해서 생성하는 역할을 하게 됩니다. 만들어진 응답은 DispatcherServlet을 통해서 전송됩니다.
'코드로 배우는 스프링 웹 프로젝트_eclipse' 카테고리의 다른 글
Chapter 6. 스프링 MVC의 Controller (0) 2021.04.22 Chapter 4. MyBatis와 스프링 연동 (0) 2021.04.20 Chapter 3. 스프링과 Oracle Database 연동 (0) 2021.04.20 Chapter 2. 스프링의 특징과 의존성 주입 (0) 2021.04.20 Chapter 1 . 개발을 위한 준비 (0) 2021.04.20