[회사일] 화면 레이아웃 잡기

난 화면 코드는 잘 못만든다. 만들긴 만드는데 시간이 너무 오래 걸린다. 그 시간에 자바 코딩을 하는편이 더 효율적이다. 하지만 회사에 프론트 엔드 개발자는 없다. 내가 다 해야된다. 그래서 그 동안 봄싹 개발을 하며 자바스크립트랑 도 좀 친해지고, CSS, HTML 과도 친해지려고 노력했다.

차마 ext-js까진 못했지만 jquery까지는 이제 플러그인 가져다 쓰고 옵션 주면서 쓰는데는 익숙해졌다. 화면 구상을 위해서 제일 먼저 검새해본게 jquery layout이고 젤 상위에 걸려있는 링크가 다음과 같다.
언제나처럼 일단 데모 페이지로 가서 어떻게 동작하는지 살펴본다.
일단 디자인 깔끔하고 레이아웃도 다양하게 만들 수 있어 보인다. 괜히 제일 위에 링크되어 있는게 아니었다.
맘에 드는 예제를 찾았다.
상단은 제목과 로그인 정보
왼쪽은 메뉴
오른쪽은 검색 옵션
하단은 카피라이트 정도?
가운데는 그리드를 띄우고
추가/수정 화면은 별도의 창에 띄우기로 결정했다.
텍스트를 채워넣고 CSS로 스타일을 조금 넣었다.

[회사일] DAO 테스트 만들기

이전 코드에서는 뭐 테스트 해보고 싶을 만한게 없었다. 사실 하두 자주 써먹었고 거의 API를 그대로 쓴 코드라서 테스트 하지 않아도 확신이 서는 코드가 대부분이었다. 그 중에서도 굳이 가장 불안한 코드를 꼽으라면 DAO 코드가 되겠다. 나머진 그냥 단순 위임이라 뭐 의심할께 없다.

DAO 코드를 보자.
    public List<Code> list() {
        return getCriteriaOf(Code.class).list();
    }
흠.. 테스트 할 맛이 안난다.
   private Session getSession() {
        return sessionFactory.getCurrentSession();
    }
    private Criteria getCriteriaOf(Class clazz){
        return getSession().createCriteria(clazz);
    }
이런 private 메서드를 쓰고 있지만 전혀 테스트 할만한 뭐시기가 감지되지 않는다.
list 사이즈를 구하는 메서드를 만들어 보기로 했다. 페이징을 구현할텐데 거기서 분명히 전체 목록 크기를 구하는 쿼리가 필요할 것이기 떄문이다.
CodeDao 인터페이스에 코드를 추가하자.
    int totalSize();
다음은 이것을 구현한다.
    public int totalSize() {
        return (Integer)getCriteriaOf(Code.class)
            .setProjection(Projections.count(“id”))
            .uniqueResult();
    }
오.. 제법 낯선 코드를 두 중 정도 코딩했다. 왠지 쬐끔 불안하다. 쬐금.. API 학습 차원에서 테스트를 해봐야겠단 생각이 든다. 좋아 결심했어.
소스 코드와 동일한 패키지에 CodeDaoImpleTest라는 테스트를 만든다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“/testContext.xml“)
@Transactional
public class CodeDaoImplTest extends DBUnitSupport {
    @Autowired CodeDao codeDao;
    @Test
    public void testTotalSize() throws Exception {
        insertXmlData(“testData.xml“);
        assertThat(codeDao.totalSize(), is(1));
    }
}
여기서 주목할 것은 굵게 표시한 부분이다. 먼저 테스트에서 사용할 DB가 운영 DB와 같으면 테스트 데이터를 넣고 확인할 때 문제가 될 수 있으니 전혀 다른 DB 설정을 보도록 테스트용 애플리케이션 컨텍스트와 데이터베이스 프로퍼티 파일을 만든다.
    <context:property-placeholder location=”classpath*:test.database.properties”/>
이 부분 빼곤 나머진 같다.
database.password=
database.username=sa
database.url=jdbc:hsqldb:mem:ㅇㅇㅇ
database.driverClassName=org.hsqldb.jdbcDriver
hibernate.dialect=org.hibernate.dialect.HSQLDialect
다음은 DBUnit을 사용하기 쉽게 해주는 DBUnitSupport 클래스다. DataSource를 필요로 하기 때문에 자동 주입 받도록 설정한다.
public class DBUnitSupport {
enum DataType {EXCEL, FLATXML}
@Autowired
private DataSource dataSource;
protected void cleanInsertXmlData(String fileSource) throws Exception {
insertData(fileSource, DataType.FLATXML, DatabaseOperation.CLEAN_INSERT);
}
protected void cleanInsertXlsData(String fileSource) throws Exception {
insertData(fileSource, DataType.EXCEL, DatabaseOperation.CLEAN_INSERT);
}
private void insertData(String fileSource, DataType type, DatabaseOperation operation) throws Exception {
InputStream sourceStream = new ClassPathResource(fileSource, getClass()).getInputStream();
IDataSet dataset = null;
if (type == DataType.EXCEL) {
dataset = new XlsDataSet(sourceStream);
}
else if (type == DataType.FLATXML) {
dataset = new FlatXmlDataSet(sourceStream);
}
operation.execute(
new DatabaseConnection(DataSourceUtils.getConnection(dataSource)), dataset);
}
protected void insertXmlData(String fileSource) throws Exception {
insertData(fileSource, DataType.FLATXML, DatabaseOperation.INSERT);
}
protected void insertXlsData(String fileSource) throws Exception {
insertData(fileSource, DataType.EXCEL, DatabaseOperation.INSERT);
}
}
DataSource가 필요하기 때문에 
나머지 코드에 대한 설명은 생략~
<dataset>
<code id=”1″ name=”블랙” code=”BLK” />
</dataset>
테스트는 성공한다.
자 이제 DaoTest라는 클래스를 만들어서 설정을 옮겨보자.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(“/testContext.xml”)
@Transactional
public class DaoTest extends DBUnitSupport {
}
참고로 이 클래스를 src 밑에서 만들었다면 컴파일 에러가 난다.
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>org.springframework.test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
여기서 스코프를 test라고 했기 때문이다. 지워준다. 프레임워크성 코드가 생기기 시작하기 때문에 패키징을 조금 신경써서 나눠둔다.
public class CodeDaoImplTest extends DaoTest {
    @Autowired CodeDao codeDao;
    @Test
    public void testTotalSize() throws Exception {
        insertXmlData(“testData.xml”);
        assertThat(codeDao.totalSize(), is(1));
    }
}
자. CodeDaoImplTest가 깔끔해졌다. 앞으로 DAO 테스트를 할 땐 이렇게 DaoTest만 확장해서 만들면 되겠다.

[회사일] 초간단 계층형 아키텍처 만들기

Code 라는 도메인이 있다. Item에서 사용할 색, 사이즈, 제품 성별 등을 이 ‘코드’라는 걸로 관리할 생각인데 그걸 대편하는 도메인이 Code다.

1. 도메인 클래스 만들기
@Entity
public class Code {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    @Column(length = 50)
    private String name;
    @Column(length = 50)
    private String code;
    @Column
    @Type(type = “text”)
    private String descr;
}
2. JPA 애노테이션을 사용해서 매핑 정보를 입력했다. CodeDao 인터페이스를 만든다.
public interface CodeDao {
    List<Code> list();
}
3. 구현체를 만든다.
@Repository
public class CodeDaoImpl implements CodeDao {
    @Autowired SessionFactory sessionFactory;
    public List<Code> list() {
        return getCriteriaOf(Code.class).list();
    }
}
4. CodeService 인터페이스를 만든다.
public interface CodeService {
    List<Code> list();
}
5. 구현체를 만든다.
@Service
@Transactional
public class CodeServiceImpl implements CodeService{
    @Autowired CodeDao codeDao;
    public List<Code> list() {
        return codeDao.list();
    }
}
6. CodeController를 만든다.
@Controller
@RequestMapping(“/base/code/*”)
public class CodeController {
    @Autowired CodeService codeService;
    @RequestMapping
    public void list() {
    }
}
7. web/WEB-INF/views/base/code/list.jsp 파일을 만든다.
생략
그리고 화면에서 확인한다.
아주 지겹게 써먹고 있는 아키텍처인데 사실 아직도 잘 못하고 있다. 
코드 제위치 잡아 주는게 쉬운 일이 아니다.
이번엔 잘 해보자.

[회사일] 프로젝트 세팅

본격적인 개발을 시작하기 전에 할일이 너무 많다. 그래서 처음엔 스프링 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 파일을 만들고 끝!
드디어 코딩을 시작할 수 있겠다.

[회사일] 간단한 재고 조사 시스템 개발

현재 재고조사는 엑셀로 하고 있다. 엑셀에 이미지들을 잔뜩 붙여놓고 쉬트마다 또 복사해서 이미지를 잔뜩 붙여서 현재 창고에 총 수량이 몇개 남았는지 매일 매일 기록하고 있다. (내가 하고 있는게 아니라 이곳 직원들이 하고 있다.) 엑셀 용량이 50M에 달한다고 한다. 엑셀 이미지를 쉬트 한곳에만 올리고 나머진 그 링크 타고 보여주도록 하면 용량좀 줄일 수 있을 것 같은데 그렇게 되나 안되나 모르겠다. 아마도 되겠지.. 그러나 아직은 손대고 싶지 않다. 나중에 기분 좋을 때 해줘야겠다.
대표님이 원하는건 매일 창고에서 나가고 들어온 수량을 확인하고 현재 창고에 어떤 제품이 몇개나 남아있는지 알고 싶다고 하신다. 매일 저 엑셀파일로 결제를 하지만 엑셀 파일로는 위와 같은 상황이기 때문에 하루치 데이터만 유지하기도 버거운 상태다. 판매량 추이나 재고의 변화를 보려면 위해서 시스템을 필요로 하신다.
하지만 현업에 종사하시는 분들의 업무도 역시 엑셀 파일 작업만 하기도 벅찬상태다. 여기서 이 시스템에 정보를 입력해달라는 요구를 하면 나한테 짜증 낼 판국이다. 내가 시킨일도 아닌데 나한테 불평을 토로해봤자… 그래도 난 친절한 개발자니까 일단은 지금 쓰고 있는 엑셀 파일만 업로드하면 거기서 정보를 친절하게 읽어다가 DB에 넣어주도록 만들어야겠다. 그 다음엔 차차 이 시스템에 재고 정보를 입력하는데 익숙해지기를 바래야겠다.
이 시스템에서 가장 중요한 도메인은 Item 과 Inventory가 될 것 같다. 
흠냐 거럼 어디 일을 시작해볼까..