본문으로 바로가기

* 스프링 프레임 워크

1. 스프링은 POJO (Plain Old Java Object) 방식의 프레임워크로서, 일반적인 J2EE 프레임워크에 비해 특정 라이브러리를 사용할 필요가 없어서 개발이 쉬우며, 기존 라이브러리의 지원이 용이합니다.


2. 스프링은 관점지향프로그래밍, AOP(Aspect Oriented Programming)를 지원합니다. 트랜잭션, 로깅, 보안 등 여러 모듈, 여러 계층에서 적용되는데, 이런 코드들을 실제 비지니스 로직과 분리할 수 있도록 도와줍니다. 한때, AOP가 OOP(Object Oriented Programming)를 대체하는 기술로 생각되기도 했지만, 실제로 AOP는 OOP를 더욱 OOP스럽게 보완해 주는 기술입니다.


3. 스프링은 의존성 주입, DI (Dependency Injection)를 지원합니다. 이는 객체간의 의존관계를 관리하는 기술이라고 생각하면 됩니다. 어떤 객체가 필요로 하는 객체를 자기 자신이 직접 생성하는것이 아니라, 외부에 있는 다른곳에서 자신이 필요로 하는 객체를 주입받는것을 말합니다.


4. 스프링은 제어 반전, IoC (Inversion of Controller)를 지원합니다. 컨트롤의 제어권이 개발자가 아니라 프레임워크에 있음을 말합니다. 즉, 객체의 생성부터 모든 생명주기의 관리까지 객체의 제어권이 바뀐것을 의미합니다.

출처 : http://addio3305.tistory.com/36?category=772645


스프링에서 MVC를 쓰는 이유는 스프링이 제공하는 트랜젝션, DI, AOP를 손쉽게 사용하기 위해서라고 한다. 그리고 스트럿츠와 같은 프레임워크와 연동하려면 설정의 중복과 개발 과정에서 오는 불편함들을 줄일 수 있다고 한다. 스프링도 다른 MVC 프레임워크와 같이 컨트롤러를 사용하여 클라이언트의 요청을 처리하는데, 이 컨트롤러의 역할을 DispatcherServlet에서 한다. 그럼 이제 스프링 MVC 구조를 살펴보자.


 구성 요소

 설명 

 DispatcherServlet

 클라이언트의 요청을 전달받아 요청에 맞는 컨트롤러가 리턴한 결과값을 View에 전달하여 알맞은 응답을 생성 

 HandlerMapping

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

 Controller

 클라이언트의 요청을 처리한 뒤, 결과를 DispatcherServlet에게 리턴 

 ModelAndView

 컨트롤러가 처리한 결과 정보 및 뷰 선택에 필요한 정보를 담음 

 ViewResolver

 컨트롤러의 처리 결과를 생성할 뷰를 결정 

 View

 컨트롤러의 처리 결과 화면을 생성, JSP 또는 Velocity 템플릿 파일 등을 뷰로 사용 

출처: http://gangzzang.tistory.com/entry/스프링Spring-MVC-프레임워크Model-View-Controller-Framework-1 [갱짱.study]


@_@ 아이고 복잡하다.. 하나씩 열어보면서 구조를 파악하는 게 오히려 더 쉬울 것이다. 나는 주로 이런 설명보다는 구조를 보고 파악하면서 공부하는 게 더 좋다. 왜냐하면 기억에도 잘 남고 프로젝트를 진행하면서도 어디로 가야하는 지 빠르게 캐치할 수 있기 때문이다. 그리고 구조를 다 보고 나면 이 설명들도 쉽게 이해가 된다. 아래는 Spring MVC로 생성한 프로젝트이다. 하나씩 차근 차근히 보자. 그리고 이건 처음 프로젝트를 생성했을 때 구조이고, 나중에 프로젝트의 구조를 다시 재정의 할 수도 있다.




1. Maven Dependencies

 Maven에서 자동적으로 받아오는 라이브러리들을 확인할 수 있는 곳이다. 프로젝트를 진행하다 보면 이러한 라이브러리를 추가하여 다운받아야 할 경우가 생긴다. 그럼 필요한 라이브러리를 직접 다운받아서 해당 폴더에 넣느냐? 그렇지 않다. 프로젝트 내에서 태그를 이용해 간단히 추가하거나 삭제할 수 있다. pom.xml이 이러한 라이브러리를 추가하고 관리할 수 있는 곳이다. pom.xml을 한번 살펴보자.


맨 밑에 있는 pom.xml을 열어서 아래의 맨 왼쪽의 pom.xml탭을 클릭해준다.


그럼 이와 같은 파일을 확인 할 수 있는데, 이곳에서 우리가 필요한 Maven 라이브러리를 관리할 수 있다. 파일을 자세히 보자. 


<properties></properties> 태그는 변수의 개념으로 많이 사용되는 변수를 선언해서 밑에서 편하게 쓰기 위해서이다. 많이 사용되는 값을 변수로 지정한다. 수정의 용이함이 있기에 일일이 찾아서 수정할 필요가 없다. <org.springframework-version>3.1.1.RELEASE</org.springframework-version> 처럼 변수 선언을 해 놓으면 밑에서 ${org.springframework-version}로 사용할 수 있다. 만약 버전이 바뀌면 선언한 곳의 버전만 수정해주면 된다.


<repositories></repositories> 태그도 있는데, 실제 라이브러리를 다운 받을 장소를 의미한다. 따로 설정할 필욘 없다. 만약 인터넷이 없는 곳에서 프로젝트를 진행하게 되면 내부 저장소를 만들고, 내부 저장소에서 라이브러리를 다운 받는다고 한다. 이럴때 사용하는 거라서 지금은 딱히 필요없다. 


<dependency></dependency> 태그는 라이브러리 하나를 의미한다. 가장 알아둬야 할 중요한 태그이다. 이 태그를 이용해서 라이브러리를 추가하거나 삭제할 수 있다. 번거롭게 원하는 라이브러리를 직접 다운받아 폴더에 넣지 않아도 태그 하나로 라이브러리를 추가할 수 있다. 너무 간단한 것 (❁´▽`❁)


그럼 추가한 라이브러리들은 어디에 저장되냐? 그것은 우리가 설정한 경로에 들어가있다. 경로를 언제 설정했냐고? Maven을 설정할 때 setting.xml에 <LocalRepostiory>에 repository 경로를 설정했었다. 바로 이 경로에 라이브러리들이 추가 되어 있다. 자신이 설정한 경로(D:\project\apache-maven-3.5.3\repository)에 가면 pom.xml의 <dependency>태그에 감싸진 라이브러리들을 파일형태로 확인할 수 있다. 태그로 라이브러리를 추가하는 것만으로도 파일들이 저절로 추가되고 생겨난다. 완전 핵 신기하다.




2. web.xml

web.xml은 tomcat(WAS)이 최초 구동될 때, WEB-INF 디렉토리에 존재하는 web.xml을 읽고, 그에 해당하는 웹 어플리케이션 설정을 구성한다. 그냥 쉽게 말해 서버가 구동되면 제일 먼저 인식하는 파일이 web.xml이다. 그럼 web.xml을 한번 보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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>
cs

- 스프링 context 설정 (7~10행) contextConfigLocation

스프링 context 설정파일 목록을 불러오기 위한 곳이다. 9행에서는 context 경로를 설정할 수 있는 곳이다.


- DispatcherServlet 설정 (18~26행)

<servlet> 태그를 이용해서 DispatcherServlet를 설정할 수 있다. 23행의 경로를 통해 스프링 설정 정보를 읽어온다.


- 요청URL (28~31행)

요청 URL 패턴(30행)을 설정한다. 서블릿에서 어떠한 요청을 할 때, 이 패턴을 통해서만 요청이 전달된다. .html 등의 직접적인 요청은 전달되지 않는다. 보통 *.do를 하는데, 나는 그냥 / 패턴을 사용한다.




3. servlet-context.xml

서블릿이란 간단하게 말하면 자바에서 동적 웹 프로젝트를 개발할 때, 사용자의 요청와 응답을 처리해주는 역할을 한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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.tody.lovely" />
 
</beans:beans>
cs

- ViewResolver 설정 (19~22행)

Controller 에서 ModelAndView로 뷰이름을 설정하는데 이 뷰이름과 매칭되는 것을 찾기 위해 사용된다. Controller에서 처리한 결과를 생성할(보내줄) View를 결정한다. 서블릿 설정이 자동으로 prefix와 suffix를 붙이는 역할을 한다. 이것으로 인해 우리가 일일이 전체경로와 .jsp를 붙이지 않도록 도와준다. 원래는 경로마다 /WEB-INF/views/home.jsp 이런식으로 사용해야하는데, 이걸로 인해서 우리는 간단하게 home만 사용하면 여기서 자동으로 붙여서 보내준다.


- Bean 설정 (24행)

스프링에서 사용하는 Bean을 일일이 xml 선언하지 않고 필요한 것을 어노테이션(Annoation)을 자동으로 인식하게 해준다. 




4. root-context.xml

1
2
3
4
5
6
7
8
<?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>
cs

지금은 아무것도 없지만, 여기에는 스프링 관련 여러가지 설정 파일들을 등록하여 읽을 수 있는 곳이다. 구조를 재정의하면서 context-*.xml로 변경되어 여러 설정 파일들을 등록할 것이다.




5. Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package first.study.spring;
 
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
/**
 * Handles requests for the application home page.
 */
@Controller
public class HomeController {
    
    private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
    
    /**
     * Simply selects the home view to render by returning its name.
     */
    @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";
    }
    
}
cs

- @Controller (17행)

Controller 어노테이션(Annoation)을 설정한 것이다. 웹 클라이언트에서 들어온 요청을 해당 비즈니스 로직으로 분기시켜주고, 수행 결과의 응답을 해주는 Dispatcher 역할을 한다.

Annotaion 설명 참고 : http://www.nextree.co.kr/p5864/


- @RequestMapping (25행)

웹 클라이언트에서 들어온 요청에 해당하는 비즈니스 로직을 찾아주는 역할을 한다. value에 적힌 게 요청url인데, jsp에서 주는 경로와 다르르면 찾지를 못하기 때문에 오타나 url주소에 주의하자. method에 GET방식인지 POST방식인지 알 수 있다. 대부분 POST 이기에 지워도 무방하다. 


- ModelAndView (34, 36행) Model

34행은 비즈니스 로직을 수행한 결과를 화면에 보내주는 역할이다. serverTime이라는 이름으로 formattedDate를 전송함을 의미한다.

36행은 수행 결과를 어디로 보내줄 지 명시하는 곳이다. home은 home.jsp파일을 의미하고 서블릿 설정에서 자동으로 앞에 "/WEB-INF/views/"를 붙여주고(prefix), 뒤에 ".jsp"를 붙여준다(suffix). ModelAndView는 다양한 형태가 존재한다.




6. jsp (View)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ 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>
cs

- ModelAndView (12행) View

Controller에서 34행에 model.addAttribute("serverTime", formattedDate ); 부분이 있었다. serverTime이라는 이름으로 formattedDate를 전송함(결과값)을 의미했는데, model로 보내진 serverTime이 view에서 ${serverTime} 방식으로 사용됨을 볼 수 있다. ${serverTime} 부분에서 서버에서 넘어온 결과(formattedDate)를 화면에 보여준다.



7. test 디렉토리

삭제해도 된다. 잘 안쓴다. jUnit 테스트로 배우긴 했는데 배우기만 하고 쓰지는 않았다.



8. log4j.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
 
    <!-- Appenders -->
    <appender name="console" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%-5p: %c - %m%n" />
        </layout>
    </appender>
    
    <!-- Application Loggers -->
    <logger name="com.tody.lovely">
        <level value="info"/>
    </logger>
    
    <!-- 3rdparty Loggers -->
     <logger name="org.springframework.core">
        <level value="info" />
    </logger>
    
    <logger name="org.springframework.beans">
        <level value="info" />
    </logger>
    
    <logger name="org.springframework.context">
        <level value="info" />
    </logger>
 
    <logger name="org.springframework.web">
        <level value="info" />
    </logger>
 
    <!-- Root Logger -->
    <root>
        <priority value="info" />
        <appender-ref ref="console" />
    </root>
    
</log4j:configuration>
cs

Log4j는 자바기반의 로깅 유틸리티로 Apache에서 만든 오픈소스 라이브러리라고 한다. 개발을 할 때 콘솔에 내용을 찍어 보고 싶을 때 system.out.println(); 을 사용해 보았을 것이다. 개발할 때 콘솔에 많이 찍어 봤을 것이다. 그게 바로 로그다. 근데 스프링에서는 system.out.println(); 를 안쓴다. 물론 써도 된다. 상관없다. 하지만 그렇게 로그를 출력하게 되면 성능에 큰 영향을 미친다. 물론 여럿이서 쓰는 프로젝트이기도 하고, 여러가지 등등의 이유가 있지만 성능에 크게 영향을 미치기 때문에 성능에 영향을 주지 않는 Log4j를 주로 사용한다. Log4j는 옵션 설정을 통해서 선택적으로 로그를 남기거나 특정 파일에 로그를 생성하는 등 다양한 이점을 가지고 있다. Log4j의 설명은 생략한다.



여기까지가 스프링 MVC 프로젝트의 기본 구조이다. 중간에도 말했지만 프로젝트의 기본 구조를 재정의해서 재정의한 프로젝트로 개발을 진행하는 경우도 있다. 구조를 재정의하는 이유는 잘 모르겠다. 편하게 보이려고 하는걸까? 근데 안해도 상관은 없는 것 같다. 나는 개인 프로젝트를 할 때 구조를 재정의 하지 않고 그냥 썼다. 그리고 context도 기능별로 분리하지 않고 한 곳에 다 넣었다. 살펴보니까 딱 정해진 재정의된 프로젝트의 구조도 없는 것 같다. 그냥 대충 이건 여기에 있다, 이 정도? 그래서 구조를 재정의 할지, 말지 고민된다. 헤헤


도움을 준 블로그 : http://addio3305.tistory.com