[회사일] 프로젝트 세팅
본격적인 개발을 시작하기 전에 할일이 너무 많다. 그래서 처음엔 스프링 Roo로 개발할까 생각도 했다. 10분만에 내가 원하는 모든 세팅을 가지고 CRUD 애플리케이션을 돌릴 수 있었다. 하지만 디자인도 맘에 안들고 스캐폴딩 코드를 빼면 내가 뭘 어떻게 해야할지 감이 잡히지 않았다. 책이라도 있었으면 보면서 했을텐데 책은 아직 없고 레퍼런스도 빈약했다. 포기했다. 그냥 맨땅에서 다시 다 만들기로 마음 먹었다. 처음부터 그렇게 생각했으면 조금 더 빨리 시작할 수 있었을 것이다.
프로젝트 세팅 작업은 STS에서 했다. 아직 인텔리J에 완전히 익숙하지 못해서 그런지 초기 프로젝트 세팅은 왠지 이클립스가 편하다.
1. 다이나믹 웹 프로젝트 만들기
중간에 웹 컨텐츠 폴더를 web으로 바꿔줬다.
2. pom.xml 만들기
STS에는 m2c 플러긴이 기본으로 깔려있다.
프로젝트를 우클릭하고 pom.xml 파일 생성을 했다.
이제부터 메이븐과의 싸움이다.
(이전에는 뭐가 뭔 설명인지 몰라서 괜히 어려워 보였었는데 이제는 머 그냥 귀찮을 따름이다.)
3. 프로젝트 구조 설정하기.
내가 원하는 프로젝트 구조는 간단하다. 일반적인 자바 프로젝트 같이 보이는 메이븐 프로젝트를 원한다. 즉 프로젝트 최상위에는 src, test, web만 있으며 src와 test 밑에는 바로 패키지가 시작된다. 일반적인 메이븐 프로젝트 구조가 아니다. 난 이게 더 편하다. 메이븐 플젝 구조라면 소스 패키지 시작하기 전에 3중 폴더를 지나야한다. 리소스와 자바 파일도 구분되어 있다. 그림 그리기는 귀찮아서 패스.
<build>
<sourceDirectory>
${project.basedir}/src
</sourceDirectory>
<testSourceDirectory>
${project.basedir}/test
</testSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/test</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
</build>
이렇게 해서 src에 메이븐 자바 소스 파일과 리소스 파일을 넣을 거구 test에 메이븐 자바 테스트 파일과 리소스 파일을 넣겠다고 설정해줬다.
4. 메이븐 플러그인 설정
설명 생략. 역시 귀찮음;;
4-1. maven-war-plugin 설정
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warSourceDirectory>web</warSourceDirectory>
</configuration>
</plugin>
4-2. maven-compiler-plugin 설정
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
4-3. maven-resources-plugin 설정
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
4-4. maven-eclipse-plugin 설정
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.4</version>
<configuration>
<additionalProjectnatures>
<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
</additionalProjectnatures>
<additionalBuildcommands>
<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
</additionalBuildcommands>
<downloadSources>${downSource}</downloadSources>
<downloadJavadocs>${downJavadoc}</downloadJavadocs>
<wtpversion>1.5</wtpversion>
<wtpContextName>${wtpContextName}</wtpContextName>
</configuration>
</plugin>
4-5. maven-clean-plugin 설정
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.4</version>
<configuration>
<filesets>
<fileset>
<directory>web/WEB-INF/lib</directory>
</fileset>
<fileset>
<directory>web/WEB-INF/classes</directory>
</fileset>
</filesets>
</configuration>
</plugin>
4-6. 플러그인 저장소 설정
<pluginRepositories>
<pluginRepository>
<id>codehaus snapshot repository</id>
<url>http://snapshots.repository.codehaus.org/</url>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
5. 프로젝트 의존성 설정
<properties>
<spring.version>3.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<!-- Spring depedencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.instrument</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web.servlet</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.context.support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.transaction</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Hibernate dependecies -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate</artifactId>
<version>3.3.2.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate.annotations</artifactId>
<version>3.4.0.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate.annotations.common</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>com.springsource.org.hibernate.ejb</artifactId>
<version>3.4.0.GA</version>
</dependency>
<!-- Caching dependecies -->
<dependency>
<groupId>net.sourceforge.ehcache</groupId>
<artifactId>com.springsource.net.sf.ehcache</artifactId>
<version>1.6.2</version>
</dependency>
<!-- DBMS dependecies -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>com.springsource.javax.persistence</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>com.springsource.javax.transaction</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>8.2-507.jdbc3</version>
</dependency>
<!-- Logging depedencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.5.10</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>com.springsource.ch.qos.logback.classic</artifactId>
<version>0.9.9</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.junit</groupId>
<artifactId>com.springsource.org.junit</artifactId>
<version>4.8.1</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>com.springsource.org.hamcrest</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>com.springsource.org.mockito</artifactId>
<version>1.8.4</version>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>com.springsource.org.dbunit</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>com.springsource.org.hsqldb</artifactId>
<version>1.8.0.9</version>
<scope>test</scope>
</dependency>
<!-- Web dependencies -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet.jsp.jstl</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet</artifactId>
<version>2.5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>com.springsource.javax.servlet.jsp</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.tuckey</groupId>
<artifactId>com.springsource.org.tuckey.web.filters.urlrewrite</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>com.springsource.org.apache.commons.fileupload</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>com.springsource.org.codehaus.jackson</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>com.springsource.org.codehaus.jackson.mapper</artifactId>
<version>1.4.2</version>
</dependency>
<!-- AspectJ dependencies -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.runtime</artifactId>
<version>1.6.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.5.RELEASE</version>
</dependency>
</dependencies>
직접 운영할 수 있거나 라이브러리 설치를 부탁할 수 있는 넥서스 같은 메이븐 저장소를 설정해주는게 좋은데 난 봄싹 저장소를 설정했다.
<repositories>
<repository>
<id>springsprout nexus</id>
<name>SpringSprout Nexus public</name>
<url>http://dev.springsprout.org/nexus/content/groups/public</url>
</repository>
<repository>
<id>springsprout snapshots</id>
<name>SpringSprout Nexus public snapshots</name>
<url>http://dev.springsprout.org/nexus/content/groups/public-snapshots/
</url>
</repository>
</repositories>
6. web.xml 설정하기
이제 메이븐은 끝났다. 이제부턴 웹 프로젝트 설정과의 싸움이다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>ㅇㅇㅇ</display-name>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>ㅇㅇㅇ.root</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext*.xml</param-value>
</context-param>
<filter>
<filter-name>CharacterEncodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter>
<filter-name>Spring OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Spring OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>smdis2</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>smdis2</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>10</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/app/uncaughtException</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/app/resourceNotFound</location>
</error-page>
</web-app>
스프링 Roo에서 만들어주는 web.xml을 배꼈다.
이 중에서 일부 필터는 사용할 ORM에 따라 바꿔야 하며, 스프링 웹 플로우도 필요하다. 아직 웹 플로우 의존성은 추가하지 않았는데 나중에 넣어도 상관없으니 그냥 쓴다.
7. URL Rewriter 설정
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite default-match-type="wildcard">
<rule>
<from>/resources/**</from>
<to last="true">/resources/$1</to>
</rule>
<rule>
<from>/static/WEB-INF/**</from>
<set type="status">403</set>
<to last="true">/static/WEB-INF/$1</to>
</rule>
<rule>
<from>/static/**</from>
<to last="true">/$1</to>
</rule>
<rule>
<from>/</from>
<to last="true">/app/index</to>
</rule>
<rule>
<from>/app/**</from>
<to last="true">/app/$1</to>
</rule>
<rule>
<from>/**</from>
<to>/app/$1</to>
</rule>
<outbound-rule>
<from>/app/**</from>
<to>/$1</to>
</outbound-rule>
</urlrewrite>
web.xml과 같은 폴더에 넣어준다. 스프링 Roo를 배꼈다.
8. 스프링 디스패처 서블릿 설정 파일 만들기
web.xml 설정에 보면 이 파일 위치가 명시되어 있는 걸 볼 수 있다. 스프링 Roo에서 사용하는 기본 위치다. 그냥 똑같이 쓰도록 하자. web.xml에서 위치를 명시하지 않으면 web.xml과 같은 위치에 디스패처 서블릿의 이름-servlet.xml 파일로 만들어 줘야한다.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p"
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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<context:component-scan base-package="ㅇㅇㅇ"
use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation" />
</context:component-scan>
<mvc:annotation-driven />
<mvc:view-controller path="/login" />
<mvc:view-controller path="/index" />
<mvc:view-controller path="/sample" />
<mvc:view-controller path="/uncaughtException" />
<mvc:view-controller path="/resourceNotFound" />
<mvc:view-controller path="/dataAccessFailure" />
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
p:defaultErrorView="uncaughtException">
<property name="exceptionMappings">
<props>
<prop key=".DataAccessException">dataAccessFailure</prop>
<prop key=".NoSuchRequestHandlingMethodException">resourceNotFound</prop>
<prop key=".TypeMismatchException">resourceNotFound</prop>
<prop key=".MissingServletRequestParameterException">resourceNotFound</prop>
</props>
</property>
</bean>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml" />
<entry key="html" value="text/html" />
<entry key="json" value="application/json" />
</map>
</property>
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>
<bean
class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
id="multipartResolver" />
</beans>
스프링 Roo에서 배낀 코드에 몇 가지 설정을 변경하고 컨텐츠 네고 설정을 추가했다. 컨텐츠 네고 설정은 토비님 책에 나오는 코드다. 머시냐면.. 웹 브라우저 주소창에 /code/list를 입력하면 JSTL 뷰를 보여주고 Ajax로 JSON 형태의 응답을 달라고 요청하면 JSON 뷰를 받을 수 있다. 정말 편하지 아니한가..
하지만 아마도 프로젝트 진행하는 도중 이 파일은 자주 손보게 될 것 같다. mvc:annotation에서 기본으로 등록해주는 핸들러 매핑이나 어댑터를 확장하려면 어쩔 수 없다.
9. applicationContext.xml 만들기
src 폴더에 만들어준다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<context:property-placeholder location="classpath*:*.properties"/>
<context:component-scan base-package="smdis">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<property name="driverClass" value="${database.driverClassName}"/>
<property name="jdbcUrl" value="${database.url}"/>
<property name="user" value="${database.username}"/>
<property name="password" value="${database.password}"/>
</bean>
<tx:annotation-driven/>
<!-- ============================================================= -->
<!-- Hibernate -->
<!-- ============================================================= -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="smdis" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
</beans>
네임스페이스 설정 빼면 별거 없다. 컴포넌트 스캔이 없던 시절은 생각하기도 싫다.
10. database.properties 파일을 만든다.
#Updated at Tue May 25 14:01:35 KST 2010
#Tue May 25 14:01:35 KST 2010
database.password=ㅇㅇㅇ
database.username=ㅇㅇㅇ
database.url=jdbc:postgresql://localhost/ㅇㅇㅇ
database.driverClassName=org.postgresql.Driver
hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
DB는 PostgereSQL을 사용하기로 결정했다.
11. index.jsp 만들기
대충 Hello 찍는 JSP 파일을 만들고 끝!
드디어 코딩을 시작할 수 있겠다.