상당히 오랜만에 글을 씁니다.
그동안 개인적으로 워낙 많은 일들이 있었고, 잠시 쉬는시간도 없어서 글을 아예 쓰지 못했네요.
그동안 제 글을 읽어주시고, 기다려주신 모든분들께 감사하다는 말씀을 드립니다.
이번 글에서는 본격적인 스프링 MVC 구조 개발을 시작하도록 하겠습니다.
------------------------------------------------------------------------------------
웹개발을 하다보면 MVC 패턴이란 말을 듣게 된다.
MVC란 Model-View-Controller의 약자로, 사용자 인터페이스와 비지니스 로직을 분리하여 웹 개발을 하는것을 가장 큰 장점으로 한다.
MVC 패턴도 MVC 모델 1과 MVC 모델 2로 나뉘어져 있는데, 요즘에는 MVC라고 하면 당연히 MVC 모델 2를 의미한다. 따라서 여기서는 MVC 모델 2 (이하 MVC)를 기준으로 진행한다.
Model : 모델은 애플리케이션의 정보, 즉 데이터를 나타낸다.
View : 뷰는 사용자에게 보여주는 인터페이스, 즉 화면을 이야기한다. 자바 웹 애플리케이션에서는 JSP를 의미한다.
Controller : 컨트롤러는 비지니스 로직과 모델의 상호동작의 조정 역할을 한다. MVC2에서는 서블릿이 흐름을 제어하는 컨트롤러 역할을 수행한다.
사진 출처 : http://blog.daum.net/gunsu0j/165
MVC 패턴에 대해서 장황하게 설명해도 쉽게 이해하기 어려운 관계로, 간단히 개념만 이야기하고 넘어간다. 실제 소스코드를 통해서 MVC 패턴에 대한 경험을 하는것이 훨씬 이해하는데 도움이 된다.
앞으로 하나씩 추가를 해나가면서 실제로 이러한 MVC 패턴이 어떻게 적용되는지를 보도록 하자.
1. Spring 라이브러리 추가
이전 글 (http://addio3305.tistory.com/36 ) 에서 pom.xml에 대해서 간단히 설명한 적이 있었다. pom.xml는 간단히 말해서 프로젝트에서 필요한 모든 라이브러리들을 관리할 수 있도록 구성되어 있다. 예전에는 어떠한 라이브러리를 사용하려고 하면, 라이브러리를 다운받아서 특정 폴더에 복사하여 사용했었다. 라이브러리가 파일 하나로 구성되어 있는 경우라면 몰라도, 하나의 라이브러리에 다양한 파일로 구성되어 있다. 스프링도 마찬가지로, 스프링 프레임워크에는 상당히 많은 파일이 존재한다.
다음은 GitHub에 올라와 있는 최신 스프링프레임워크 소스이다. 밑에서 보는것과 같이 수없이 많은 패키지가 존재함을 볼 수 있다.
메이븐에서는 <dependency></dependency>라는 태그를 통해서 각 라이브러리를 추가할 수 있다.
여기서 추가해야할 라이브러리가 굉장히 많은 관계로 각 라이브러리에 대한 자세한 설명은 생략하고 바로 pom.xml만 먼저 확인하고, 복사해서 붙여넣는것을 권장한다. 소스를 열어보면 여태까지와는 다르게 상당히 길기 때문에, 먼저 마음의 준비를 해야한다.
pom.xml 소스보기 접기
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.company</groupId>
<artifactId>first</artifactId>
<name>first</name>
<packaging>war</packaging>
<version>1.0.0-BUILD-SNAPSHOT</version>
<properties>
<java-version>1.7</java-version>
<org.springframework-version>3.2.4.RELEASE</org.springframework-version>
<org.aspectj-version>1.7.3</org.aspectj-version>
<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>
<repositories>
<repository>
<id>mvn2</id>
<url>http://repo1.maven.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>egovframe</id>
<url>http://www.egovframe.go.kr/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>oracle</id>
<name>ORACLE JDBC Repository</name>
<url>http://mesir.googlecode.com/svn/trunk/mavenrepo</url>
</repository>
</repositories>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
<exclusions>
<!-- Exclude Commons Logging in favor of SLF4j -->
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- AOP Alliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.0</version>
</dependency>
<!-- DBCP -->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-dbcp</artifactId>
<scope>provided</scope>
<version>7.0.53</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
<!-- Oracle -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.4.0</version>
</dependency>
<!-- AspectJ -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<!-- Web -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
<version>2.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.3alpha-8</version>
</dependency>
<dependency>
<groupId>org.lazyluke</groupId>
<artifactId>log4jdbc-remix</artifactId>
<version>0.2.7</version>
</dependency>
<!-- MappingJacksonJsonView -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- MultipartHttpServletRequset -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
<!-- Apache Codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!-- EgovProperty -->
<dependency>
<groupId>egovframework.rte</groupId>
<artifactId>egovframework.rte.fdl.property</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
<exclusion>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jdmk</groupId>
<artifactId>jmxtools</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.jmx</groupId>
<artifactId>jmxri</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- @Inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<directory>${basedir}/target</directory>
<finalName>first</finalName>
<plugins>
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.9</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
<encoding>UTF-8</encoding>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<configuration>
<mainClass>org.test.int1.Main</mainClass>
</configuration>
</plugin>
<!-- JavaDoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.5</version>
</plugin>
</plugins>
</build>
</project>
접기
pom.xml을 열어보았다면 상당히 긴 것에 놀랐을 것이라고 생각한다.
사실 지금 여기에 있는 것들이 지금 당장 필요한 라이브러리들은 아니다. 그렇지만 앞으로 프로젝트를 하는데 있어서 필요한 대부분의 라이브러리들을 미리 추가해놓았다. (실제로 필자가 진행하는 프로젝트에서도 이정도는 기본으로 들어간다. 여기서 각 프로젝트의 특성에 맞는 라이브러리들이 추가된다.)
원래는 각 기능을 하나씩 만들면서, pom.xml에도 필요한 라이브러리들을 추가해야 하지만, 그 일은 앞으로 설명해나가려고 한다.(정말로??????)
일단 pom.xml을 간단히 설명하려 한다.
첫번째로 <properties></properties> 태그는 변수의 개념으로 생각하면 된다. 우리가 프로그래밍을 하면서 어떠한 값은 상수 (java에서 final static 으로 선언)로 선언하는 경우가 많은데, 이는 그 변수는 여기저기서 많이 사용되는 변수이기 때문이다. 이러한 변수를 직접 소스 여기저기에 일반적인 값으로 써놓으면, 그 값을 변경해야 할 경우, 모든 소스에서 일일이 변경해야하는 경우가 발생한다. 이럴때, 상수로 선언해놓고, 그 하나만 변경하면 쉽게 값을 변경하면서도 다른 소스는 수정할 필요가 없다.
여기서도 마찬가지로 많이 사용되는 값은 변수로 지정하여, 추후 수정이 용이하도록 되어있다.
잠시 살펴보면 <org.springframework-version>3.2.4.RELEASE</org.springframework-version> 라는것을 볼 수 있는데, 이 글을 시작할때 Spring 3.2.4 버전을 사용하기 때문에, 이 값을 이렇게 선언하였다. 만약 스프링 버전을 변경하고 싶으면 이 변수만 바꿔주면 된다. (참고로 이 글을 작성중인 현재, 스프링의 최신 버전은 4.1.4 버전이다. 4.2.0은 아직 정식버전이 아니어서 이야기 하지 않는다.)
다음은 <repositories></repositories> 태그다.
이는 실제 라이브러리를 다운받을 저장소를 의미한다. 보통은 따로 설정할 필요가 없다. 하지만 프로젝트를 진행하다보면 인터넷에 연결할 수 없는 프로젝트도 상당히 많은데, 이럴때 내부 저장소를 만들어놓고, 개발자들은 내부저장소에서 라이브러리를 다운받도록 되어있다.
여기서는 인터넷에서 라이브러리를 받으면서, 추가로 전자정부 프레임워크의 기능 중 하나를 사용하기 위해서 전자정부 프레임워크 저장소도 같이 추가했다.
그 다음으로 <dependencies><dependencies> 태그는 실제 라이브러리를 지정한다.
라이브러리는 다음과 같은 형태로 구성된다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework-version}</version>
</dependency>
여기서 <version>태그 안에 아까 우리가 위에서 지정한 프로퍼티를 사용한 것을 볼 수 있다. 라이브러리를 추가하는것은 별도 포스팅을 통해서 좀 더 자세히 설명할 예정이다.
여기서는 pom.xml에 대해서 간단히 설명했는데, 좀 더 자세히 알고싶은 사람들은 인터넷에서 메이븐에 관련된 글을 읽어보는것을 추천한다.
2. 설정파일 변경 (web.xml, action-servlet.xml 등등)
이제 하나씩 설정을 하도록 하자.
첫번째로 web.xml 파일을 열어보자.
기존에 web.xml 파일에 대해서 간단히 설명하고, 수정했었다. (http://addio3305.tistory.com/39 )
여기서 몇가지 설정을 덧붙이려고 한다.
1. UTF-8 설정
다음을 web.xml에 추가한다.
<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-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
요즘에는 기본적으로 UTF-8로 인코딩한다. 뭐 한글을 아예 안쓸거면 안해줘도 상관없지만... 아마 그럴 사람은 없는걸로 생각한다.
2. 그 외 설정파일 경로 설정
기존 설정파일을 보면 <servlet>설정중에 <param-value>라는것이 있었다.
기존에는 servlet 설정파일이 action-servlet.xml 하나만 있었는데, 이제 또 추가되기 때문에, 확장성을 생각하여 특정 폴더에 있는 설정파일을 모두 읽어오는 방식으로 변경한다.
<param-value> 태그를 다음과 같이 수정한다.
<param-value>/WEB-INF/config/*-servlet.xml </param-value>
이는 /WEB-INF/config/ 폴더안에 있는 -servlet.xml로 끝나는 모든 파일을 읽어오는것을 의미한다. 따라서 앞으로 설정파일을 추가할때는 XXXXXX-servlet.xml로 만들게 되면, 자동적으로 설정파일을 읽어들인다.
다음으로는 Spring 설정파일을 추가한다. 기존에 <context-param> 태그의 <param-value>태그안에는 아마 아무것도 작성되지 않았을 것이다. 이제 본격적인 스프링 설정파일을 읽어오기 위해서 다음과 같이 바꾼다.
<param-value>classpath*:config/spring/context-*.xml</param-value>
이는 앞에서 설명한것과 비슷하게 context-로 시작하는 모든 .xml 을 읽어오는것을 뜻한다.
여기서 지금 이렇게 추가하면 context-XXXXXX.xml 파일이 없기 때문에 에러가 날 것이다. 일단 작성만 해 놓고 주석처리하도록 한다.
여기까지 작성해서 완성된 web.xml은 다음과 같다.
web.xml 보기 접기
<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">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<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-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/*-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:config/spring/context-*.xml</param-value>
</context-param>
</web-app>
접기
------------------------------------------------------------------------------------
오랜만에 작성한 이번글은 좀 짧습니다. 시간적인 여유도 부족하고, 이번글부터 한동안은 설정에 관련된 부분이라서 복잡하기도 하기 때문에 좀 짧게 작성하였습니다.
앞으로 몇페이지에 걸쳐서 지루한 스프링 설정에 대해서 이야기를 하게 됩니다. 아마 처음에는 굉장히 보기 힘들고, 어려운 내용이 될것이라고 생각합니다. 많이 지루하기도 할거구요. 당장 눈으로 결과를 보기도 쉽지 않습니다.
그렇지만 제가 이런식으로 글을 써가는 것은, 인터넷에서 그냥 흔히 볼수있는 글이 아닌, 실제 프로젝트를 진행한 경험을 바탕으로 "실제 프로젝트에서는 이런식으로도 하는구나" 라는걸 보여주기 위함입니다. 물론 모든 프로젝트가 이렇게 진행되지 않고, 또 제가 작성하는 것보다 훨씬 높은 수준의 스프링 아키텍쳐가 잡혀있는 경우도 많이 있습니다. 그런 경험을 하신 분들이 보기에는 부족함이 많겠지만, 일단 제가 아는 한도내에서 최대한 편하게 개발을 하고, 이론적으로만 보고 배운 내용을 직접 실습해보는 기회가 되었으면 하는 바람입니다.
앞으로는 좀 더 자주 글을 쓰도록 하겠습니다~