본문 바로가기
Homo Faber/Maven Definitive Guide

5장. 간단한 웹 어플리케이션

by javauser 2008. 10. 30.
5.1 소개

이 장에서 Maven Archetype 플러그인으로 간단한 웹 어플리케이션을 생성한다. 이 웹 어플리케이션을 Jetty라고 하는 서블릿 컨테이너에서 실행하고, 몇가지 의존관계를 추가하고, 간단한 서블릿을 작성하고, WAR 파일을 생성할 것이다. 이 장의 끝에서 웹 어플리케이션의 개발을 촉진하기 위해 메이븐 사용을 시작할 수 있을 것이다.

5.1.1. 이장의 예제 다운로딩

이 장의 예제는 Maven Archetype 플러그인으로 생성된다. 에제 소스 코드없이 이장의 개발을 따라서 할 수도 있는 반면에 참조로 사용하기 위해 예제 코드의 복사본을 다운로딩받기를 권한다. 이 장의 예제 프로젝트는 http://www.sonatype.com/book/mvn-examples-1.0.ziphttp://www.sonatype.com/book/mvn-examples-1.0.tar.gz 에서 책의 예제 코드와 같이 다운로드 받을 수 있다. 디렉토리에 이 압축파일을 풀고, ch05/ 디렉토리로 이동한다. ch05/ 디렉토리에서 이 장에서 만든 Maven 프로젝트를 포함하는 simple-webapp/ 라는 디렉토리를 볼 수 있을 것이다. 웹 브라우저에서 에제 코드를 따라서 보기 원한다면, http://www.sonatype.com/book/examples-1.0 로 가서 ch05/ 디렉토리를 클릭하라.

5.2 간단한 웹 어플리케이션 정의

의도적으로 이 장을 기본적인 웹 어플리케이션 (Plain-Old Web Application, POWA), 즉 서블릿과 JSP에 대해서 초점을 맞추도록 한다. Struts 2, Tapestry, Wicket, JSF, 혹은 Waffle 어플리케이션 개발 방법에 대해서 설명하지 않으며, Plexus, Guice, 혹은 Spring 프레임워크와 같은 제어 역행 (IoC) 컨테이너와 통합하는 것에 대해서도 설명하지 않는다. 이 장의 목적은 메이븐이 더도 말고 덜도 말고 웹 어플리케이션을 개발하는데 제공하는 기본적인 기능을 보여주는 것이다. 이 책 이후에 두가지 웹 어플리케이션 하나는 Hibernate, Velocity, Spring 프레임워크를 사용한 것과, 다른 하나는 Plexus를 사용한 것에 대한 개발을 살펴볼 것이다.

5.3 간단한 웹 어플리케이션 생성

웹 어플리케이션 프로젝트를 생성하기 위해서 artifactId와 groupId를 포함하는 mvn archetype:create 를 실행한다. maven-archetype-webapp 으로 archetypeArtifactId 을 지정한다. 이것을 실행하면 적절한 디렉토리 구조와 Maven POM을 생성한다.

~/examples$ mvn archetype:create -DgroupId=org.sonatype.mavenbook.ch05 \
                                  -DartifactId=simple-webapp \
                                  -DpackageName=org.sonatype.mavenbook \
                                  -DarchetypeArtifactId=maven-archetype-webapp
[INFO] [archetype:create]
[INFO] ----------------------------------------------------------
[INFO] Using following parameters for creating Archetype:
           maven-archetype-webapp:RELEASE
[INFO] ----------------------------------------------------------
[INFO] Parameter: groupId, Value: org.sonatype.mavenbook.ch05
[INFO] Parameter: packageName, Value: org.sonatype.mavenbook
[INFO] Parameter: basedir, Value: ~/examples
[INFO] Parameter: package, Value: org.sonatype.mavenbook
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: artifactId, Value: simple-webapp
[INFO] ********************* End of debug info from resources from
           generated POM *******
[INFO] Archetype created in dir: ~/examples/simple-webapp

일단 Maven Archetype 플러그인이 프로젝트를 생성하면, simple-web 디렉토리로 이동해서 pom.xml을 살펴보자. 예제 5-1에 보이는 XML 문서를 보게될 것이다.

예제 5-1. simple-web 프로젝트에 대한 초기 POM
<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>org.sonatype.mavenbook.ch05</groupId>
  <artifactId>simple-webapp</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>simple-webapp Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

<packaging> 요소는 war 값을 포함하고 있음을 유의하라. 이 패키징 유형은 WAR 파일에 웹 어플리케이션 내용을 만들기 위해 메이븐을 설정하는 것이다. war 패키징을 가지는 프로젝트는 target/ 디렉토리에 WAR 파일을 생성하게 된다. 이 파일에 대한 기본 이름은 ${artifactId}-${version}.war 이다. 이 프로젝트에서 기본 WAR 는 target/simple-webapp-1.0-SNAPSHOT.war에 생성될 것이다. simple-webapp 프로젝트에서 해당 프로젝트의 빌드 설정의 내부에 있는 <finalName> 요소를 추가함으로써 생성된 WAR 파일의 이름을 수정했다. simple-webapp의 <finalName>을 사용해서 package 단계는 target/simple-webapp.war에 WAR 파일을 만든다.

5.4 Jetty 플러그인 설정

일단 웹 어플리케이션을 컴파일하고, 테스트하고, 패키징했다면, 서블릿 컨테이너에 이를 배포하고 Maven Archetype 플러그인에 의해 생성되었던 index.jsp 를 테스트하기 원할 것이다. 정상적으로, 이는 Jetty나 아파치 톰캣과 같은 것을 다운로드 받고, 배포판을 풀고, 어플리케이션의 WAR파일을 webapps/ 디렉토리에 복사하고, 그 다음에 컨테이너를 실행하게 된다. 이렇게 작업을 수행할 수도 있지만, 메이븐을 사용하면 이러한 작업 필요없다. 대신에 메이븐 내에서 웹 어플리케이션을 실행하기 위해 Maven Jetty 플러그인을 사용할 수 있다. 이렇게 하려면 프로젝트의 pom.xml에 Maven Jetty 플러그인을 설정할 필요가 있다. 프로젝트의 빌드 설정에 예제 5-2에 나타난 것과 같이 plugin 요소를 추가하라.

예제 5-2. Jetty 플러그인 설정
<project>
  [...]
  <build>
    <finalName>simple-webapp</finalName>
    <plugins>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
      </plugins>
    </plugins>
  </build>
  [...]
</project>

일단 프로젝트의 pom.xml에 Maven Jetty 플러그인을 설정했다면, Jetty 서블릿 컨테이너에 웹 어플리케이션을 시작하기 위해 Jetty 플러그인의 run goal을 호출한다. 다음과 같이 mvn jetty:run 을 실행한다.

~/examples$ mvn jetty:run
...
[INFO] [jetty:run]
[INFO] Configuring Jetty for project: simple-webapp Maven Webapp
[INFO] Webapp source directory = \
       /Users/tobrien/svnw/sonatype/examples/simple-webapp/src/
       main/webapp
[INFO] web.xml file = \
       /Users/tobrien/svnw/sonatype/examples/simple-webapp/src/
       main/webapp/WEB-INF/web.xml
[INFO] Classes = /Users/tobrien/svnw/sonatype/examples/simple-webapp/
       target/classes
2007-11-17 22:11:50.532::INFO:  Logging to STDERR via
       org.mortbay.log.StdErrLog
[INFO] Context path = /simple-webapp
[INFO] Tmp directory =  determined at runtime
[INFO] Web defaults = org/mortbay/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] Webapp directory = \
       /Users/tobrien/svnw/sonatype/examples/simple-webapp/src/
       main/webapp
[INFO] Starting jetty 6.1.6rc1 ...
2007-11-17 22:11:50.673::INFO:  jetty-6.1.6rc1
2007-11-17 22:11:50.846::INFO:  No Transaction manager found - if\
       your webapp requires one, please configure one.
2007-11-17 22:11:51.057::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

메이븐이 Jetty 서블릿 컨테이너를 시작한 후에, 웹 브라우저에서 http://localhost:8080/simple-webapp/ URL을 로딩한다. Archetype에 의해 생성된 간단한 index.jsp 는 단순하다. “Hello World!”라는 글자를 가진 2단계의 헤딩을 포함한다. 메이븐은 src/main/webapp 에 저장된 웹 어플리케이션의 문서 루트로 가정한다. 이 디렉토리에서 예제 5-3에 보여지는 index.jsp 파일을 찾을 수 있을 것이다.

예제 5-3. src/main/webapp/index.jsp 의 내용
<html>
  <body>
    <h2>Hello World!</h2>
  </body>
</html>

src/main/webapp/WEB-INF 에서 예제 5-4에 보여지는 web.xml 에서 가장 작은 웹 어플리케이션 디스크립터를 볼 수 있다.

예제 5-4. src/main/webapp/WEB-INF/web.xml의 내용
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
</web-app>


5.5 간단한 서블릿 추가

하나의 JSP 페이지와 아무런 서블릿 설정이 없는 웹 어플리케이션은 사용되지 않는다. 이 어플리케이션에 간단한 서블릿을 추가하고 이러한 추가를 지원하기 위해 pom.xml 과 web.xml 에 몇가지 변화를 주자. 처음에 다음과 같이src/main/java 밑에 org.sonatype.mavenbook.web 라고 하는 새로운 패키지를 추가할 필요가 있다.

$ mkdir -p src/main/java/org/sonatype/mavenbook/web
$ cd src/main/java/org/sonatype/mavenbook/web

이 패키지를 만들었다면, src/main/java/org/sonatype/mavenbook/web 디렉토로 이동해서 예제 5-5에 나타난 코드를 포함하는 SimpleServlet.java 에 SimpleServlet 이라는 클래스를 생성한다.

예제 5-5. SImpleServlet 클래스
package org.sonatype.mavenbook.web;

import java.io.*;
import javax.servlet.*;                                                        
import javax.servlet.http.*;

public class SimpleServlet extends HttpServlet {
    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws ServletException, IOException {
   
        PrintWriter out = response.getWriter();
        out.println( "SimpleServlet Executed" );
        out.flush();
        out.close();
    }
}

SimpleServlet 클래스는 응답의 Writer 에 단순한 메시지를 출력하는 서블릿이다. 이 서블릿을 웹 어플리케이션에 추가하고 요청 경로로 매핑하기 위해서, 프로젝트의 web.xml 파일에 예제 5-6에 보여지는 <servlet> 과 <servlet-mapping> 요소를 추가한다. web.xml 파일은 src/main/webapp/WEB-INF에서 찾을 수 있다.

예제 5-6. simple 서블릿 매핑
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>simple</servlet-name>
    <servlet-class>org.sonatype.mavenbook.web.SimpleServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>simple</servlet-name>
    <url-pattern>/simple</url-pattern>
  </servlet-mapping>
</web-app>

남아있는 것은 이 서블릿을 테스트하는 것이다. 클래스는 src/main/java 에 있으며 web.xml 은 변경되었다. Jetty 플러그인을 실행하기 전에 mvn compile 을 실행해서 프로젝트를 컴파일한다.

~/examples$ mvn compile
...
[INFO] [compiler:compile]
[INFO] Compiling 1 source file to ~/examples/ch05/simple-webapp/target/classes
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[4,0] \
  package javax.servlet does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[5,0] \
  package javax.servlet.http does not exist

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[7,35] \
  cannot find symbol
  symbol: class HttpServlet
  public class SimpleServlet extends HttpServlet {

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[8,22] \
  cannot find symbol
  symbol  : class HttpServletRequest
  location: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[9,22] \
  cannot find symbol
  symbol  : class HttpServletResponse
  location: class org.sonatype.mavenbook.web.SimpleServlet

/src/main/java/org/sonatype/mavenbook/web/SimpleServlet.java:[10,15] \
  cannot find symbol
  symbol  : class ServletException
  location: class org.sonatype.mavenbook.web.SimpleServlet

메이븐 프로젝트가 서블릿 API에 대한 의존관계를 가지고 있지 않기 때문에 컴파일을 실패한다. 다음 절에서 이 프로젝트의 POM에 서블릿 API를 추가할 것이다.

5.6. J2EE 의존관계 추가

서블릿을 작성하려면 프로젝트 의존관계로 서블릿 API를 추가할 필요가 있다. 서블릿 명세는 Sun Microsystems의 http://java.sun.com/products/servlet/download.html 에서 다운로드 받을 수 있는 JAR 파일이다. JAR 파일이 다운로드 받았다면, ~/.m2/repository에 위치한 로컬 Maven 레파지토리에 결과 JAR를 설치할 필요가 있다. 동일한 절차를 통해 Sun Microsystems 의 모든 J2EE API – JNDI, JDBC, 서블릿, JSP, JTA 등 - 들을 반복해야 한다. 이것이 다소 지루하다면, 혼자서 느끼는 감정이 아니다. 다행스럽게 이러함 모든 라이브러리들을 다운로딩받고 수동으로 설치하는 더 간단한 대안인 Apache Geronimo의 독립 오픈 소스 구현이 있다.

수년간 서블릿 명세 JAR를 얻는 유일한 방법은 Sun Microsystems으로부터 직접 다운로드받는 것이었다. Sun 웹 사이트로 가서 클릭을 통한 라이선스 동의를 수행하고, 그 다음에 서블릿 JAR 를 접근할 수 있었다. 이는 Sun 명세 JAR들은 재배포를 위해 허용되는 라이선스 하에서 가능도록 만든 것이 아니기 때문에 모두 필요했다. 수동으로 Sun 산출물들을 다운로드받는 것은 수년간 Maven 프로젝트로부터 서블릿을 작성하거나 JDBC를 사용하는데 필요한 작업이었다. 이러한 작업은 Apache Geronimo 프로젝트가 소스와 바이너리에 대한 무료 재배포를 허용하는 Apache 소프트웨어 라이선스 버전 2.0 하에 엔터프라이즈 명세 JAR들을 배포하는 수많은 엔터프라이즈 명세에 대한 Sun에 인증을 받은 구현제를 만들 수 있을때까지 지루하고 귀찮았었다. 이제 프로그래밍 목적으로 Sun Microsystems로부터 다운받은 서블릿 API JAR와 Apache Geronimo 프로젝트에 의해 구현된 서블릿 API JAR 간의 차이가 거의 없다. 둘 다 Sun Microsystems 로부터 엄격한 TCK(Test Compatibility Kit)를 통과했다.

JSP API나 서블릿 API와 같은 것에 의존관계를 추가하는 것은 이제 매우 간단하며, 웹 사이트에서 JAR 파일을 수동으로 다운로드 받을 필요없이 로컬 레파지토리로 설치할 수 있다. 알아야 할 것은 어디에서 찾으며, 적합한 Apache Geronimo 구현체를 참고하는데 사용하는 groupId, artifactId, version이 무엇인지를 알아야 한다. 프로젝트의 POM에 의존관계로 서블릿 명세 API를 추가하려면 pom.xml에 예제 5-7에 보여지는 dependency 요소를 추가한다.

예제 5-7. 의존관계로 서블릿 2.4 명세 추가
<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-servlet_2.4_spec</artifactId>
      <version>1.1.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>

모든 Apache Geronimo 명세 구현에 대한 groupId는 org.apache.geronimo.specs 이다. artifactId는 익숙한 명세의 버전을 포함한다. 예를 들어, 서블릿 2.3 명세를 포함하도록 하려면 geronimo-servlet_2.3_spec의 artifactId를 사용하며, 서블릿 2.4 명세를 사용하려면 artifactId는 geronimo-servlet_2.4_spec 이 될 것이다. 버전으로 사용해야 하는 버전이 어떤 것인지를 알아보기 위해 공식 Maven 레파지토리를 살펴보아야 한다. 버전에 대해 가장 좋은 것은 특정 명세 구현체에 대한 가장 최근 버전을 참고하는 것이다. Sun 명세에서 Apache Geronimo 프로젝트로 특정 대안으로 살펴보려면, 부록 B에 사용 가능한 명세 목록을 모아두었다.
이 의존관계에 대해 provided 영역을 사용했다는 것은 짚고 넘어갈 만한 가치가 있다. 이는 메이븐에게 JAR가 컨테이너에 의해서 “제공되는(provided)” 것이고 따라서 WAR에 포함되지 않는다를 것을 말한다.
이 간단한 웹 어플리케이션에 대해 JSP 태그를 작성하고 한다면, JSP 2.0 명세에 대한 의존관계를 추가할 필요가 있다. 이 의존관계를 추가하기 위해 예제 5-8에 나타난 설정을 사용하라.

예제 5-8. 의존관계로 JSP 2.0 명세 추가
<project>
  [...]
  <dependencies>
    [...]
    <dependency>
      <groupId>org.apache.geronimo.specs</groupId>
      <artifactId>geronimo-jsp_2.0_spec</artifactId>
      <version>1.1</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  [...]
</project>

의존관계로 서블릿 명세를 추가했다면, mvn clean install 을 실행하고 다음에 mvn jetty:run 을 실행한다.

[tobrien@t1 simple-webapp]$ mvn clean install
...
[tobrien@t1 simple-webapp]$ mvn jetty:run
[INFO] [jetty:run]
...
2007-12-14 16:18:31.305::INFO:  jetty-6.1.6rc1
2007-12-14 16:18:31.453::INFO:  No Transaction manager found - if your webapp\
           requires one, please configure one.
2007-12-14 16:18:32.745::INFO:  Started SelectChannelConnector@0.0.0.0:8080
[INFO] Started Jetty Server

이 시점에서 SimpleServlet 의 결과를 볼 수 있을 것이다. 명령행에서 표준 출력에 이 서블릿의 결과를 출력하는 curl을 사용할 수 있다.

~/examples$ curl http://localhost:8080/simple-webapp/simple
SimpleServlet Executed


5.7 결론

이 장을 읽은 후에 간단한 웹 어플리케이션을 설치할 수 있다. 이 장은 완전한 웹 어플리케이션을 만드는 다양한 다른 방법을 설명하지 않는다. 다른 장은 좀 더 유명한 웹 어플리케이션과 기술을 중에 몇가지와 관련된 프로젝트에 대한 더 이해될 수 있는 내용을 제공한다.


<<Pre   Next>>

Creative Commons License
Elvis Lee에 의해 창작된 메이븐 가이드 은(는) 크리에이티브 커먼즈 저작자표시-비영리-동일조건변경허락 2.0 대한민국 라이선스에 따라 이용할 수 있습니다.
www.sonatype.com의 저작물에 기초

반응형