728x90
반응형

Maven의 기초를 다루는 초급 튜토리얼은 이 기사의 참고자료 섹션에 소개된 일부 튜토리얼을 비롯하여 많이 있으므로 이 기사의 5가지 팁에서는 그 이후의 단계 즉, Maven을 사용하여 애플리케이션의 라이프사이클을 관리할 때 발생하는 프로그래밍 시나리오와 관련된 정보를 제공한다.

1. 실행 가능 JAR 파일

Maven을 사용하면 JAR 파일을 매우 쉽게 빌드할 수 있다. 프로젝트 패키지를 "jar"로 정의한 다음 패키지 라이프사이클 단계를 실행하기만 하면 된다. 하지만 실행 가능 JAR 파일을 정의하는 작업은 좀 까다롭다. 이를 효과적으로 수행하려면 다음 단계를 따른다.

  1. 실행 가능 클래스를 정의한 JAR의 MANIFEST.MF 파일에서 main 클래스를 정의한다. (MANIFEST.MF는 Maven에서 애플리케이션을 패키징할 때 생성되는 파일이다.)
  2. 프로젝트에 종속된 모든 라이브러리를 찾는다.
  3. 애플리케이션 클래스에서 찾을 수 있도록 해당 라이브러리를 MANIFEST.MF 파일에 포함시킨다.

이 모든 작업을 수동으로 수행할 수도 있지만 maven-jar-plugin  maven-dependency-plugin이라는 두 Maven 플러그인을 사용하여 효율적으로 수행할 수도 있다.

maven-jar-plugin

maven-jar-plugin에는 많은 기능이 있지만 여기에서는 이 플러그인을 사용하여 기본 MANIFEST.MF 파일의 내용을 수정하는 방법만 살펴본다. POM 파일의 플러그인 섹션에 Listing 1의 코드를 추가한다.


Listing 1. maven-jar-plugin을 사용하여 MANIFEST.MF 수정하기

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.mypackage.MyClass</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>


모든 Maven 플러그인은 <configuration> 요소를 통해 해당 구성을 노출한다. 이 예제에서는 maven-jar-plugin이 MANIFEST.MF 파일의 내용을 제어하는 archive 속성 구체적으로 말해서, 아카이브의 manifest 속성을 수정한다. 이 속성에는 다음 세 가지 요소가 있다.

  • addClassPath: 이 요소를 true로 설정하면 maven-jar-plugin Class-Path 요소를 MANIFEST.MF 파일에 추가하고 해당Class-Path 요소의 모든 종속 항목을 포함시킨다.
  • classpathPrefix: 모든 종속 항목을 JAR과 같은 디렉토리에 포함시킬 계획이라면 이 요소를 생략할 수 있다. 그렇지 않은 경우에는 classpathPrefix를 사용하여 모든 종속 JAR 파일의 접두부를 지정한다. Listing 1에서 classpathPrefix는 모든 종속 항목이 아카이브의 "lib" 폴더에 있어야 한다고 지정한다.
  • mainClass: 이 요소를 사용하여 사용자가 java -jar 명령으로 JAR을 실행할 때 실행할 클래스의 이름을 정의한다.

maven-dependency-plugin

이러한 세 요소를 사용하여 MANIFEST.MF 파일을 구성한 후에는 모든 종속 항목을 lib 폴더에 실제로 복사해야 한다. 이를 위해maven-dependency-plugin을 사용한다(Listing 2 참조).


Listing 2. maven-dependency-plugin을 사용하여 lib에 종속 항목 복사하기

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>install</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>
                              ${project.build.directory}/lib
                            </outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


maven-dependency-plugin에는 사용자가 선택한 디렉토리에 종속 항목을 복사하는 copy-dependencies goal이 있다. 이 예제에서는build 디렉토리 아래의 lib 디렉토리(project-home/target/lib)에 종속 항목을 복사했다.

종속 항목과 수정된 MANIFEST.MF를 배치한 후에는 다음과 같이 간단한 명령으로 애플리케이션을 시작할 수 있다.

java -jar jarfilename.jar


2. MANIFEST.MF 사용자 정의하기

maven-jar-plugin을 사용하여 MANIFEST.MF 파일의 일반적인 부분을 수정할 수 있기는 하지만 MANIFEST.MF를 사용자 정의해야 하는 경우도 있다. 이 작업은 다음과 같이 2단계로 수행할 수 있다.

  1. 모든 사용자 정의 구성을 "임시" MANIFEST.MF 파일에 정의한다.
  2. MANIFEST.MF 파일을 사용하고 Maven 사용자 정의를 통해 확장할 수 있도록 maven-jar-plugin을 구성한다.

예를 들어, Java 에이전트가 포함된 JAR 파일이 있다. Java 에이전트를 실행하려면 Premain-Class와 권한을 정의해야 한다. Listing 3에서는 그러한 MANIFEST.MF 파일의 내용을 보여 준다.


Listing 3. 사용자 정의 MANIFEST.MF 파일에 있는 Premain-Class 정의

Manifest-Version: 1.0
Premain-Class: com.geekcap.openapm.jvm.agent.Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true


Listing 3에서는 클래스를 재정의 및 변환할 수 있는 권한을 Premain-Class com.geekcap.openapm.jvm.agent.Agent에 부여한다. 그런 다음 MANIFEST.MF 파일을 포함하도록 maven-jar-plugin을 업데이트한다(Listing 4 참조).


Listing 4. Premain-Class 포함시키기

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestFile>
                          src/main/resources/META-INF/MANIFEST.MF
                        </manifestFile>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>
                              com.geekcap.openapm.ui.PerformanceAnalyzer
                            </mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>


Maven 3

Maven 2는 가장 유명하고 많이 활용되는 오픈 소스 Java 라이프사이클 관리 도구 중 하나로 자리잡았다. 2010년 9월에 alpha 5까지 개발된 Maven 3에서는 그 동안 간절히 기다려왔던 몇 가지 변경 사항이 적용되었다. 참고자료 섹션에서 Maven 3의 새로운 기능에 대해 알아보기 바란다.

이 예제에서는 JAR 파일을 Java 에이전트로 실행할 수 있도록 허용하는Premain-Class도 정의하고 실행 가능 JAR 파일로 실행할 수 있도록 허용하는 mainClass도 있다는 점이 흥미롭다. 이 특별한 예제에서는OpenAPM(필자가 빌드한 코드 추적 도구)을 사용하여 Java 에이전트 및 사용자 인터페이스에 의해 기록될 코드 추적을 정의한다. 이렇게 하면 기록된 추적을 효율적으로 분석할 수 있다. 간단히 말해서 이 예제에서는 명시적 매니페스트 파일과 동적 수정 사항을 결합하여 얻을 수 있는 효과를 보여 준다.

3. 종속 항목 트리

Maven의 가장 유용한 기능 중 하나는 종속 항목 관리 기능이다. 사용자가 애플리케이션에 종속된 라이브러리를 정의하기만 하면 Maven이 로컬 또는 중앙 저장소에 있는 종속 항목을 찾아서 다운로드한 후 해당 종속 항목을 사용하여 코드를 컴파일한다.

가끔씩 특정 종속 항목의 원본을 알아야 하는 경우가 있다(예를 들어, 빌드에 동일한 JAR 파일의 호환되지 않는 다양한 버전이 있는 경우). 이 경우에는 JAR 파일의 한 버전이 빌드에 포함되지 않도록 해야 한다. 하지만 먼저 JAR을 가지고 있는 종속 항목을 찾아야 한다.

다음 명령을 알고 있다면 종속 항목을 매우 쉽게 찾을 수 있다.

mvn dependency:tree


dependency:tree 인수는 모든 직접 종속 항목과 함께 모든 하위 종속 항목을 보여 준다. 예를 들어, Listing 5는 필자의 종속 항목 중 하나에 필요한 클라이언트 라이브러리에서 발췌한 내용이다.


Listing 5. Maven 종속 항목 트리

[INFO] ------------------------------------------------------------------------
[INFO] Building Client library for communicating with the LDE
[INFO]    task-segment: [dependency:tree]
[INFO] ------------------------------------------------------------------------
[INFO] [dependency:tree {execution: default-cli}]
[INFO] com.lmt.pos:sis-client:jar:2.1.14
[INFO] +- org.codehaus.woodstox:woodstox-core-lgpl:jar:4.0.7:compile
[INFO] |  \- org.codehaus.woodstox:stax2-api:jar:3.0.1:compile
[INFO] +- org.easymock:easymockclassextension:jar:2.5.2:test
[INFO] |  +- cglib:cglib-nodep:jar:2.2:test
[INFO] |  \- org.objenesis:objenesis:jar:1.2:test


Listing 5를 보면 sis-client 프로젝트에 woodstox-core-lgpl  easymockclassextension 라이브러리가 필요하다는 것을 알 수 있다. 그런 다음 easymockclassextension 라이브러리에는 cglib-nodep  objenesis 라이브러리가 필요하다. 만일 objenesis에 문제가 있으면, 예를 들어, 1.2와 1.3이라는 두 개의 버전이 있으면 1.2 아티팩트가 easymockclassextension 라이브러리를 통해 간접적으로 반입되었다는 메시지가 이 종속 항목 트리에 표시된다.

dependency:tree 인수를 사용하면 빌드 문제점을 디버깅하는 시간을 많이 줄일 수 있으므로 적극적으로 활용하면 좋을 것이다.

4. 프로파일 사용하기

대부분의 실제 프로젝트에는 개발, 품질 보증(QA), 통합 및 프로덕션과 관련된 태스크로 구성된 핵심 환경 그룹이 하나 이상 있다. 그러한 모든 환경을 관리할 때 어려운 부분은 빌드를 구성하는 작업이다. 즉, 올바른 데이터베이스에 연결하고, 올바른 스크립트 세트를 실행하고, 각 환경에 올바른 모든 아티팩트를 배치해야 한다. Maven 프로파일을 사용하면 개별적으로 각 환경에 대한 명시적 지시사항을 빌드하지 않고도 이 모든 작업을 수행할 수 있다.

핵심은 환경 관련 프로파일과 태스크 지향적 프로파일을 결합하는 것이다. 각 환경 프로파일에서는 특정 위치, 스크립트 및 서버를 정의한다. 따라서 이 예제의 pom.xml 파일에서는 태스크 지향적 프로파일인 "deploywar"를 정의한다(Listing 6 참조).


Listing 6. 배치 프로파일

    <profiles>
        <profile>
            <id>deploywar</id>
            <build>
                <plugins>
                    <plugin>
                        <groupId>net.fpic</groupId>
                        <artifactId>tomcat-deployer-plugin</artifactId>
                        <version>1.0-SNAPSHOT</version>
                        <executions>
                            <execution>
                                <id>pos</id>
                                <phase>install</phase>
                                <goals>
                                    <goal>deploy</goal>
                                </goals>
                                <configuration>
                                    <host>${deploymentManagerRestHost}</host>
                                    <port>${deploymentManagerRestPort}</port>
                                    <username>${deploymentManagerRestUsername}</username>
                                    <password>${deploymentManagerRestPassword}</password>
                                    <artifactSource>
                                      address/target/addressservice.war
                                    </artifactSource>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>


ID "deploywar"로 식별되는 이 프로파일은 특정 사용자 이름 및 비밀번호 신임 정보를 사용하여 특정 호스트 및 포트에 연결하도록 구성되어 있는 tomcat-deployer-plugin을 실행한다. 이 모든 정보는 변수를 사용하여 정의된다(예: ${deploymentmanagerRestHost}). 이러한 변수는 각 환경의 profiles.xml 파일에 정의되어 있다(Listing 7 참조).


Listing 7. profiles.xml

        <!-- Defines the development deployment information -->
        <profile>
            <id>dev</id>
            <activation>
                <property>
                    <name>env</name>
                    <value>dev</value>
                </property>
            </activation>
            <properties>
                <deploymentManagerRestHost>10.50.50.52</deploymentManagerRestHost>
                <deploymentManagerRestPort>58090</deploymentManagerRestPort>
                <deploymentManagerRestUsername>myusername</deploymentManagerRestUsername>
                <deploymentManagerRestPassword>mypassword</deploymentManagerRestPassword>
            </properties>
        </profile>

        <!-- Defines the QA deployment information -->
        <profile>
            <id>qa</id>
            <activation>
                <property>
                    <name>env</name>
                    <value>qa</value>
                </property>
            </activation>
            <properties>
                <deploymentManagerRestHost>10.50.50.50</deploymentManagerRestHost>
                <deploymentManagerRestPort>58090</deploymentManagerRestPort>
                <deploymentManagerRestUsername>
                  myotherusername
                </deploymentManagerRestUsername>
                <deploymentManagerRestPassword>
                  myotherpassword
                </deploymentManagerRestPassword>
            </properties>
        </profile>


Maven 프로파일 배치하기

Listing 7의 profiles.xml에서는 두 개의 프로파일을 정의한 다음 env(environment) 특성의 값을 기반으로 프로파일을 활성화했다. env특성이 dev로 설정된 경우에는 개발 배치 정보가 사용되며, env 특성이 qa로 설정된 경우에는 QA 배치 정보가 사용된다. 이처럼 특성 값에 따라 사용되는 정보가 달라진다.

다음은 파일을 배치하는 명령이다.

mvn -Pdeploywar -Denv=dev clean install


-Pdeploywar 플래그는 Maven에게 deploywar 프로파일을 명시적으로 포함하도록 지시한다. -Denv=dev 명령문은 env라는 시스템 특성을 작성하고 그 값을 dev로 설정한다. 이렇게 하면 개발 구성이 활성화된다. -Denv=qa를 전달하면 QA 구성이 활성화된다.

5. 사용자 정의 Maven 플러그인

Maven에는 사용자가 자유롭게 사용할 수 있는 수십 개의 사전 빌드된 플러그인이 있지만 어떤 경우에는 사용자 정의 플러그인이 필요할 수도 있다. 사용자 정의 Maven 플러그인은 다음과 같이 쉽게 빌드할 수 있다.

  1. POM 패키지 세트를 "maven-plugin"으로 설정한 새 프로젝트를 작성한다.
  2. 노출된 플러그인 goal을 정의한 maven-plugin-plugin에 대한 호출을 포함시킨다.
  3. Maven 플러그인 "mojo" 클래스(AbstractMojo를 확장한 클래스)를 작성한다.
  4. Goal을 정의하는 클래스와 구성 매개변수로 노출할 변수에 대한 Javadoc 주석을 작성한다.
  5. 플러그인이 호출될 때 호출할 execute() 메소드를 구현한다.

예를 들어, Listing 8에서는 Tomcat을 배치하기 위해 설계된 사용자 정의 플러그인의 관련 부분을 보여 준다.


Listing 8. TomcatDeployerMojo.java

package net.fpic.maven.plugins;

import java.io.File;
import java.util.StringTokenizer;

import net.fpic.tomcatservice64.TomcatDeploymentServerClient;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;

import com.javasrc.server.embedded.CommandRequest;
import com.javasrc.server.embedded.CommandResponse;
import com.javasrc.server.embedded.credentials.Credentials;
import com.javasrc.server.embedded.credentials.UsernamePasswordCredentials;
import com.javasrc.util.FileUtils;

/**
 * Goal that deploys a web application to Tomcat
 *
 * @goal deploy
 * @phase install
 */
public class TomcatDeployerMojo extends AbstractMojo
{
 /**
  * The host name or IP address of the deployment server
  * 
  * @parameter alias="host" expression="${deploy.host}" @required
  */
 private String serverHost;
 
 /**
  * The port of the deployment server
  * 
  * @parameter alias="port" expression="${deploy.port}" default-value="58020"
  */
 private String serverPort;

 /**
  * The username to connect to the deployment manager (if omitted then the plugin
  * attempts to deploy the application to the server without credentials)
  * 
  * @parameter alias="username" expression="${deploy.username}"
  */
 private String username;

 /**
  * The password for the specified username
  * 
  * @parameter alias="password" expression="${deploy.password}"
  */
 private String password;

 /**
  * The name of the source artifact to deploy, such as target/pos.war
  * 
  * @parameter alias="artifactSource" expression=${deploy.artifactSource}" 
  * @required
  */
 private String artifactSource;
 
 /**
  * The destination name of the artifact to deploy, such as ROOT.war. 
  * If not present then the
  * artifact source name is used (without pathing information)
  * 
  * @parameter alias="artifactDestination" 
  *   expression=${deploy.artifactDestination}"
  */
 private String artifactDestination;
 
    public void execute() throws MojoExecutionException
    {
     getLog().info( "Server Host: " + serverHost + 
              ", Server Port: " + serverPort + 
              ", Artifact Source: " + artifactSource + 
              ", Artifact Destination: " + artifactDestination );
     
     // Validate our fields
     if( serverHost == null )
     {
      throw new MojoExecutionException( 
        "No deployment host specified, deployment is not possible" );
     }
     if( artifactSource == null )
     {
      throw new MojoExecutionException( 
        "No source artifact is specified, deployment is not possible" );
     }

        ...
   }
}


클래스 헤더에서 @goal 주석은 이 MOJO가 실행될 것이라는 goal을 지정하며, @phase는 goal이 실행될 단계를 지정한다. 노출된 각 특성에는 매개변수를 실행할 때 사용되는 별명과 함께 실제 값이 포함된 시스템 특성에 맵핑하는 표현식을 지정하는 @parameter 어노테이션이 있다. 특성에 @required 어노테이션이 있다는 것은 필수 특성임을 의미한다. default-value가 있는 경우에는 지정된 값이 없을 때 이 값이 사용된다. execute() 메소드에서 getLog()를 호출하여 Maven 로거에 액세스할 수 있다. Maven 로거는 로깅 레벨에 따라 지정된 메시지를 표준 출력 장치에 출력한다. 플러그인이 실행되지 않으면 MojoExecutionException이 발생하면서 빌드가 실패한다.

결론

Maven을 빌드 작업에만 사용할 수도 있지만 Maven은 프로젝트 라이프사이클 관리 도구로서도 매우 뛰어나다. 이 기사에서는 잘 알려져 있지는 않지만 Maven을 더욱 효과적으로 사용되는 데 도움이 되는 5가지 기능을 살펴보았다. Maven에 대한 자세한 정보는 참고자료 섹션을 참조한다.

5가지 사항 시리즈의 다음 주제는 Swing으로 아름다운 사용자 인터페이스를 빌드하는 데 도움이 되는 5가지 팁이므로 다음 기사에서도 만나볼 수 있기를 바란다.


728x90
반응형
블로그 이미지

nineDeveloper

안녕하세요 현직 개발자 입니다 ~ 빠르게 변화하는 세상에 뒤쳐지지 않도록 우리모두 열심히 공부합시다 ~! 개발공부는 넘나 재미있는 것~!

,