top bar

글 목록

레이블이 maven인 게시물을 표시합니다. 모든 게시물 표시
레이블이 maven인 게시물을 표시합니다. 모든 게시물 표시

2016년 2월 28일 일요일

[MAVEN] Shade Plugin (2) - artifactSet과 filter 설정 사용 및 minimizeJar

들어가기 앞서



가령 아래와 같은 구조의 project-a, project-b, project-c 라고 하는
3개의 프로젝트가 있다고 하자. (클릭하면 크게 보임)





이때 'MainClass' 가 있는 'project-a'의 pom.xml의 'dependency' 설정은 아래와 같이 구성한다.
<dependencies>

    <dependency>
        <groupId>com.asuraiv.bbb</groupId>
        <artifactId>project-b</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
    </dependency>

</dependencies>

간단히 코드도 살펴보자.

'project-a'의 'MainClass'는 아래처럼 'project-b'의
'com.asuraiv.bbb.util_1.B_Utils1' 클래스를 사용한다.
package com.asuraiv.aaa;

import com.asuraiv.bbb.util_1.B_Utils1;

public class MainClass {
    
    public static void main(String[] args) {
        
        System.out.println("Hello Shade Plugin !!");
        
        // project-b 의 Utils 클래스 사용
        B_Utils1.printString("Hello Project B");
    }
}
'B_Utils1.printString' 메소드는 아래처럼 단순히 System out으로 문자열을 찍는다.
package com.asuraiv.bbb.util_1;

public class B_Utils1 {
    
    public static void printString(String text) {
        System.out.println(text);
    }
}

한편, 'com.asuraiv.bbb.util_2.B_Utils2' 클래스는 'project-c' 의 클래스를 사용한다.
package com.asuraiv.bbb.util_2;

import com.asuraiv.ccc.util_2.C_Utils2;

public class B_Utils2 {
    
    public static void printIntegerUsingCUtils(int value) {
        
        // project-c 의 Utils 클래스 사용
        C_Utils2.printInteger(value);
    }
}
뭐, 이런 상황이다.

이렇게 되면 이들 3개의 프로젝트의 의존관계는 아래처럼 될 것이다.










정리하자면 'project-a' 의 'MainClass' 에서 'project-b'의 'com.asuraiv.bbb.util_1.B_Utils1' 를 사용하고, 'project-b' 의 다른 패키지인 'com.asuraiv.bbb.util_2.B_Utils2'에서는 'project-c'의 'com.asuraiv.ccc.util_2.C_Utils2' 를 사용하는 것이다.

이때 'project-a'를 shade 플러그인을 사용하여 'uber-jar' 만들고,
디렉토리 구조를 보면 아래와 같은 것이다.
├─com
│  └─asuraiv
│      ├─aaa
│      ├─bbb
│      │  ├─util_1
│      │  └─util_2
│      └─ccc
│          ├─util_1
│          └─util_2
├─junit
│  ├─awtui
│  ├─extensions
│  ├─framework
│  ├─runner
│  ├─swingui
│  │  └─icons
│  └─textui
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        ├─com.asuraiv.bbb
        │  └─project-b
        └─com.asuraiv.ccc
            └─project-c


artifactSet



위의 상황들이 전제로 주어졌을때, 결과적으로 'project-c'는 'project-b'가 의존하고 있지만, 사용되지 않는다. 'project-a'의 'MainClass'가 'project-b'의 'com.asuraiv.bbb.util_1.B_Utils1' 만을 사용하고 있기 때문이다.

하지만 project-c를 사용하지 않음에도 불구하고 shade 플러그인을 사용해 생성된 uber-jar는 위의 tree 구조를 보면 알 수 있듯 물리고 물린 의존관계는 몽땅 패키징이 되어 사용되지 않더라도 uber-jar 안에 포함되 있는것을 볼 수 있다.

이때 사용하지 않는 'proejct-c' 의 의존관계를 uber-jar 생성시에 제외시킬 수 있다.

'configuration' 태그 밑의 'artifactSet' 설정을 사용하여 어떤 라이브러리를 제외하고 포함시킬 것인지 정의가 가능하다. 아래와 같이 설정한다.
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>2.4.3</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer
                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.asuraiv.aaa.MainClass</mainClass>
                    </transformer>
                </transformers>
                <artifactSet>
                    <excludes>
                        <exclude>com.asuraiv.ccc:project-c</exclude>
                    </excludes>                             
                </artifactSet>
                <outputFile>d:/shade/project-a.jar</outputFile>
            </configuration>
        </execution>
    </executions>
</plugin>
'artifactSet'태그와 함께 'excludes' 설정을 하면, 어떤 라이브러리를 제외 시킬 것인지 명시 할 수 있게 된다. 더불어 'outputFile'은 jar 파일이 떨어지는 위치를 말한다.

이러한 설정을 바탕으로 package를 하게 되면 아래와 같은 tree 구조가 된다.
├─com
│  └─asuraiv
│      ├─aaa
│      └─bbb
│          ├─util_1
│          └─util_2
├─junit
│  ├─awtui
│  ├─extensions
│  ├─framework
│  ├─runner
│  ├─swingui
│  │  └─icons
│  └─textui
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        └─com.asuraiv.bbb
            └─project-b
보는것과 같이 사용하지 않는 'project-c' 가 패키징 되지 않았다.


filters



하지만 아래처럼 'MainClass'에서 'B_Utils1.printString'을 사용하지 않고 'project-c'를 사용하여 문자열을 찍는 'B_Utils2.printIntegerUsingCUtils' 메소드를 사용한다면 어떨까?
package com.asuraiv.aaa;

import com.asuraiv.bbb.util_2.B_Utils2;

public class MainClass {
    
    public static void main(String[] args) {
        
        System.out.println("Hello Shade Plugin !!");
        
        // project-c를 사용하여 문자열을 찍는 project-b의 Utils 클래스를 사용
        B_Utils2.printStringUsingCUtils("Hello Project B");
    }
}
'project-c' 라이브러리를 통째로 날렸다가는 프로그램 실행시에 아래와 같은 에러를 볼 것이다.



'MainClass'에서, 'project-c' 라이브러리를 사용하는 'project-b'의 메소드를 호출했는데, 'project-c'가 통째로 제외 되었으니 런타임에 오류가 나는것은 당연하다.

이때 'filter' 설정을 사용해 'project-c' 라이브러리에서 사용하는 패키지를 남기고 나머진 제외 시켜서 uber-jar를 생성할 수 있다!
<configuration>
    <transformers>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.asuraiv.aaa.MainClass</mainClass>
        </transformer>
    </transformers>
    <artifactSet>
        <excludes>
            <exclude>junit:junit</exclude>
        </excludes>
    </artifactSet>
    <filters>
        <filter>
            <artifact>com.asuraiv.ccc:project-c</artifact>
            <excludes>
                <exclude>com/asuraiv/ccc/util_2/**</exclude>
            </excludes>
        </filter>
    </filters>
    <outputFile>d:/project-a.jar</outputFile>
</configuration>
앞서 배운 'artifactSet' 설정을 사용하여 'junit'을 통째로 제외 시켰다.

여기서 'filters' 설정에 주목하자. 현재 'project-b'의 'printStringUsingCUtils' 메소드는, 'project-c'의 'com.asuraiv.ccc.util_1' 패키지만을 사용하고 'com.asuraiv.ccc.util_2' 패키지는 사용하지 않는다. 따라서 'filters' 설정을 사용하여, 특정 라이브러리의 특정 패키지만을 제외 시킨 것이다.

tree로 디렉토리 구조를 보면 아래와 같이 필요한 패키지만 uber-jar에 포함되어 있다.
├─com
│  └─asuraiv
│      ├─aaa
│      ├─bbb
│      │  ├─util_1
│      │  └─util_2
│      └─ccc
│          └─util_1
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        ├─com.asuraiv.bbb
        │  └─project-b
        └─com.asuraiv.ccc
            └─project-c
ccc.utils_2 패키지가 제외 되었다. 근데 사실 bbb.utils_1 패키지도 사용하지 않는다.
filter 설정을 추가해보자
<filters>
    <filter>
        <artifact>com.asuraiv.bbb:project-b</artifact>
        <excludes>
            <exclude>com/asuraiv/bbb/util_1/**</exclude>
        </excludes>
    </filter>
    <filter>
        <artifact>com.asuraiv.ccc:project-c</artifact>
        <excludes>
            <exclude>com/asuraiv/ccc/util_2/**</exclude>
        </excludes>
    </filter>                               
</filters>
위와같이 'project-b' 에 관련한 필터설정도 추가했다.

빌드후에 디렉토리 구조를 보면 아래와 같이 필요없는 패키지들이 모두
제거된 것을 볼 수 있다.
├─com
│  └─asuraiv
│      ├─aaa
│      ├─bbb
│      │  └─util_2
│      └─ccc
│          └─util_1
└─META-INF
    └─maven
        ├─com.asuraiv.aaa
        │  └─project-a
        ├─com.asuraiv.bbb
        │  └─project-b
        └─com.asuraiv.ccc
            └─project-c


minimizeJar



사실 'artifactSet' 이나 'filter' 설정처럼 세밀하게 포함될/제외될 라이브러리나 패키지를 설정하기 힘들다면, 'minimizeJar' 설정으로 간편하게 필요없는 소스코드들을 제외 시킬 수 있다
<configuration>
    <transformers>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.asuraiv.aaa.MainClass</mainClass>
        </transformer>
    </transformers>
    <minimizeJar>true</minimizeJar> <!-- 필요없는 소스코드 제외! -->
    <outputFile>d:/shade/project-a.jar</outputFile>
</configuration>
위와 같이 'minimizeJar' 태그 설정만 하면 우리 위에서 살펴봤던 'artifactSet'과 'filter'설정을 한 것과 동일한 결과를 가져온다.

여태까지 'exclude'의 경우만 설명하고 'include'의 경우를 설명하지 않았는데, 사용법은 'exclude'와 동일하다.

만약 minimizeJar를 사용했는데 런타임에 필요한 패키지까지 제외되어 오류가 난다면 'include' 설정을 사용하여 필요한 패키지를 포함 시킬 수 있을 것이다.

'artifactSet'을 사용하여 어떤 라이브러리를 포함/제외 시킬 것인지, 'filter'를 사용하여 패키지 레벨로 세세하게 명시할 것인지, minimizeJar를 사용하여 간편하게 jar파일을 만들것인지.. 이 모든 방법들을 적당하게 조합하여 사용하면 효율적인 용량의 jar 파일을 만들 수 있을 것이다.


2016년 1월 26일 화요일

[MAVEN] Shade Plugin (1) - 기본 사용법과 Resource Transformer

개요



메이븐에서 자바로 구성된 프로젝트를 자바 아카이브 파일 (이하 jar 파일)로 말아(?) 주는 플러그인이 몇가지가 있는데, 오늘 정리할 플러그인은 'maven-shade-plugin' 이다.

일단 정리하기에 앞서 'uber-jar' 라는 개념에 대해서 살펴볼 필요가 있다.

아래 페이지에 전형적인 질/답 이 나와 있다.

http://stackoverflow.com/questions/11947037/what-is-an-uber-jar

'uber' 라는 말은 독일어로서, 'above' 또는 'over' 라는 뜻이 있다고 한다.
그러니까 영어로 따지면 'over-jar' 인 것이다.

각설하고, 'uber-jar'라는 것은 자바 어플리케이션의 모든 패키지와, 그에 의존관계에 있는 패키지 라이브러리까지 모두 하나의 'jar' 에 담겨져 있는 것 을 말한다.

'uber-jar'를 사용하면 어플리케이션을 배포할때 의존관계를 생각할 필요가 없다. 왜냐면 이미 필요한 의존관계 라이브러리들을 가지고 있기 때문이다.


Maven shade plugin



이러한 'uber-jar'를 생성할때, 해당 어플리케이션에 모든 의존관계까지 포함하다 보니, 어플리케이션 배포시에 필요없는 라이브러리까지(예를들어서 개발 프로세스에만 필요한 라이브러리 - JUnit같은..? - 라던가..) 몽땅 패키징되는 경우가 있다.

shade 플러그인의 강력함은, 배포시에 필요한 라이브러리들을 'exclude/include' 시킬 수있고, 라이브러리 수준뿐만 아니라 class 파일 수준으로 jar 파일을 minimize 함으로서 보다 가벼운 jar 파일을 생성할 수 있다는 것이다. (그러니까 미사용 class 파일도 걸러낸다는 말이다.)


기본 사용법



아래는 기본적인 사용법이다.
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <configuration>
                <!-- put your configurations here -->                         
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
메이븐 골(goal)로 'shade:shade' 를 입력하여 직접 구동 시킬 수 있지만, <executions> 설정으로 package phase에 shade 골을 바인딩 하는 설정을 하면, 'mvn package' 으로 구동 시킬 수 있다.

Resource Transformer



shade 플러그인을 적용 할때  'Resource Transformer' 라는 개념을 이해할 필요가 있다

Resource Transformer 설정을 하면 서로 다른 artifacts 들로부터 uber-jar 를 생성할때, classes 및 resources 파일들을 '중복없이' 패키징 할 수 있게 해준다.

각 Resources Transformer 설정의 종류 및 특징은 아래와 같다

Transformers in org.apache.maven.plugins.shade.resource
ApacheLicenseResourceTransformerPrevents license duplication
ApacheNoticeResourceTransformerPrepares merged NOTICE
AppendingTransformerAdds content to a resource
ComponentsXmlResourceTransformerAggregates Plexus components.xml
DontIncludeResourceTransformerPrevents inclusion of matching resources
IncludeResourceTransformerAdds files from the project
ManifestResourceTransformerSets entries in the MANIFEST
PluginXmlResourceTransformerAggregates Mavens plugin.xml
ServicesResourceTransformerRelocated class names in META-INF/services resources and merges them.
XmlAppendingTransformerAdds XML content to an XML resource
이들 설정 가운데 흔히 쓰이는 몇가지 설정만 살펴보자.

(1) ManifestResourcesTransformer


여기서 주로 쓰이는 것은 ManifestResourcesTransformer 인데, 설명에 나와있는대로 자바 'MANIFEST' 파일의 entries 를 세팅해 준다.

아래와 같이 <configuration> 설정에 추가한다
<configuration>
    <transformers>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>com.asuraiv.project.aaa.MainClass</mainClass>
        </transformer>
    </transformers>
</configuration>
실행 가능한 jar 파일을 생성할시에 자바 어플리케이션을 구동할 MainClass를 지정해야하는데, 이것은 'MANIFEST' 파일의 entry 중 하나이다. 'MANIFEST' 파일 관련은 이전 포스트를 참고하길 바란다.

위 예제처럼 <mainClass> 설정으로 해당 어플리케이션의 메인클래스를 입력한다.

(2) AppendingTransformer


만약 스프링 batch 프로젝트를 shade 플러그인을 통해 'Executable JAR' 파일로 패키징 한다고 하자. 그럴 경우 Main 클래스는 스프링 batch job을 커맨드 라인에서 실행 할 수 있게 해주는 'org.springframework.batch.core.launch.support.CommandLineJobRunner' 가 된다.

그러면 ManifestResourceTransformer의 <mainClass> 설정만 해당 클래스로 설정해주면 될까?
그것만 해서는 안된다.

스프링으로 구성된 어플리케이션은 스프링 컨텍스트 xml의 namespace를 핸들링 해주는 Handler 클래스들이 정의되어 있는 spring.handlers 파일과, 스프링 컨텍스트 xml 설정 파일의 스키마(xsd 파일 등)가 정의되어 있는 spring.schemas 파일이 필요하다.

바로 이때, AppendingTransformer 설정을 사용하여 uber-jar에 포함 시킬 수 있다.
<configuration>
    <transformers>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
            <mainClass>org.springframework.batch.core.launch.support.CommandLineJobRunner</mainClass>                                   
        </transformer>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
            <resource>META-INF/spring.handlers</resource>
        </transformer>
        <transformer
            implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
            <resource>META-INF/spring.schemas</resource>
        </transformer>
</configuration>
기본적으로 스프링 라이브러리 jar 파일(spring-context, spring-aop, spring-beans 등등...)을 까보면 각각 META-INF 밑에 spring.handlers, spring.schemas 파일이 존재한다.

앞서 설명했듯이 shade 플러그인이 이 모든 의존관계 라이브러리들을 한데 묶어서 uber-jar 를 생성할때, 위 2개의 파일들이 각각 스프링 라이브러리에 동일한 이름으로 존재(하지만 그 내용은 또 각각 다르다ㅠㅠ)하기 때문에 중복의 문제가 존재한다.

따라서 AppendingTransformer 설정으로 해당 파일들을 포함시키면, 마치 'merge' 를 하는것과 같이 각 라이브러리의 핸들러, 스키마 정보들이 각각 하나의 spring.handlers, spring.shcemas 파일로 생성되는 것이다. 신통하다!

ResourcesTransformer에 관해 여기서 다 정리할 수는 없으니, 나머지는 공식 도큐먼트를 참고하자

다음 포스트에는 앞서 설명한 artifacts 단위로 include/exclude 하는 방법, minimize 설정을 통해 가벼운 jar 파일을 생성하는 방법 등을 정리해야 것다!


2015년 11월 11일 수요일

[MAVEN] Sonatype Nexus + Maven + Jenkins 배포환경 구성

1. 개요


 유지보수 업무를 하는 중, 여러 컴포넌트들이 공통적으로 쓰는 core 클래스들을 라이브러리로 묶어, 사내 maven repository에 등록하여 사용할 필요성을 느꼈다. 하지만 사내 repository에 jar를 등록하는 방법은 아이디를 발급받아야 하는 등 절차가 어려웠다.. 기보다는 귀찮았다. 그래서 직접 'sonatype nexus' 를 활용하여 maven repository를 구축해보기로 하였다.

Let the working begin~


2. Sonatype Nexus 설치


 이 작업은 소름돋을 정도로 쉽다. 예전엔 Nexus webapp을 다운받아, 따로 톰캣에 올려야했는데 요즘엔 jetty 기반의 WAS가 built-in 되어있는 컴포넌트로 릴리즈 되는것 같다.

아래 공식 웹사이트를 참고하자

http://www.sonatype.org/nexus/

참고로 nexus는 oss 와 pro 버전이 있는데, pro는 비용을 지불해야 하는 상용 버전이다. (pro도 기능에 따라 종류가 세분화 되는 듯 하다) 하지만 구지 pro 를 사용할 필요없이 oss 버전도 기본적인 repository management를 제공하기때문에 여기서는 oss 버전으로 진행한다.

1) download
$ wget --no-check-certificate https://sonatype-download.global.ssl.fastly.net/nexus/oss/nexus-2.11.1-01-bundle.tar.gz
wget으로 먼저 'tar' 파일을 다운받는다. 'https' 프로토콜 이기 때문에 '--no-check-certificate' 옵션을 잊지 말자

2) 압축해제
$ tar -xvzf nexus-2.11.1-01-bundle.tar.gz

3) 구동
bin]$ ./nexus start
Starting Nexus OSS...
Started Nexus OSS.

4) 접속

기본포트는 '8081'이며, /conf/nexus.properties 파일에서 변경할 수 있다
아래처럼 http://[server ip]:8081/nexus 로 접속한다

























5) 둘러보기

우측 상단에 'Log In' 버튼을 눌러 로그인할 수 있는데, 기본적으로 관리자 계정으로 'admin' (비번 admin123) 을 제공한다.

























왼쪽 메뉴의 'Repositories' 를 클릭하면 각종 저장소가 보인다.

저장소에는 3가지 유형이 있는데, 각각 아래와 같다.


  • 프록시 저장소 (proxy) : 프록시 저장소는 외부에 있는 메이븐 공개 저장소에 대한 프록시 역할을 하는 저장소이다.  위 사진에서는 'Central' 이라는 이름으로 메이븐 중앙 저장소가 이미 추가가 되어있으며, 대략적인 개념은 아래와 같다. 말그대로 '대리자'인 셈이다.








  • 호스티드 저장소(hosted) : 필자가 이 삽질을 하고 있는 이유다. 말그대로 사내에서 사용하는 라이브러리 관리 또는 3rd Party 라이브러리를 관리하기 위한 용도이다. 보면 알겠지만 기본적으로 'Release' , 'Snapshots' , '3rd party' 라는 저장소가 이미 추가되어있다.
  • 버추얼 저장소(virtual) : 넥서스에 이미 설정되어 있는 저장소에 대하여 다른 URL로 접근 할 수 있도록 지원하기 위한 논리적인 저장소이다.
  • 저장소 그룹(group) : 넥서스에 설정한 저장소의 그룹이다. 프로젝트가 진행되면서 의존 관계에 있는 라이브러리가 증가하면서 외부 저장소도 증가하는데, 이 저장소 그룹에다 추가되는 외부 저장소를 추가하면 메이븐 설정파일 변경 없이 의존 관계를 확장할 수 있다.

3. Maven 설정


이 작업 또한 매우 간단하다

1) .m2/settings.xml 파일 변경

아래처럼 <servers> 설정에 nexus 사용자 아이디와 비번을 설정한다.
      .
      .
      .
      <server>
            <id>releases</id>
            <username>admin</username>
            <password>admin123</password>
      </server>
</servers>

2) project pom.xml 파일 설정 추가
<distributionManagement>
        <repository>
            <id>releases</id>
            <url>http://[server ip]:8081/nexus/content/repositories/releases</url>
        </repository>
</distributionManagement>

이때 주의해야 할점은 settings.xml 파일에 설정한 <server><id> 와 pom.xml 에 설정한 <repository><id> 값이 일치해야 한다는 점이다.


4. Jenkins 설정 및 Nexus로 배포하기


1) Jenkins 설정

Jenkins에서 해당 프로젝트를 빌드할때 maven goal을 'deploy'로 설정한다. 아래와 같다.











'profile' 매개변수는 빌드 환경에따라 다른 저장소로 (release/alpha) 배포하기 위해 설정한다. 'deploy' 를 입력하면 deploy phase에 메이븐에 기본적으로 내장된 'maven-deploy-plugin' 이 수행되면서 <distributionManagement> 에 설정된 저장소로 배포하게 된다.

이때 profile 마다 <distributionManagement> 설정을 바꿔 다른 저장소로 배포하게 하면 개발 환경에 따라 저장소를 달리해서 구분하여 관리할 수 있다.

2) 배포 하기 

Jenkins Build를 수행한다. output console 로그를 보면 아래와 같이 해당 Nexus로 Uploaded 되었다는 내용이 출력 될 것이다.







3) Nexus에서 확인


해당 repository의 'Browse Index' 탭을 열어보면 방금 빌드한 라이브러리가 배포된 것을 볼 수 있다.

이것으로 완료 되었다!


2015년 8월 5일 수요일

[Maven] 메이븐 모듈 (Module)

1. 개요



 프로젝트 진행시 초기에는 하나의 프로젝트만으로 개발하다가 규모가 커지거나 새로운 기능을 요구하는 경우에는 프로젝트를 분리하게 되는데, 이때 모듈 구조를 사용한다.

메이븐 모듈 방식을 사용하면 하나의 프로젝트가 어려개의 모듈을 가지고 여러개의 프로젝트(모듈)을 한번에 빌드할 수 있게 된다.


2. 개념



1) 상속

메이븐에서 모든 설정 파일은 최상위 pom.xml 파일을 상속하고 있다.
최상위 pom.xml 의 정보를 출력하기 위해서는 아래와 같은 명령어를 사용한다
$ mvn help:effective-pom
이렇게 기본으로 최상위 pom.xml 을 상속하듯이 프로젝트에서 공통으로 사용하는 설정은 공통 pom.xml 파일을 만들어 관리하고, 하위 모듈에서 이 pom.xml 파일을 상속 할 수 있다.
<!-- 부모 POM 파일 예제 -->
 
<project>
   <modelVersion>1.0.0</modelVersion>
   <groupId>com.mycorp</groupId>
   <artifactId>parent</artifactId>
   <packaging>pom</packaging>           <!-- packaging 엘리먼트 값을 'pom'으로 설정 -->
   <version>1.0-SNAPSHOT</version>
   .
   .
   .
이때 위의 설정처럼 부모 pom.xml 파일의 packaging 태그 값은  'pom'으로 설정 하여야한
다. 자식 pom.xml 파일은 <parent/> 엘리먼트를 이용하여 부모 pom.xml 파일을 상속한다
<!-- 자식 POM 파일 예제 -->
<project>
   .
   .
   <parent>
      <artifactId>parent</artifactId>
      <groupId>com.mycorp</groupId>    
      <version>1.0-SANPSHOT</version>
   </parent>
   . 
   .
</project>
모듈 구조는 일단 이러한 식으로 구성된다.

파일 tree로 보자면 일반적인 모듈 구조는 아래와 같다.
parent
├── batch
│   ├── src
│   └── pom.xml
├── core
│   ├── src
│   └── pom.xml
├── web
│   ├── src
│   └── pom.xml
└── pom.xml
일단 최상위 디렉토리인 parent에 부모 pom.xml 파일이 있다. 이를 위에서 예로 든 것처럼 batch, core, web 의 3개의 모듈의 pom.xml에서 상속하고 있다.

2) 집합

느낌상으로 알겠지만 core 모듈은 batch 와 web 모듈에서 공통적으로 쓰이는 클래스를 제공한다. 다시말해 각각 batch와 web은 core 모듈에 의존성이 존재한다.

한마디로 web을 빌드하려면 core -> web 순으로 빌드되어야 하고, batch를 빌드하려면 core -> batch 순으로 빌드가 되어야 하는것이다. 이런식으로 빌드가 되게 하기 위해 부모 pom.xml 파일은 아래와 같이 작성되어야 한다.
<project>
   <modelVersion>1.0.0</modelVersion>
   <groupId>com.mycorp</groupId>
   <artifactId>parent</artifactId>
   <packaging>pom</packaging>           
   <version>1.0-SNAPSHOT</version>
   <modules>
      <id>web-build</id>
      <module>core</module>  <!-- 모듈 목록을 등록한다 -->
      <module>web</module>
   </modules>
   <modules>
      <id>batch-build</id>
      <module>core</module>  <!-- 모듈 목록을 등록한다 -->
      <module>batch</module>
   </modules>
   .
   .
   .
각각 id로 구분하여 빌드해야할 모듈 목록을 등록한다. mvn 명령어로 빌드시에, 위에 설정된 대로 id값을 빌드파라메터로 전달하면, 해당 id를 가진 모듈들이 위에서부터 아래로 순서대로 빌드 된다.

다시 정리하자면, 하나의 프로젝트가 여러 모듈로 분리될 경우, 모듈간에 의존관계가 발생하므로 빌드를 개개의 모듈별로 진행하면 빌드가 실패할 수 있다. 따라서 여러 모듈을 빌드할때 같은 단위로 빌드 할 수 있도록 하는 기능이 '집합' 이다.

예를들어 web 프로젝트를 빌드하고 싶다면 아래와 같이 빌드한다!
$ mvn -P web-build clean package



[Maven] package org.junit does not exist

 메이븐 빌드시에 junit 의존성 패키지를 찾지 못하며 빌드가 실패 할때가 있다. 문제 해결을 위해서 compiler 플러그인, shade 플러그인 설정 등등을 찾아봤지만 삽질의 연속..

일단 문제는 아래와 같다.
5,209  SUCCESS>[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project amigo-service-broker: Compilation failure: Compilation failure:
  5,210  SUCCESS>[ERROR] xxx.java:[21,17] package org.junit does not exist
  5,211  SUCCESS>[ERROR] xxx.java:[21,17] package org.junit does not exist
알고보니 매우 흔한 이슈이다. 아래 Stack Overflow에 간단한 이슈 해결법이 나와있다.

http://stackoverflow.com/questions/5845990/maven-3-and-junit-4-compilation-problem-package-org-junit-does-not-exist

확인해보니 maven의 pom.xml파일에 JUnit 관련 의존성 설정이 아래와 같이 되어있었다.
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope> <!-- 이 설정을 제거! -->
</dependency>
test로 설정된 scope를 제거하면 된다. JUnit 라이브러리를 테스트 시에만 사용하도록 하는 설정인데, 빌드할때 해당 라이브러리가 포함되지 않았기 때문에 JUnit에 대한 패키지를 찾을 수 없다는 메시지가 나오며 빌드가 실패한 것이다.