Maven(메이븐) 이란?

이 글은 박재성님이 쓰신 자바세상의 빌드를 이끄는 메이븐이라는 책과 메이븐 공식문서를 보고 정리한 글입니다.
책이 있으신분은 메이븐 개념을 한번 머릿속에 정리하고 싶을때 읽으시면 매우 좋습니다. 책에 스토리로 이끌어가는 부분이 있어 매우 재밌게 읽을 수 있습니다.
다만, 책이 절판되어 책을 구하고싶으신 분들은 아마 알라딘이나 다른 곳에서 중고서적으로 구매를 하셔야 합니다.

Maven

메이븐은 자바기반 프로젝트를 빌드하고 관리하기 위한 툴이다.
요즘은 Gradle이 많이 쓰이지만 아직 maven을 사용하고 있는 프로젝트도 많다.
메이븐은 빌드 프로세스를 최대한 쉽게 하는것을 목표로 하고 이 뿐만 아니라 프로젝트에 질높은 정보를 제공하고, 단일 빌드시스템을 제공하는 것을 목표로 한다.

다른 build tool 없이 자바프로젝트를 개발하게 되면 의존성관리 등 신경써야할게 한두가지가 아니다. 메이븐이 이를 도와준다.
메이븐의 장점은 다음과 같다.

  • 편리한 의존관계 관리를 지원한다.
  • 모든 프로젝트가 일관된 프로젝트 디렉토리 구조, 빌드 프로세스를 유지할 수 있다.
  • 다양한 메이븐의 플러그인을 활용할 수 있다.
  • 프로젝트의 template을 만들수있다.

메이븐은 저장소를 지원해서 메이븐만 설치하면 프로젝트 build에 필요한 라이브러리, plugin을 저장소에서 우리의 PC로 자동으로 다운로드한다. 다운로드한 라이브러리들은 특정 디렉터리에 위치하게 되는데 이를 localRepository(로컬저장소)라고 부른다. 기본적으로는 ~/.m2/repository 에 위치하고 settings.xml로 설정을 변경할 수도 있다.
또 메이븐은 처음 생성하는 프로젝트 종류에 따라 기반이 되는 template을 제공한다. 이를 이용해서 메이븐 기반 프로젝트를 생성할 수 있는데, 그러면 프로젝트의 기본적인 뼈대를 자동으로 생성할 수 있다. 메이븐의 이 같은 기능을 archetype이라고 한다.

메이븐 공식문서의 getting started에서 처음 메이븐 프로젝트를 만들게 될때도 archetype을 사용한다.
다음 명령어로 메이븐 프로젝트를 생성해보자.

1
mvn -B archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4

이를 실행하면 my-app 프로젝트의 디렉터리가 만들어지고 그 안에 pom.xml 파일이 생성된다.
메이븐은 source code와 test code를 분리해서 관리하는데 source code는 src/main/java 에 위치하고, test code는 src/test/java 에 위치한다.
여기서 사용한 groupId, artifactId는 뒷부분에서 다루겠다.

메이븐 기본명령어

메이븐 명령어는 다음과 같은 형태를 가진다.
mvn [options] [goal] [phase] 위의 명령어에서도 사용했던 -D 옵션들은 메이븐 설정파일(pom.xml)에 인자를 전달한다.
예를 들어 단위테스트를 실행하지 않으려면 mvn -Dmaven.test.skip=true [<phase>]와 같이 실행할 수 있다. 메이븐에는 phase와 goal 개념이 있는데 이들을 이용하며 빌드를 실행할 수 있고, 빌드를 실행할 때 여러개의 phase와 goal을 실행할 수 있다. 예를들어 다음과 같이 다양한 형태로 실행이 가능하다.
mvn clean test: clean phase와 test phase를 실행한다.
mvn clean compiler:compile: clean phase와 compiler plugin의 compile goal을 실행한다.
phase와 goal은 밑에서 자세히 다루겠다.

Pom.xml

위의 메이븐 archetype:generate 명령어로 메이븐 프로젝트를 생성했으면 pom.xml 파일이 생성된다. POM은 Project Object Model을 의미한다. 그러면 pom.xml의 각 element 들을 살펴본다.

먼저 생성된 pom.xml은 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>

<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>maven-app</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

.. 이하생략 ..

  • project: pom.xml의 최상위 element
  • modelVersion: POM version. 최근버전이 4.0.0이다.
  • artifactId: project를 식별하는 id를 의미한다. groupId 안에서 여러개의 project가 있을 수 있다.
  • groupId: project를 생성하는 조직의 고유 id를 결정한다. 도메인 이름을 많이 사용하는데 꼭 그럴필요는 없다.
    groupId + artifactId는 값이 유일해야한다. 그렇지 않으면 중앙저장소에서 충돌한다.
  • packaging: 어떤 방식으로 패키징할지 결정한다. jar, war 등을 설정가능하다.
  • version: project의 현재 버전을 의미한다. 프로젝트 개발중에는 SNAPSHOT을 suffix로 사용가능하다.
    SNAPSHOT은 maven의 예약어이며 SNAPSHOT을 사용하면 라이브러리를 다른방식으로 관리한다.
  • name: project 이름이다.
  • url: project url이 있다면 이를 기입한다.
  • dependencies: 프로젝트와 의존관계에 있는 라이브러리들을 관리한다.

각 프로젝트의 pom.xml은 기본적으로 최상위 POM이라고 불리는 설정을 상속한다. 그래서 pom.xml의 설정내용이 단순하더라도 메이븐의 기본 규약들을 전부다 따르는 것이 가능하다. 실제 정의된 설정들을 보려면 다음 명령어를 사용하면 된다.

mvn help:effective-pom

설정되어있는 repository 정보를 담고있는 repositories 태그, plugin 설정정보를 담는 태그등 기존 pom.xml에 보이지 않았던 태그들을 볼 수 있다. 이 내용들이 기본적으로 최상위 POM에 존재했기 때문에 우리가 만든 project의 pom.xml은 단순하게 가져갈 수 있다.

Lifecycle

메이븐은 모든 빌드 단위가 이미 정의되어 있으며 이는 개발자가 임의로 변경할 수가 없다. 여기서 말하는 빌드 단위란 compile, test, package, deploy 등을 말한다.
메이븐은 이와같이 미리 정의되어 있는 빌드 순서를 lifecycle이라고 하며 메이븐은 3개의 lifecycle을 제공한다.

  1. compile, test, package, deploy를 담당하는 기본 lifecycle
  2. 빌드 결과물 제거를 위한 clean lifecycle
  3. project document site를 생성하는 site lifecycle이다.

메이븐은 기본적으로 빌드후의 모든 산출물을 target 디렉터리에서 관리한다.

target 디렉터리에 생성되는 하위디렉터리는 다음과 같다.

  • target/classes: src/main/java의 소스코드가 컴파일된 class 파일들과 src/main/resources 디렉터리의 자원이 복사된다.
  • target/test-classes: src/test/java의 소스코드가 컴파일된 class 파일들과 src/test/resources 디렉터리의 자원이 복사된다.
  • target/surefire-reports: report 문서들이 위치한다.

기본 Lifecycle

기본 lifecycle을 활용해 source code를 compile, test 등을 할 수 있는데 각 phase들을 살펴보면 다음과 같다.

  • process-resources: src/main/resources의 모든 자원을 target/classes 로 복사한다.
  • compile: src/main/java에 있는 source code를 compile한다.
  • process-test-resources: src/test/resources의 모든 자원을 target/test-classes 로 복사한다.
  • test-compile: src/test/java에 있는 source code를 compile한다.
  • test: Junit 같은 unit test framework로 test를 진행하고 test가 실패하면 빌드실패로 간주한다.
    결과물을 target/surefire-reports 디렉터리에 생성한다.
  • package: pom.xml의 packaging 값에 따라 압축한다.(jar, war)
  • install: local repository에 압축한 파일을 배포한다.
  • deploy: 원격저장소에 압축한 파일을 배포한다.

이처럼 maven 기본 lifecycle은 여러개의 phase로 구성되어 있으며, 각 phase는 의존관계를 가진다.
process-resources ← compile ← process-test-resources ← test-compile ← test ← package
이 순으로 의존관계를 가지고 있어서 package phase를 실행(mvn package)하면 의존관계에 있는 test phase가 먼저 실행되고, test phase는 compile phase에 의존관계가 있기때문에 compile phase가 먼저 실행된다.
따라서 package phase를 실행하면 process-resources → compile → process-test-resources → test-compile → test → package 순으로 빌드가 진행된다.

process-resources phase는 src/main/resources 에 있는 모든 자원을 test/classes 디렉터리로 복사하는데, 만약 다른 디렉터리에도 자원이 존재한다면 pom.xml에 따로 설정할 수 있다.

package phase는 jar나 war형태로 압축하여 target 디렉터리에 위치시킨다.
<build>/<finalName>에 값이 설정되어 있으면 {finalName}.{packaging} 형태로 압축파일이 생기고,
값이 설정 안되어있다면 {artifactId}-{version}.{packaging} 형태로 된다.

clean phase는 빌드한 결과물들을 제거하는 phase인데 이는 다른 phase와 관련이 없다.
clean phase를 실행하지 않고 다른 phase를 실행할 때 불필요한 산출물들 때문에 오류가 날 수 있으므로 clean을 실행하고 빌드하는 습관을 가지면 좋다.

Clean Lifecycle

clean lifecycle은 빌드를 통해 나온 산출물을 모두 삭제한다.
target directory를 삭제하는 것과 동일하다.

Site Lifecycle

site lifecycle은 사용안하는 경우가 많은데 핵심만 짚고 넘어가자면,
site, site-deploy phase를 사용해 실행가능하다. site lifecycle은 메이븐에 설정되어 있는 기본설정, 플러그인 설정에 따라 target/site directory에 문서 사이트를 생성한다. site-deploy는 이를 배포한다.

Plugin

메이븐에서 제공하는 모든 기능은 plugin을 기반으로 동작한다.
메이븐 phase 또한 메이븐 plugin을 통하여 실질적인 작업이 실행된다. 따라서 phase가 실행되는 과정을 이해하려면 maven plugin을 먼저 이해해야 한다.

사용하고자 하는 maven plugin이 있다면 pom.xml에 다음과 같이 설정한다.

1
2
3
4
5
6
7
8
9
10
11
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
</plugin>
</plugins>
</build>
</project>

이와 같이 사용하고자 하는 plugin의 groupId, artifactId, version을 명시하면 된다.
version을 생략하면 가장 최신버전의 plugin이 설치된다.
메이븐 plugin은 하나의 plugin에서 여러 작업을 실행할 수 있도록 지원하는데 여기서 각각 실행할 수 있는 작업을 goal이라고 정의한다.
위의 compiler plugin은 하나지만 이 플러그인이 지원하는 goal은 compile(source directory의 compile), testCompile(test directory의 compile) 등이 있다.

plugin은 다음과 같이 실행할 수 있다.
mvn groupId:artifactId:version:goal

예를들어 앞의 compiler plugin의 compile goal은 다음과 같이 실행한다.
mvn org.apache.maven.plugins:maven-compiler-plugin:2.1:compile

만약 settings.xml에 pluginGroup이 설정이 되어있다면 groupId를 생략이 가능하고,
version을 생략하면 local repository에 있는 가장 최신 버전의 플러그인을 사용하며,
plugin 이름이 maven-$name-plugin 이나 $name-maven-plugin 형식을 따른다면 $name 값만 명시할 수 있다.
앞에서 실행했던 compile 플러그인을 다음과 같이 실행할 수 있다.
mvn compiler:compile

앞부분에서 실행했던 mvn archetype:generate 명령도 mvn org.apache.maven.plugins:maven-archetype-plugin:generate를 축약한 것이다.

메이븐은 매우 많은 플러그인들을 활용할 수 있는게 큰 장점이다. 다양한 플러그인을 제공하고 있어서 원하는 개발환경을 얼마든지 만들어 나갈 수 있다.

Phase와 Goal

메이븐에서 phase는 build lifecycle에서 각 단계와 순서를 정의하는 개념으로 실제로 빌드작업을 하지는 않는다.
실제 빌드작업은 해당 phase와 연결되어 있는 plugin의 goal에서 진행한다.
mvn compile은 compile phase를 실행한 것인데 이는 compile phase와 연결되어 있는 compiler plugin의 compile goal이 실행되면서 컴파일 작업을 진행한다.
기본 lifecycle에서 phase를 실행할 때 기본으로 연결된 plugin의 goal을 실행하는 구조이다.
기본 lifecycle에서 phase에 연결되어 있는 plugin을 실행할 때에는 자동으로 메이븐 중앙저장소에서 plugin을 다운로드 한다.

phase와 goal과의 관계를 보여주는 그림이다.

phase와 goal의 관계

각 핵심 phase 별로 구체적인 내용을 알아보자.

mvn compile

compile phase를 실행하면 먼저 의존관계에 있는 process-resources phase가 먼저 실행된다. process-resources phase는 src/main/resources 디렉터리에 있는 모든 자원을 target/classes 디렉터리로 복사한다.
만약 src/main/java 안에서도 소스와 같은 패키지로 관리하는 리소스들이 있고 이들또한 target/classes에 복사되기를 원한다면 다음과 같은 설정을 하면된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
<project>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
</project>

그러면 compile phase 실행시 src/main/java에 있는 *.java 파일을 제외한 모든 설정파일을 target/classes 로 복사한다.
resource plugin과 compiler plugin 에 대한 자세한 정보는 다음 공식문서에서 확인할 수 있다.
resources plugin : resources-plugin
compiler plugin : compiler-plugin

mvn test

test phase를 실행하면 process-test-resources phase가 먼저 실행되면서 src/test/resources 디렉터리의 자원복사를 먼저 진행한다.
그리고 test-compile phase에서 src/test/java 디렉터리의 test code들을 컴파일한다.
test phase는 target/test-classes 에 컴파일한 단위 테스트 클래스를 실행하고 그 결과물을 target/surefire-reports 디렉터리에 생성한다.
기본적으로 test phase는 target/test-classes 에 있는 모든 단위 테스트 클래스를 실행하는데 특정 테스트 suite 별로 실행할 필요가 있다면 test option을 사용할 수 있다.

mvn -Dtest=MyUnitTest test

이와 같이 특정 테스트 클래스만 실행할 수 있고 여러개의 test 클래스 들을 실행하고 싶다면 쉼표로 여러개를 정의하면된다.

mvn package

package phase는 compile, test-compile, test, package 순으로 실행된 후 jar, war 파일이 target 디렉터리 하위에 생성된다.
<build>/<fileName> 에 값이 설정되어 있고 jar로 패키징을 하게되면 {finalName}.jar 형태로 jar 파일이 생성된다. 만약 finalName element가 설정되어 있지 않다면 {artifactId}-{version}.{packaging} 이 압축파일 그리고 디렉터리 이름이 된다.
예를들어, finalName element가 설정되어있지 않고 pom.xml 설정이 다음과 같다면

1
2
3
4
5
6
<project>
<groupId>io.github.tk-one</groupId>
<artifactId>myapp</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
</project>

파일은 다음 위치에 생성된다.
target/myapp-1.0-SNAPSHOT/myapp-1.0-SNAPSHOT.war

mvn install

install phase는 package phase와 의존관계에 있기때문에 package phase를 먼저 실행한다. package phase에서 jar or war 파일로 압축을 완료하면 이를 local repository에 배포한다.

mvn deploy

deploy phase는 jar or war 파일을 원격저장소에 등록한다.


라이브러리 의존관계

메이븐은 의존관계에 있는 라이브러리를 관리하기 위해 의존 라이브러리 관리기능을 제공한다. 이는 메이븐의 lifecycle과 더불어 메이븐의 핵심기능이기 때문에 반드시 이해하는게 좋다.

메이븐 저장소는 로컬저장소와 원격저장소로 나뉜다.

로컬저장소

로컬저장소는 개발자 PC에 있는 저장소로 메이븐을 빌드할때 다운로드하는 라이브러리나 플러그인을 관리 및 저장한다.
로컬저장소는 기본값으로는 ~/.m2/repository 에 위치한다.

원격저장소

원격저장소는 외부에 위치하는 저장소로 사내에서 사용하는 저장소도 있고 중앙저장소라고 불리는 오픈소스 라이브러리나, 메이븐 플러그인 등을 저장하고 있는 저장소도 있다. 중앙저장소는 원격저장소 중 하나라고 생각하면 된다.

메이븐은 빌드를 할때 로컬저장소에 이미 다운로드한 라이브러리가 있으면 원격저장소에서 다운로드 하지않고 로컬저장소에 있는 라이브러리르 사용한다. 메이븐이 다운로드 하고자 하는 저장소는 repositories 태그로 설정할 수 있다.
기본적으로 우리가 repositories 태그로 설정을 안하고 다운로드할 수 있는 이유는 최상위 POM에 이미 정의가 되어있기 때문이다.

1
2
3
4
5
6
7
8
9
<project>
<repositories>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>https://repo.maven.apache.org/maven2</url>
</repository>
</repositories>
</project>

여러개의 repository들을 추가할 수 있는데 그러면 메이븐은 라이브러리를 다운로드할때 repositories 태그에 있는 저장소 순서대로 다운로드를 시도한다.

위에서 생성한 myapp에서는 다음과 같이 dependencies 태그로 라이브러리를 관리한다.

1
2
3
4
5
6
7
8
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>

이와 같이 설정하고 빌드하면 메이븐은 먼저 로컬저장소에 해당 라이브러리가 있는지 확인한다.
없다면 메이븐은 중앙저장소에서 junit 4.11 버전이 있는지 확인하고 있다면 jar 파일을 로컬저장소에 다운로드한다.
중앙저장소에 해당 라이브러리와 버전이 존재하는지 확인할 때에는 위에 repository 설정에 적힌 url을 바라보고
https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar 파일이 있는지를 파악한다.
이와 같이 설정하고 빌드하면 메이븐은 중앙저장소에서 junit 4.11 버전의 jar 파일을 로컬저장소에 다운로드한다.
로컬저장소에 다운로드 받는 위치는 기본적으로 다음과 같다.
~/.m2/repository/junit/junit/4.11/junit-4.11.jar

그리고 메이븐은 로컬저장소에 다운로드한 라이브러리를 활용해 src/main/java 그리고 src/test/java 에 있는 source code들을 컴파일한다.

version을 LATEST 혹은 RELEASE로 설정할 수도 있는데 그러면 항상 가장 최신버전의 라이브러리와 의존관계를 갖게된다.
또, 한번 로컬저장소에 다운로드한 라이브러리는 다시 원격저장소에서 다운로드하지 않는데 이부분에서 애플리케이션이 개발단계에 있어 코드가 지속적으로 변경되는 상황이라면 SNAPSHOT을 활용하자.
version 정보에 SNAPSHOT을 포함하게되면 빌드할 때마다 가장 최근에 배포한 라이브러리가 있는지 확인하고 로컬저장소에 있는것보다 최신일경우 이를 다운로드한다.

scope

메이븐에서는 사용하는 라이브러리의 성격에 따라 scope를 지정할 수 있다.
JUnit 라이브러리의 경우 실제 배포할때는 필요없고 테스트를 진행할때만 필요하다. 이런경우 scope를 test로 주면된다.

scope는 6가지 종류가 있다.

  • compile: default scope이다. compile 및 deploy시 같이 제공해야하는 라이브러리이다.
  • provided: compile 시점엔 필요하지만 deploy에 포함할 필요는 없는경우 사용한다.
  • runtime: compile에는 필요없지만 runtime에는 필요한 경우 사용한다.
  • test: test 시점에만 사용하는 라이브러리에 설정한다.
  • system: provided scope와 비슷한데 로컬저장소에서 관리되는 jar파일이 아닌 우리가 직접 jar 파일을 제공해야한다.
  • import: 다른 pom.xml 에 정의되어있는 의존관계설정을 가져온다.

Dependency Mechanism

Dependency Mechanism은 메이븐의 핵심중 하나이다.
그러므로 메이븐이 어떻게 의존성을 관리하는지는 꼭 이해하고 넘어가는게 좋다.

Dependency Transitive(의존성 전이)

프로젝트에서 의존하는 라이브러리들의 숫자는 제한이 없지만 의존성 cycle이 있으면 문제가 발생한다.
프로젝트에 외부 라이브러리를 하나씩 추가할때마다 그 라이브러리가 또 의존하고있는 라이브러리를 또 추가해야 하므로 의존관계에 있는 라이브러리 숫자가 증가한다.
예를들어, project A가 B, C에 의존하고있다면 B가 의존하고있는 D, E, F 라이브러리가 또 필요할 것이고 또 D 가 의존하는 G 라이브러리도 필요할 것이다. 연쇄작용으로 의존하는 프로젝트는 점점 커진다.
참고로 메이븐은 의존성이 있는 라이브러리가 또 어떤 라이브러리에 의존성을 가지고있는지 알기위해 jar 파일을 다운로드 하는 동시에 해당 라이브러리의 pom파일도 같이 다운로드 한다.
메이븐은 위처럼 프로젝트 라이브러리 숫자가 급격히 증가하는 문제점을 해결하기 위해 라이브러리 제한이 가능하도록 의존성 전이 설정을 지원한다.

  • Dependency mediation: 같은 의존성의 여러버전을 마주치게 되었을때 artifact의 어떤 버전을 사용할지 결정한다.
    메이븐은 이때 더 가까운 의존관계에 있는 버전의 의존관계를 선택한다.
    예를들어 다음과 같은 의존성이 있다고 가정한다.

    1
    2
    3
    4
    5
    6
      A
    ├── B
    │ └── C
    │ └── D 2.0
    └── E
    └── D 1.0

    이 예에서는 A를 build할때 D 1.0이 사용된다. 왜냐하면 A -> B -> C -> D 2.0 보다 A -> E -> D 1.0 이 더 가깝기 때문이다.
    여기서 서로 depth 가 같은 상황이라면 먼저 명시된 라이브러리의 버전이 사용된다.
    만약 project A의 pom.xml에 직접 version을 적어주면 그 버전을 사용한다.

    1
    2
    3
    4
    5
    6
    7
    8
      A
    ├── B
    │ └── C
    │ └── D 2.0
    ├── E
    │ └── D 1.0

    └── D 2.0

    이처럼 A에 직접 D 2.0 의 의존성을 추가하면 D 2.0 을 사용한다.

  • Dependency management: 메이븐의 <dependencyManagement> element로 의존관계에 있는 artifact의 버전을 직접 명시랄 수 있다.

  • Dependency scope: 현재 빌드상태에 맞는 라이브러리만 의존관계를 포함한다.
    즉, test scope를 가지는 경우 최종 배포산출물을 빌드하는 시점에는 포함되지 않는다.
  • Excluded dependencies: 만약 A -> B -> C와 같이 의존성이 있으면 project A에서 명시적으로 project C에 대한 의존성을 <exclusion>태그를 사용해 명시적으로 제외시킬 수 있다.
  • Optional dependencies: 만약 A -> B -> C와 같이 의존성이 있고 project B에 C가 optional로 설정이 되어있으면 project A를 빌드할때 project C에 대한 의존관계를 가지지 않는다.

메이븐의 Dependency Transitive가 의존관계를 최대한 잘 설정해 주겠지만 pom.xml에 항상 라이브러리의 명확한 version을 명시하는게 좋다.
현재 프로젝트에서 의존하고있는 라이브러리의 tree를 보고싶다면 다음 plugin의 goal을 사용하면 좋다.
mvn dependency:tree

Property

pom.xml에서 발생하는 중복설정은 속성(property)를 정의하여 개선할 수 있다.
보통 공통된 버전관리에 많이 사용하고는 하는데 예제를 보면 바로 이해할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
<project>
<properties>
<spring.version>3.0.1.RELEASE</spring.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}></version>
</dependency>
</dependencies>
</project>

속성은 <properties> element에서 <property.name>value</property.name> 형태로 정의한다.
그리고 이렇게 정의한 내용은 pom.xml 파일내에서 ${property.name} 으로 접근할 수 있다.