이번 글에서는 maven의 많은 plugin 중 shade-plugin을 이용하여 'uber-jar'를 만들어 보겠다.

 

'uber' 라는 말은 독일어로서, 'over' 라는 뜻이 있다고 한다.

영어로 따지자면 'over-jar' 인 것이다.

 

'uber-jar' 라는 것은 자바 어플리케이션의 모든 패키지와 그에 의존관계에 있는 패키지 라이브러리 까지

모두 하나의 'jar'에 담겨져 있는 것을 말한다.

 

'uber-jar'를 사용하면 어플리케이션을 배포할 때 의존관계를 생각할 필요가 없다.

왜냐하면 이미 필요한 의존관계 라이브러리를 가지고 있기 때문이다.

 

이러한 'uber-jar'를 생성할 때, 해당 어플리케이션의 모든 의존관계까지 포함하다 보니

어플리케이션 배포시에 필요없는 라이브러리까지 몽땅 패키징되는 경우가 있다.

 

shade plugin의 강력함은 배포시에 필요한 라이브러리들을 'exclude/include' 시킬 수 있고,

라이브러리 레벨 뿐만 아니라 class 파일 레벨로도 jar 파일을 minimize 함으로서

보다 가벼운 jar파일을 생성할 수 있다는 것이다.

 

이제부터 shade 플러그인의 사용법을 알아보자.

 

아래는 기본적인 사용법이다.

 

 

 

메이븐 goal로 'shade:shade' 를 입력하여 직접 구동 시킬 수 있지만,

<executions> 설정으로 package phase에 shade goal을 바인딩 하는 설정을 하면

'mvn package' 로 구동 시킬 수 있다.

 

 

Resource Transformer

 

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

 

Resource Transformer 설정을 하면 서로 다른 artifacets 들로부터 uber-jar를 생성할 때,

classes 및 resources 파일들을 중복없이 패키징 할 수 있게 해준다.

 

각 Resource Transformer 설정의 종류 및 특징은 다음과 같다.

 

 

출처 : https://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html

 

이들 중 흔히 쓰이는 몇가지 설정만 살펴보자.

 

1. ManifestResourcesTransformer

 

여기소 주로 쓰이는 것은 ManifestResourcesTransformer 인데, 

설명대로 자바 'MANIFEST' 파일의 entries를 세팅해 준다.

 

아래와 같이 <configuration> 설정에 추가한다.

 

 

실행 가능한 jar 파일을 생성할시에 자바 어플리케이션을 구동할 MainClass를 지정해야 하는데,

이것은 'MANIFEST' 파일의 entry 중 하나이다.

 

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

 

2. AppendingTransformer

 

만약 스프링 batch 프로젝트를 shade 플러그인을 통해 'Executable Jar'(사용가능한 Jar) 파일로 패키징 한다고 하자.

그럴 경우 Main 클래스는 스프링 batch Job을 커맨드 라인에서 실행 할 수 있게해주는

'org.springframework.batch.core.launch.support.CommandLineJobRunner'가 된다.

 

위의 ManifestResourcesTransformer의 mainClass 설정만 해주면 될까?

그것만 해서는 안된다.

 

스프링으로 구성된 어플리케이션은 spring-context.xml의 namespace를 핸들링 해주는

Handler 클래스들이 정의되어 있는 spring.handlers, 스키마(xsd 등)가 정의되어 있는 spring.schemas 파일이 필요하다.

 

바로 이때, AppendingTransformer 설정을 사용하여 uber-jar에 포함 시킬 수 있다.

 

 

 

기본적으로 스프링 라이브러리 jar 파일을 까보면 각각 META-INF 밑에 spring.handlers, spring.schemas 파일이 존재한다.

 

앞서 설명했듯이 shade 플러그인이 모든 의존관계 라이브러리들을 한데 묶어서 uber-jar를 생성할 때,

위 2개의 파일들이 각각 스프링 라이브러리에 동일한 이름으로 존재하기 때문의 중복의 문제가 존재한다.

 

따라서 AppendingTransformer 설정으로 해당 파일들을 포함시키면, 마치 병합(merge)을 하는 것과 같이

각 라이브러리의 핸들러, 스키마 정보들이 각각 하나의 spring.handlers, spring.shcemas 파일로 생성되는 것이다.

 

나머지 ResourcesTransformer에 대해서는 공식 Document를 참고하자.

 

 

이제 shade 플러그인의 기본적인 설정은 완료되었으니, 프로젝트를 패키징 해보자.

 

패키징이 완료가 되면 ${project.basedir}/target에 XXX.jar 파일이 생성이 되었을 것이다.

 

cmd창을 열어 해당 위치로 접근한 후, java -jar XXX.jar 명령어를 실행하게 되면, jar파일이 실행될 것이다.



다음은 실행한 화면이다.

 

 

pom.xml에서 라이브러리의 의존성을 설정하는 부분에서 <scope>의 값을 system으로 설정을 하였다면,

shade plugin에서 해당 라이브러리를 include 하지 않아서 

XXX.jar를 실행 시에 ClassNotFoundException 발생할 가능성이 있다.

 

다음 글에서는 <scope>의 값을 system로 설정하였더라도

패키징시에 해당라이브러리 까지 포함하는 Jar를 만드는 방법을 기술하겠다.

 

 

 

Maven 빌드 시 [ERROR] COMPILATION ERROR : (패키지명) does not exist가 나오는 경우가 있다.

 

이는 해당 소스에서 특정 라이브러리를 참조하지 못해서 나오는 경우인데

 

즉, Maven에는 없는 자체 jar 파일 등 추가를 해야될 필요가 있을 때 사용하는 방법이다.

 

방법은 다음과 같다.

 

1. pom.xml에 필요한 라이브러리 내용을 추가한다.

<id>, <name> 은 원하는 명칭으로 지정

<url>file://${project.basedir}/libs<url>은 프로젝트 내의 추가할 라이브러리가 있는 임의폴다의 최상위 경로를 지정한다.

 

libs라는 디렉토리를 프로젝트 내에 바로 생성하였다.

pom.xml의 <url>file://${project.basedir}/"디렉토리명"<url>

"디렉토리명" 과 같은 이름으로 생성하면 된다.

 

2. pom.xml에 다음과 같이 jar 내용을 추가한다.

 

<groupid>, <artifactid>, <version>을 임의로 지정한다.

이제 libs 폴더 안에 다음과 같이 폴더를 생성한다. 

 

 

 

pom.xml에 지정한 명칭을 사용하여 <groupid>/<artifactid>/<version> 구조로 디렉토리를 생성

그리고 해당 jar를 넣는다.

jar의 이름은 <artifactid>-<version>.jar 라 지정한다.

 

이때 주의해야할 것은 버전명의 범위인데,

0 부터 0.0.0 까지이다. 다만 범위가 더 커져야 할 경우 0.0.0-0-0 까지 '-' 으로 지정 가능하다.

 

로컬에서 개발할 때와 알파 서버에 배포하였을 때, 경로를 다르게 설정하기 위해 Maven Profile을 사용.

프로필 정의

local(개발용), alpha(배포용) 프로필 설정

여기서 사용하는 프로필에 따라 <env>...</env> 값이 다르게 설정되는 것을 볼 수 있다. 

이 값은 빌드시 어떤 Resource 경로를 참조할 건지 결정하는데 사용된다.

리소스 정의

아래 설정에서 env 값을 어떻게 사용하는지 볼 수 있다. 

프로필이 local일 때는 resources-local 디렉토리를 추가로 참조한다.  (local 서버용)

alpha일 때는 resources-alpha를 참조한다. (alpha 서버용)

이클립스에서 보면 아래와 같은 구조

resource-local/image-config.properties에는 로컬의 설정 파일 경로가 저장되어 있다.

resource-alpha/image-config.properties에는 alpha라는 서버의 설정 파일가 저장되어 있다.

이클립스에서 프로필 선택

프로필을 설정하면 프로젝트 우클릭 > Maven > Select Maven Profiles에서 프로필 선택이 가능하다. 

이 프로젝트의 경우 local이 <activeByDefault>true</activeByDefault>로 설정되었기 때문에 자동으로 선택되어 있다.

배포시 프로필 설정

알파라는 서버에 배포할 때는 alpha 프로필을 선택해야 한다. 

mvn package 명령어 실행시 -P 옵션으로 프로필을 지정할 수 있다.

> mvn package -P alpha

Dynamic Web Project 생성

File > New > Dynamic Web Project 선택합니다.

프로젝트 이름 입력 > NextSource folders on build path 에서 기본으로 있는 "src"를 삭제합니다.

그리고 "Add Folder" 버튼으로 아래와 같이 Maven 관례에 따라 디렉토리 구조를 만듭니다.

src/main/java

src/main/resources

src/main/webapp

src/test/java

src/test/resources

프로젝트 속성으로 보면 아래와 같은 모습이 됩니다.


Maven Project로 변환

프로젝트 이름 위에서 오른쪽 마우스 클릭 > Configure > Convert To Maven Project


Group Id, Artifact Id, Version을 입력합니다.

입력을 마치면, 새로운 디렉토리가 몇 개 생기고 pom.xml 파일이 생성됩니다.

pom.xml을 열어보면, <build> 에 2개의 plugin이 존재합니다.

하나는  maven-war-plugin 이고 다른 하나는 maven-compiler-plugin 

이 중에서 우리가 만들 Dynamic Web Project를 war로 패키징 해주는 plugin이  maven-war-plugin 입니다.

지금 글을 쓰는 시점에서는 2.4 버전이 최신입니다.

Maven 프로젝트 구조의 관례에 따라, 모든 웹 관련 리소스 들은 src/main/webapp에 두게합니다. 

그런데 Eclipse에서 Dynamic Web Project를 만들 때에 동일한 역할을 하는 디렉토리가 WebContent입니다.

 WebContet를 그대로 써도 되긴 하지만 Maven 관례를 따르는 것이 좋습니다. 

그래야 나중에 다른 사람이 봐도 이해하기 쉬우니까요.

pom.xml 파일을 다시보면 maven-war-plugin에 <warSourceDirectory>가 있습니다. 

이 값이 현재는 WebContent로 되어 있습니다. 

이것을 src/main/webapp로 변경합니다.

그리고 WebContent 하위의 디렉토리와 파일을 src/main/webapp로 복사하고 WebContent는 삭제합니다.

최종 디렉토리 구조는 아래와 같습니다.


Deployment Assembly 설정

이제 마지막으로 확인해야 할 것이 있습니다.

프로젝트 이름위에서 마우스 오른쪽 클릭 > Properties > Deployment Assembly

WebContent 는 위에서 디렉토리를 삭제했기 때문에, 제거합니다.

m2e-wtp/web-resources 도 삭제합니다.

src/main/webapp는 WEB-INF/classes 로 되어 있는 것을 "/"로 변경합니다. 

그래야만 src/main/webapp 아래있는 리소스들이 war 패키징될 때에 최상위에 있게 됩니다. 

Edit 안된다면 삭제한 후에 "Add..."를 합니다

Maven 실행

먼저 프로젝트를 clean 합니다 (맨 처음 한번 clean을 먼저 실행 해 주는 것이 좋습니다)

프로젝트 이름 위에서 마우스 오른쪽 클릭 > Run As... > Maven clean

프로젝트 이름 위에서 마우스 오른쪽 클릭 > Run As... > Maven install

 

터미널에서 pom.xml이 있는 곳으로 이동하여

mvn clean install

정상적으로 실행되는지 확인합니다.

 

+ Recent posts