sitelink1 http://wiki.javajigi.net/pages/viewpage.action?pageId=277 
sitelink2  
sitelink3  

 

Maven 툴을 이용한 효율적인 프로젝트 관리 방안

Table of Contents

What is Maven?

Maven is a software project management and comprehension tool.
Based on the concept of a project object model (POM), Maven can manage a project's build,
reporting and documentation from a central piece of information.

위 원문은 Jakarta Project 인 Maven Home에 쓰여 있는 소개로 Maven은 프로젝트를 효율적으로 관리,
혹은 레포팅, 문서화를 중앙 집중적으로 관리하게 해주는 POM 기반의 Project build 툴이라고 소개를 하고 있다.
Project Object Model (POM) 에 대한 자세한 설명은 밑에서 자세하게 설명하겠다.

Ant가 자바 프로그램 빌드 툴로서 사실상 표준으로 자리잡았지만, 프로젝트 관리 기능에 대해서는 아직 부족하다. 이와 반대로, 아파치 자카르타 프로젝트에서 생성된 고수준의 프로젝트 관리 툴인 Maven은 Ant가 제공하는 것 이외에 더 많은 것을 제공한다. 자바 개발자인 Charles Chan은 다음에서 Maven의 기능을 소개하고 설치를 위한 완벽한 가이드를 제공한다.
요즘, 대부분의 개발자들은 Ant가 자바 프로그래밍 프로젝트에서 표준 빌드 툴이라는 것을 인식하고 있다. 불행히도, make의 대체용으로서, Ant의 프로젝트 관리 기능은 대부분의 개발자 요구에는 부족한 감이 있다. Ant 빌드 파일을 예를들어 보면, 프로젝트의 종속(dependency) 관계와 개발자/소유자, 버젼, 사이트 홈페이지와 같은 메타 정보를 발견하기 힘들다.

Maven은 프로그램 빌드 기능 이외에도, Ant에는 없는 고수준의 프로젝트 관리 툴을 제공한다. Maven의 기본 빌드 법칙(rule)은 재사용성이 매우 높기 때문에, 두세줄의 Mavan 빌드 스크립트로도 간단히 프로젝트를 빌드할 수 있다. 수십줄의 Ant와 비교해 보라. 사실상 많은 아파치 자카르타 프로젝트들이 현재 Maven을 사용하고 있으며, Maven의 프로젝트 중심 접근때문에 기업 프로젝트에 적용하는 비율이 점차 증가되고 있다.

----------------------------------------------------------------------------------------------
대략 Maven의 등장배경에 대해서 설명하고 있지만 여기까지 읽은 상태에선 이것만큼은 알아둬야 한다.
Ant와 Maven은 빌드 툴이다. 하지만 Ant는 Project적인 관점에서 부족하다는 점 즉, Maven은 Ant의 장점을 모두 갖고 있으면서 Project 관리 측면에서 훨씬 더 효율적이고 reusability 하다는 것이다. 즉, 고로 Maven이 더 좋다는 이야기 겠지요..

Maven 의 설치

Maven은 Maven홈에서 구할수 있다.
Maven Download

 

 

Maven을 다운 받고 다음과 같이 설치한다. 필자는 Windows Installer를 사용하였다.

 

 

설치 후, 매번 그랬듯이 환경변수에 %MAVEN_HOME%과 %MAVEN_HOME%/bin을 PATH로 잡는다.
당연히 %JAVA_HOME%은 잡혀있어야 겠죠?

커맨드 창에서 maven -v 를 실행해서 다음과 같은 화면이 뜨면 제대로 설치가 된 것이다.

 

 

Maven Simple Project

우리는 지금 Maven에 대해서 아무것도 모른다. 하지만 일단 무조건 따라하기 식의 무모함이 필요할 때도 있다.
일단 필자를 따라해보자 처음부터 아무리 어려운 용어를 써가며 해봐야 이해가 안될 것이다. 물론 독자들을 무시하는 것은 절대 아니다.

1. Project 디렉토리를 생성한다.

필자는 다음과 같은 곳에 생성하였다. D:Projectmavenmaven_first

2. project.xml 정의

프로젝트 객체 모델(Project Object Model : POM)은 프로젝트의 여러가지 상황을 묘사한다.
이에 해당하는 project descriptor가 project.xml 이다.

sample project에서 사용할 project.xml 이다.

<?xml version="1.0" encoding="EUC-KR" ?><project>	<!-- POM 버젼. -->    <pomVersion>1</pomVersion>        <!-- 유일한 프로젝트 ID. 프로젝트 ID와 버젼 번호는 빌드시에 파일과       디렉토리 이름 생성시에 사용된다. 예를들어, 프로젝트 JAR 파일은 <id>-<version>       이름 규칙을 따른다. -->    <id>Porter_Fist_Maven</id>         <!-- 프로젝트 그룹 ID. 존재하면, ID는 저장소에서의 프로젝트 디렉토리 이름 역할을 한다. -->    <groupId>Porter_Fist_Maven Group</groupId>        <!-- 프로젝트 버젼 번호. Maven은 버젼 넘버링 규칙에 제한이 없다. -->     <currentVersion>1.0</currentVersion>        <!-- 프로젝트의 짧은 이름 -->    <name>Porter First Maven Project</name>         <!-- 프로젝트를 보유한 소속에 대한 자세한 정보. name 정보만 필수이다. -->    <organization>         <name>javajigi Open Source Study Group</name>        <url>http://www.javajigi.net/</url>    	<logo>http://www.javajigi.net/opensource_study-logo.gif</logo>    </organization>        <!-- (OPTIONAL) 프로젝트 메인 패키지 이름 -->  	<package>porter.maven.first.*</package>		<!-- (OPTIONAL) 프로젝트 배포 디렉토리 (물리적 위치) -->     <distributionDirectory>    /maven_first/repository/${pom.artifactId}/    </distributionDirectory>        <!--  개발자 정보 -->    <developers>    	<developer>    		<name>Young Jo Jang</name>    		<id>foresight</id>    		<email>porter@enet.co.kr</email>    		<organization>javajigi OpenSource Study Group</organization>    		<roles>    			<role>Documentation</role>    			<role>Java Developer</role>    		</roles>    	</developer>    </developers>        <dependencies> 	<!-- 이 프로젝트는 Maven 저장소 spring/jars 서브 디렉토리에 있는          "spring-1.0.2.jar" 파일에 종속되어 있다.          (저장소에 대해서는 나중에 더 자세히 알아보겠다) -->    <dependency>       <id>spring</id>      <version>1.0.2</version>    </dependency>  </dependencies>    <build>      <sourceDirectory>${basedir}/src</sourceDirectory>      <unitTestSourceDirectory>${basedir}/test</unitTestSourceDirectory>      <unitTest>         <includes>            <include>*Test.java</include>         </includes>      </unitTest>               <resources/>    </build></project>

3. Maven의 실행

command 창에서 해당 디렉토리(D:Projectmavenmaven_first) 로 이동한 후, maven을 실행한다.

 

여기서 아주 희한한 일이 벌어지고 있다는 사실을 알고 있어야 한다.
<dependency> element에서 정의한 spring 1.0.2.jar 가 어디선가 다운로드를 실행하고 로컬로 배치되고 있음을 말이다.
이일은 도대체 무슨일인가.

일단 ${user.home}/.maven 디렉토리가 있음을 확인해 보자.
필자 컴퓨터는 C:Documents and SettingsSONY.maven 에 해당한다.

 

 

위 그림을 보면 C:Documents and SettingsSONY.mavenrepositoryspringjars 디렉토리에 spring-1.0.2.jar 파일이 존재함을 알 수 있다.

저장소 즉, repository 는 Maven의 또다른 중요 컴포넌트이다. 자바기반의 여러 프로젝트를 수행하는 사이트에서는, 서드 파티 라이브러리에 대한 중앙 저장소가 프로젝트간의 일관성을 제공하곤 한다. Maven은 저장소의 구조를 표준화하였고 인터넷이든 인트라넷이든 원격 저장소를 제공한다.

의문사항 C:Documents and SettingsSONY.maven 에 있는 cache는 모냐 ????

저장소(repository)의 표준 저장 구조를 보자.

repository|-- spring                   <-- 프로젝트 그룹 ID -->|   `-- jars                 <-- artifact 타입, 뒤에 's'가 붙는다. jars, wars, ears -->|       `-- spring-1.0.2.jar <-- 실제적인 artifact -->...

원격 저장소를 만들기 위해서는, 저장소 디렉토리를 웹 사이트에 배치만 하면 된다. Maven은 저장소를 중앙 제어할 수 있도록 원격 저장소를 사용하기를 권하고 있고 이를 이용해 프로젝트간 리소스 공유를 최대한 가능토록 할 수 있다. 빌드때마다 파일을 다운로드 하지 않기 위해, Maven은 처음 다운로드 할 때, 로컬 저장소에 필요한 종속 라이브러리들을 자동으로 캐쉬한다.

---------------------------------------------------------------------------------------------------

여기까지 이해를 하셨는지 전 맨 처음에 이 부분이 이해가 잘 가지 않았다. 근데 이거 하나만 잘 생각해보라..project에서 쓰이는 library들이 중앙의 특정
repository에서 관리되고 maven 실행시 캐쉬 영역에 존재하면 그것을 이용하고 그렇지 않으면 중앙에서 관리되는 repository에 있는 것을 다운로드 해서 사용한다.
대단하지 않은가???
근데 여기서 궁금한 점이 하나 생긴다. 도대체 나는 어떠한 원격 repository 설정을 하지 않았음에도 불구하고 과연 spring-1.0.2.jar 는 어디서 다운을 받았냐는 것이다.

문서에는 http://www.ibiblio.org/maven가 기본으로 원격 repository로 쓰이고 있다. 물론 이것 또한 수정해서 우리 프로젝트에 맞는 것으로 사용 가능하다. 당연한거 아닌가..

project.xml에 project.properties에 다음과 같은 properties를 정의하는데 이것이 repository관련 property들이다.

속성 내용
maven.repo.remote URL들의 콤마 구분 리스트를 이용해 원격 저장소를 표시한다; http://www.ibiblio.org/maven가 기본으로 사용된다.
maven.proxy.host, maven.proxy.port, maven.proxy.username, maven.proxy.password 파이어월 내에 있어서 인터넷에 접근하기 위해 프락시 인증이 필요할 때, 이 세팅값들을 다루게 된다.
maven.repo.local 다운로드된 종속 라이브러리들이 캐쉬되는 장소를 표현한다. ${MAVEN_HOME}/repository가 기본으로 제공된다. 유닉스 환경에서, 저장소 디렉토리를 여러 팀과 공유할 때는, 개발자들을 위한 특별한 그룹을 생성하고 저장소 디렉토리에 읽기/쓰기 권한을 할당해야 한다.

이것은 Maven의 가장 주요한 concept 인 project 개념과 더불어 너무나도 중요한 repository 개념이다.

중요하다는 것은 해당 원문에서 알 수 있다. 알아서 해석하기길...

Maven basics
The basic concept of Maven is a project. In Maven terms, any directory that has a project.xml in it is a project.
When the sub-directories underneath have their own project.xml, they are projects on their own too.
Another concept in Maven is that of a repository. The repository holds the artifacts on which your project depends.
There are two kinds of repository: local and remote. Both of them can be declaratively set.
Unless specified otherwise, the local repository is created in a special directory called ".maven/repository".
In Windows, this directory is created in C:Documents And Settings.
For example, if your project depends on commons-logging version 1.0.2, you can specify the dependency in project.xml and when maven is executed,
it will copy the appropriate commons-logging jar file from the remote repository to the local repository and then use it to build your project's artifact.
The maven repository folder has subfolders for each library. For instance, there is a sub folder for commons-logging. Beneath the commons-logging folder there is another subfolder called jars.
This jars folder has the commons logging jar files suffixed by version number. Jars are not the only type of artifacts supported in the repository. You can have EARs and WARs too.

메이븐(마빈?)의 기본 개념은 프로젝트다. 메이븐에서는 project.xml을 가진 모든 디렉토리가 하나의 프로젝트다.
만약 그 아래의 하위 디렉토리에 고유의 project.xml이 있다면 그 디렉토리도 하나의 프로젝트가 된다.
메이븐의 또 다른 중요한 개념은 저장소(repository)이며, 이 저장소에는 여러분의 프로젝트가 의존하는 요소들이 들어있다.
저장소는 로컬과 원격의 두 가지 종류가 있으며 이 두가지 모두 선언적으로 집합(set)이 될 수 있다.
특정 위치를 지정해주지 않으면 로컬 저장소는 ".maven/repository"라는 이름을 가진 폴더로 생성된다.
윈도우에서는 이 디렉토리가 C:Documents And Settings 에 생성된다.
예를 들어 여러분의 프로젝트에 1.0.2버전의 commons-logging jar 파일이 필요하다면 이런 종속 관계를 project.xml에 명기한다.
그러면 메이븐이 실행될 때 원격 저장소에서 알맞은 commons-logging jar 파일을 로컬 저장소로 다운로드한 다음, 프로젝트를 빌드할 때 사용하게 된다.
메이븐 저장소 폴더에는 각 라이브러마다 하위 폴더가 있다. 만약 commons-logging을 위한 하위 폴더가 있다면
그 아래에 jars 라는 이름을 가진 또 다른 하위 폴더가 존재하는 식이다.
이 jars 폴더 안에 버전 넘버가 붙은 commons logging jar 파일들이 있다.
메이븐 저장소에는 jar 형식뿐 아니라 EAR이나 WAR 형식 파일도 사용할 수 있다.
? 마빈 이라고 했던가요? ㅡㅡㅋ (정명진 2005.7.4)

4. Maven의 compile

해당 디렉토리 (D:Projectmavenmaven_first) src 디렉토리를 생성하고 HelloWorld.java 소스를 위치 시키자.

package porter.maven.first;/** * @author Porter * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */public class HelloWorld{	 public String sayHello(){	  return "HelloWorld";	 }	 public static void main(String args[]){	  new HelloWorld();	 }	}

command 창에서 maven java:compile 을 실행한다.

 

 

컴파일 명령 maven java:compile 에 컴파일 할 타겟 파일 이름이 들어가지 않음에 주목한다.
maven의 특징중 하나인 빌드의 단순성을 보여주는 부분이다.
컴파일러는 target이라는 폴더를 만들고 하부에 컴파일한 클래스를 위치시켰다.

의문사항 그럼 컴파일 할 타겟 파일이름을 지정해 줄 수 있는가 ????

4. Maven의 Test 기능

위 project.xml 의 <unitTestSourceDirectory> element를 보게 되면 unit test 관련한 작업이 있음을 알 수 있다.
해당 디렉토리에서 maven test 를 실행한다.

 

 

아니나 다를까 프로젝트에서 필요한 library 들을 remote repository로 부터 local로 다운받는 일이 일어난다.

maven test:ui 를 실행한다.

 

 

Maven의 구조

지금 까지 Simple Project를 통해서 아주 간단한 maven의 기능을 보아왔다 이제 본격적으로 Maven이란 놈에 대해서 디벼 보겠다.
일단 Maven의 구조를 도식화한 그림이다. 아주 중요한 그림이다.

 

 

보통 위와 같은 그림을 보면 나 같은 경우는 당황하고 쫄게 된다. 모야 이거 대체.. 어쩌라고..
하지만 이제부터 이 놈들을 디벼 보도록 하자.
우리가 연구 할 것은 이 그림을 바탕으로 할 것이다.

위 Simple Project 를 통해서 이미 Project Object Model - POM (project.xml)과 Repository(local or remote) default remote repository를 이용해서
이미 다루었다.
위 그림에서 회색 사각형으로 된 부분은 사용자가 직접 작성하고 customizing 할 수 있는 영역이라고 본다.
그렇다면 다루지 않은 것은 Define Goals 와 plugins 들이다. 이것들은 대략 Maven의 대략 아주 중요한 요소라는 것을 여러분들은 눈치를 채야 할 것이다.

Project Object Model - POM

우선 자꾸 Project Object Model 이라고 해서 POM, POM 하고 떠드는데 이것부터 디벼 보겠다.

The Project Object Model, almost always referred as the POM for brevity, is the metadata that Maven needs to work with your project. Its name is "project.xml" and it is located in the root directory of each project.

----------------------------------------------------------------------
위 원문은 maven 홈의 glossary(용어집)에서 POM 에 대한 글이다.
Project Object Model은 project에서 필요한 metadata를 간결하게 기술하고 그것은 project.xml 이라는 project descriptor에 기술한다고 한다.
이 project.xml 파일은 각각의 프로젝트의 root directory에 위치하게 된다는 대략적 설명이다.

이렇게 생각해보자 위에서 했던 simple project 에서의 boundary를 한 project라고 보고 이것을 project.xml에 기술 한 것이다.
그럼 이것이 POM ???

project.xml details

project.xml은 4개의 main part로 구성되어 있다. 이 4개의 구성파트는 다음과 같다.

  1. Project Management Section
  2. Project Dependency Section
  3. Project Build Section
  4. Project Reports Section

project.xml outline

<?xml version="1.0" encoding="EUC-KR" ?> <project>   <pomVersion>3</pomVersion>   <groupId>Sample-Maven-Project</groupId>   <id>sample-project</id>   <currentVersion>1.1</currentVersion>   <name>Sample Maven Project</name>   <!--         Project Management section  goes here        -->   <!--         Project Dependency section  goes here        -->   <!--         Project Build section goes here           -->   <!--         Project Reports section goes here          --> </project>

Line 02 - project.xml 의 root element 이다.
Line 03 - Project Object Model (POM) Version. 이태그는 현재 쓰이지 않는다고 한다.
Line 04 - 프로젝트 그룹 ID. 존재하면, ID는 저장소에서의 프로젝트 디렉토리 이름 역할을 한다. --> 이게 뭔소리냐??
Line 05, 06 - [id][version]은 최종 artifact 생성시 [id]-[version].jar 가 된다.
Line 07 - 프로젝트 명이다.

자, 그럼 section 별로 정리를 해보자.

Project Management Section in project.xml

다음은 Project Management Section 의 설명이다.
general information on the organization, its web site, project web site, location of SCM, deployment and issue tracking site, developer list, mailing lists to name a few

<organization>      <name>Foobar Travels</name>      <url>http://www.foobar.com</url>      <logo>http://www.foobar.com/logo.jpg</logo>   </organization>   <inceptionYear>2003</inceptionYear>   <package>foobar.blah.*</package>   <logo>http://www.foobar.com/project-logo.jpg</logo>          |   <description>Project description goes here</description>   <shortDescription>Short Description</shortDescription>   <url>http://www.foobar.com</url>   <issueTrackingUrl>http://jira.foobar.com</issueTrackingUrl>   <siteAddress>http://staging.foobar.com</siteAddress>   <siteDirectory>/etc/staging</siteDirectory>   <distributionDirectory>/etc/builds</distributionDirectory>   <repository>      <connection>cvs:pserver:anon@foobar.com:/foo</connection>      <url>http://scm.foobar.com</url>   </repository>   <mailingLists>      <mailingList>         <name>Dev List</name>         <subscribe>subscribe-dev@foobar.com</subscribe>         <unsubscribe>unsubscribe-dev@foobar.com</unsubscribe>      </mailingList>      ...      ...   </mailingLists>   <developers>      <developer>         <name>Srikanth Shenoy</name>         <id>shenoy</id>         <email>srikanth@srikanth.org</email>      </developer>      ...      ...   </developers>

Lines 01-05 - Organization details
Line 08 - Top level package for the project
Line 09 - Project Logo
Line 12 - Project web site
Line 14 - The site where the project is hosted
Line 15 - Physical location of project deployment
Line 16 - Physical location where the project distributions are available
Lines 18-21 - SCM to access the project source
Lines 23-31 - Mailing list for the project
Lines 33-41 - Developers in the project

Project Build Section in project.xml

Project Build Section 은 source와 test 코드 resource file 들을 descripbe 하는 section 이다.

<build>   <nagEmailAddress>srikanth@srikanth.org</nagEmailAddress>   <sourceDirectory>${basedir}/src/java</sourceDirectory>   <unitTestSourceDirectory>${basedir}/test/java</unitTestSourceDirectory>   <unitTest>      <includes>         <include>**/*Test.java</include>      </includes>   </unitTest>   <resources>      <resource>         <directory>${basedir}/src/conf</directory>         <includes>            <include>*.properties</include>         </includes>      </resource>   </resources> </build>

Line 02 - Email address to send notification about the build status
Line 03 - Folder containing the source files for the project. The source can be java, jsp and so on.
Line 04 - Directory containing the unit test files for the project.
Lines 05-09 - The test file name pattern to run after the build is completed
Lines 11-19 - Resources to be copied in case a jar is created.

Project Reports Section in project.xml

Build 작업이 이루어지면 다양한 형태의 report와 document를 생성하는 Project Reports Section은 다양
한 형태의 report generate가 가능하도록 지원한다.

<reports>   <report>maven-changes-plugin</report>   <report>maven-jdepend-plugin</report>   <report>maven-checkstyle-plugin</report>   <report>maven-pmd-plugin</report>   <report>maven-junit-report-plugin</report>   <report>maven-clover-plugin</report>   <report>maven-changelog-plugin</report>   <report>maven-file-activity-plugin</report>   <report>maven-developer-activity-plugin</report>   <report>maven-file-activity-plugin</report>   <report>maven-license-plugin</report>   <report>maven-linkcheck-plugin</report>   <report>maven-jxr-plugin</report></reports>

Project dependency Section in project.xml

Project dependency section은 종속관계를 나타내는 것으로 앞에서 배웠던 repository 개념이 바로 이것이다.
repository는 ./maven/repository에 위치하게 된다.

<dependencies>      <dependency>        <groupId>BeanUtils</groupId>        <artifactId>commons-beanutils</artifactId>        <version>1.5</version>      </dependency>      <dependency>        <groupId>commons-logging</groupId>        <artifactId>commons-logging</artifactId>        <version>1.0.3</version>      </dependency>      <dependency>        <groupId>castor</groupId>        <artifactId>castor</artifactId>        <version>0.9.4.3</version>      </dependency></dependencies>

Plugins and Goals

우리는 여태껏 지겹도록 POM 이란것에 대해서 배웠다. 이제 위 그림에 있는 plugins와 goals 에 대해서 디벼보도록 하겠다.
우리는 위의 Sample Proeject 를 통해서 몇가지 명령어를 사용했던 것을 기억해야 한다.
source compile 시에 command 창에서 실행했던 command 창에서 maven java:compile 을 말이다.

이 명령어를 사용했을때의 상황을 기억을 더듬어 보자..당근 source compile이 되었고 지정하지도 않았던 target class가 만들어지면서 말이다.

maven에선 java를 plugin으로 compile을 goal이라고 칭한다. 즉,

<plugin name>:<goal name>

이 되는 셈이다.
다시 말하면 java라는 plugin이 가지고 있는 compile이라는 goal을 실행하라라고 해석하면 되는 것이다.
그럼 이 plugin은 %MAVEN_HOME% 에 plugins 디렉토리에 jar형태로 배포되고 있고.../maven/cache 디렉토리를 보면 plugin들을 볼수 있게 된다.

 

그럼 여기서 의문을 가져야 한다. ./maven/cache 디렉토리는 jar형태의 maven plugin 들이 풀려진 형태가 저장되는 곳인가?
답은 '그렇다' 이다. 왜냐고??? 현 시스템에서 쓰이고 있는 plugins들을 공유하기 위함 이겠다.
어디서든 maven 명령을 사용할때 저 plugin을 사용할 수 있게 말이다.

./maven/cache 디렉토리에서 java:compile 을 찾아보겠다.

필자의 컴퓨터는 C:Documents and SettingsSONY.mavencachemaven-java-plugin-1.5 여기에 해당된다.
cache 디렉토리 밑에 이름만 봐도 아 이것이 java plugin이라는 것을 알 수 있을 것이다.
이곳에서 plugin.jelly라는 파일을 열어보자.

 

 

다음은 plugin.jelly에서 java:compile에 해당하는 것만 발췌한 것이다.

<goal name="java:compile"        description="Compile the project"        prereqs="java:prepare-filesystem">    <ant:echo>Compiling to ${maven.build.dest}</ant:echo>    <j:choose>      <j:when test="${sourcesPresent == 'true'}">        <j:if test="${context.getVariable('maven.compile.target') == null}">          <ant:echo>==========================================================  NOTE: Targetting JVM ${java.specification.version}, classes  will not run on earlier JVMs==========================================================          </ant:echo>        </j:if>      	<ant:javac          destdir="${maven.build.dest}"          excludes="**/package.html"          debug="${maven.compile.debug}"          deprecation="${maven.compile.deprecation}"          optimize="${maven.compile.optimize}">          <ant:src>            <ant:path refid="maven.compile.src.set"/>          </ant:src>          <!--           |            | Source Modifications.           |          -->                <j:forEach var="sm" items="${pom.build.sourceModifications}">            <ant:available property="classPresent" classname="${sm.className}"/>            <j:if test="${classPresent != 'true'}">              <j:forEach var="exclude" items="${sm.excludes}">                <ant:exclude name="${exclude}"/>              </j:forEach>              <j:forEach var="include" items="${sm.includes}">                <ant:include name="${include}"/>              </j:forEach>            </j:if>          </j:forEach>                <j:if test="${context.getVariable('maven.compile.bootclasspath') != null}">              <ant:bootclasspath>                <ant:pathelement path="${maven.compile.bootclasspath}"/>              </ant:bootclasspath>          </j:if>          <j:if test="${context.getVariable('maven.compile.extdirs') != null}">              <ant:bootclasspath>                <ant:pathelement path="${maven.compile.extdirs}"/>              </ant:bootclasspath>          </j:if>          <ant:classpath>            <ant:path refid="maven.dependency.classpath"/>            <ant:pathelement path="${maven.build.dest}"/>          </ant:classpath>          <j:if test="${context.getVariable('maven.compile.compilerargs') != null}">            <ant:compilerarg line="${maven.compile.compilerargs}" />          </j:if>                    <j:if test="${context.getVariable('maven.compile.encoding') != null}">            <ant:setProperty name="encoding" value="${maven.compile.encoding}" />          </j:if>                    <j:if test="${context.getVariable('maven.compile.executable') != null}">            <ant:setProperty name="executable" value="${maven.compile.executable}" />          </j:if>                    <j:if test="${context.getVariable('maven.compile.fork') != null}">            <ant:setProperty name="fork" value="${maven.compile.fork}" />            <j:if test="${context.getVariable('maven.compile.memoryInitialSize') != null}">              <ant:setProperty name="memoryInitialSize" value="${maven.compile.memoryInitialSize}" />            </j:if>            <j:if test="${context.getVariable('maven.compile.memoryMaximumSize') != null}">              <ant:setProperty name="memoryMaximumSize" value="${maven.compile.memoryMaximumSize}" />            </j:if>          </j:if>                    <j:if test="${context.getVariable('maven.compile.source') != null}">            <ant:setProperty name="source" value="${maven.compile.source}" />          </j:if>          <j:if test="${context.getVariable('maven.compile.target') != null}">            <ant:setProperty name="target" value="${maven.compile.target}" />          </j:if>                    <j:if test="${context.getVariable('maven.compile.verbose') != null}">            <ant:setProperty name="verbose" value="${maven.compile.verbose}" />          </j:if>        </ant:javac>      </j:when>      <j:otherwise>        <ant:echo>No java source files to compile.</ant:echo>      </j:otherwise>          </j:choose>  </goal>

늘상 얘기하지만 이런 소스를 보고 난 항상 쫀다는게 특징이다. ㅠㅠ

하지만 대충 그까이꺼 머 대충 살펴보면 plugins.jelly에 java:compile이라는 goal을 보면 javac 역할을 하는 구나라는 것은 대략 알수 있지 않은가???

우리가 java:compile을 쳤을때 바로 이 process 가 실행된것이라고 보면 되는 것이다.
여기서 엄청난 사실을 알게 되는데 위에서 설명할때 maven은 ant를 내부적으로 포함한다고 했는데 여기서 ant task를 사용하는 것을 알 수 있지 않은가..
내부적으로 ant:javac 를 이용함을 말이다.

또한, j prefix를 가진 제어문들이 있는데 jelly라는 script 언어로 build에 활력을 심어 주는 역할을 하게 된다.
다음은 jelly의 소개이다. a Java technology- and XML-based scripting and processing engine

멋지지 않은가??
필자는 jelly까지 정리하기엔 능력상 한계에 부딪혀 추후에 정리를 해 나가는 방향으로 하고..
관심있는 분들은 http://jakarta.apache.org/commons/jelly/ 를 참조하시기 바란다.
현재 존재하는 plugin들은 maven이 가지고 있는 default plugin들이다. 자기의 구미에 맞는 plugin을 직접만들어서 확장할 수도 있다.
하지만 자제 제작되어 있는 무료 plugins들을 가져다 쓰는게 훨 좋지 않을까? 개인적으로 그렇게 생각한다. 원래 인생을 이렇게 살았다. ㅠㅠ

각종 플러그인들은 http://maven.apache.org 왼쪽 메뉴에 getting plugins에서 구할 수있다.
이건 캡쳐 뜨기 귀찮아서 안뜨겠다.

자 그럼 위에 simple project 에서 junit을 위해 사용했던 maven test / maven test:ui 는 설명할 가치도 없겠죠???

현재 셋팅 되어 있는 goal 리스트들은 command 창에서 maven -g 로 확인해 볼 수 있다.

 

 

너무 많다. 공부할게 그만큼 많다는 이야기가 아닌가 ㅠ,.ㅠ;

Customizing Maven with maven.xml

위에서 source compile , testing 등을 했지만 maven.xml을 통해서 우리에 구미에 맞는 Maven behavior 작성을 생각할 수 있다.
maven.xml 은 goal 과 같다라는 의미로 봐도 크게 무리한 해석은 아니라고 본다.

먼저 maven.xml의 샘플 코드를 보자.

<project default="foobar-dist" xmlns:m="jelly:maven">    <preGoal name="java:compile">        <attainGoal name="xdoclet:webdoclet"/>    </preGoal>    <goal name="foobar-dist">        <attainGoal name="war:install" />    </goal>    <postGoal name="war:war">      <mkdir dir="${test.result.dir}"/>      <echo>Creating directories</echo>        </postGoal></project>

그렇다면 구미에 맞는 goal을 가지기 위해선 그것에 알맞는 plugin을 찾거나 작성 한다.
정, 목적에 맞는 goal을 작성키 어려울때는 표준 goal을 <preGoal> 이나 <postGoal> 을 이용하여 확장할 수있다.

위에 문서를 해석해 보면 foobar-dist 라는 default goal이 존재하는데 그 goal이 실행되기 전에 preGoal 을 정의하여 소스를 컴파일 (java:compile)하기전에
XDoclet을 이용하여 소스를 생성하라는 것이다. postGoal은 ?? 말을 안해도 그 훗단 작업인 것이다.

여기서 여러분들은 딜레마에 빠질지도 모르겠다. 적어도 필자는 엄청나게 이해를 하지 못했다. ㅠㅠ

도대체가 project.xml은 뭐고 그럼 maven.xml은 모야 헛갈리게 그냥 하나에 xml에다가 하면 되지?? 이런 의문 말이다.
하지만 정말 다른 개념으로생각해야 한다.

project.xml은 meta성 데이터라고 할 수 있다. project를 더 유연하게 management할수 있는 역할을 하며,
maven.xml은 plugins과 goal을 기반으로 project의 compile이나 deploy등등의 task engine라고 보면 될 것이다.

그럼 맨 처음에 작성했던 simple project에서는 왜 maven.xml이 없었는가??? 정답을 적길 바란다.

Plugin에 대해서

프로젝트들 간에 새로운 goal들을 공유하기 위해서는, 작성된 goal들을 Maven이 설치된 플러그인 디렉토리(${MAVEN_HOME}/plugins)에 플러그인으로 패키지해야 한다. 보통 Maven 플러그인은 project.xml과 plugin.jelly 파일을 포함한다. project.xml 파일은 플러그인의 POM을 묘사하고 plugin.jelly는 maven.xml과 유사하며 플러그인에서 사용가능한 goal들을 포함하고 있다

Maven Web Project

한참 동안 글을 쓰고 있자니 내가 무엇을 하고 있는지 모르겠다는 생각이 문득 들었다.
그렇다h2. 이제 지겹기만한 이론공부 그만하고 실전으로 헤딩해 보는게 훨씬 효율적이고 이해가 빠를 것이라는 생각이 문들 들었던 것이다.
자 이제 아주 너무너무 간단한 Web Project를 하나 Maven 기반으로 Build를 해보겠다.

Web Project 의 기본 디렉토리 구조는 다음과 같다.

maven-web-project||-- project.xml       - MASTER POM|   maven.xml||-- util-project|   ||   `-- project.xml   - Produces the Utility JAR file|       maven.xml|`-- web-project    |    `-- project.xml   - Produces the WAR file        maven.xml

구조를 자세히 보면 maven-web-project가 root에 project.xml과 maven.xml이 있고
그 밑에 subproject인 util-project, web-project에도 각각 project.xml과 maven.xml이 존재함을 알 수 있다.

이것은 알아두자 Master POM , 즉 최상위 project.xml은 그 밑에 subproject까지 관리하는 master template이다 라는 것을 말이다.

일단, 각설하고 차례대로 project.xml 파일을 보겠다.

Master project.xml

<?xml version="1.0" encoding="ISO-8859-1"?><project>  <pomVersion>3</pomVersion>  <id>maven-j2ee-project</id>  <name>Maven J2EE Project</name>   <currentVersion>1.0</currentVersion>   <organization>         <name>javajigi OpenSource Study Group</name>        <url>http://wiki.javajigi.net/Edit.jsp?page=OpenSourceStudy_Maven</url>        <logo>http://wiki.javajigi.net/Edit.jsp?page=OpenSourceStudy_Maven/images/logo.gif</logo>    </organization>  <inceptionYear>2005</inceptionYear>  <package>net.javajigi.openstudygroup.*</package>  <description>Using Maven in J2EE Project</description>   <shortDescription>Using Maven in J2EE Project</shortDescription>	<dependencies/>  <build>    <nagEmailAddress>porter@enet.co.kr</nagEmailAddress>    <sourceDirectory>${basedir}/src/java</sourceDirectory>    <unitTestSourceDirectory>${basedir}/src/test</unitTestSourceDirectory>    <unitTest>      <includes>        <include>**/*Test.java</include>      </includes>    </unitTest>    <jars/>  </build></project>

util-project project.xml

<?xml version="1.0" encoding="ISO-8859-1"?><project>  <extend>${basedir}/../project.xml</extend>  <pomVersion>3</pomVersion>  <groupId>maven-j2ee-project</groupId>  <id>util-project</id>  <name>Util Project</name>  <dependencies>    <dependency>      <groupId>commons-logging</groupId>      <artifactId>commons-logging</artifactId>      <version>1.0.2</version>    </dependency>  </dependencies></project>

web-project project.xml

<?xml version="1.0"?><project>  <extend>${basedir}/../project.xml</extend>  <pomVersion>3</pomVersion>  <id>web-project</id>  <groupId>maven-j2ee-project</groupId>  <name>Web Project</name>  <dependencies>    <dependency>      <groupId>maven-j2ee-project</groupId>      <artifactId>util-project</artifactId>      <version>1.0</version>      <properties>        <war.bundle>true</war.bundle>        <war.manifest.classpath>true</war.manifest.classpath>      </properties>    </dependency>	<dependency>        <groupId>j2ee</groupId>        <artifactId>j2ee</artifactId>        <version>1.3.1</version>    </dependency>    <dependency>      <groupId>commons-beanutils</groupId>      <artifactId>commons-beanutils</artifactId>      <version>1.4.1</version>      <properties>        <war.bundle>true</war.bundle>        <war.manifest.classpath>true</war.manifest.classpath>      </properties>    </dependency>  </dependencies></project>

이 project.xml을 보게 되면 sub project인 util-project과 web-project 의 project.xml에는 master project.xml을 상속하고 있는 표현이 눈에 띤다.

바로 <extend>${basedir}/../project.xml</extend> 부분이다.
말 그대로 확장을 한 셈이다. 다르게 말하면 상속 개념이고 master project.xml의 개념을 확장하며 자신만의 특징적인 것을 자신의 project.xml에 기술할 수 있는 것이다.

또한, web-project를 보게 되면

<dependency>      <groupId>maven-j2ee-project</groupId>      <artifactId>util-project</artifactId>      <version>1.0</version>      <properties>        <war.bundle>true</war.bundle>        <war.manifest.classpath>true</war.manifest.classpath>      </properties>    </dependency>

util-project를 종속관계로 참조하는 것을 알 수 있다. 이 web-project가 war로 묶일때 같이 bundle로 묶이는 것이다.

그담 maven.xml들을 살펴 보겠다.

root maven.xml

<?xml version="1.0"?><project    default="all"            xmlns:j="jelly:core"            xmlns:maven="jelly:maven">    <goal name="all">        <maven:reactor  basedir="${basedir}"                        includes="*/project.xml"                        goals="maven-j2ee-project-install"                        banner="Building Maven J2EE Project"                        ignoreFailures="true"/>    </goal></project>

util-project maven.xml

<project default="maven-j2ee-project-install" xmlns:m="jelly:maven">  <goal name="maven-j2ee-project-install">     <attainGoal name="jar:install" />  </goal></project>

web-project maven.xml

<project default="maven-j2ee-project-install" xmlns:m="jelly:maven">   <goal name="maven-j2ee-project-install">    <attainGoal name="war:install"/>  </goal></project>

현재 만들어진 project.xml은 총 3개의 프로젝트로 구성되어 있다. 그럼 이 것을 모두 각각 build를 해야 한다는 것은 짜증나는 일이다.

각각의 프로젝트에 대해 같은 절차를 반복한다는 것은 시간을 잡아먹는 일이며 에러를 발생시키기도 한다. 이러한 문제를 덜기 위해, Maven의 reactor 기능이 서브프로젝트를 자동으로, 올바른 순서대로 빌드하여 시간과 실수를 줄여준다.

Reactor는 먼저 basedir 디렉토리의 project.xml 파일을 검사해서, maven-j2ee-project-install goal을 호출한다. 수행될 순서는 각 프로젝트의 종속 섹션에 달려있다. 더구나, 일반적으로 reactor를 마스터 프로젝트의 maven.xml 파일에 정의한다. goal들은 서브프로젝트에 상속되기 때문에, goal의 이름을 선태할 때 주의해야 한다.

maven-j2ee-project-install 은 util-project이나 web-project 각각의 maven.xml에 존재하는 goal 이다. 즉 , 이것들이 실행되는 것이다.

여기서 의문점을 가져야 하는 것은 어떤 거 순서대로 build가 되냐는 것인데, 위에서 설명했듯이 종속관계와 관련이 있다. 예를 들어 ,

web-project 의 project.xml에서 util-project를 dependency 하였으므로 maven은 자동으로 util-project를 먼저 빌드한 후, web-project를 빌드하게 된다.

대단하지 않은가 ???

이 maven_web_project는 발표 시 직접 소스를 보며 설명을 하겠다.

Maven Web Project 예제 소스 : maven.zip

개인적 사담

여기저기 참고문헌들을 보고 베끼고 짜집기 하고 그것을 내것으로 만들고 그것을 남들에게 전달하기 위해 노력을 하고 문서를 작성은 하였다.
마치 난중일기를 쓴거 처럼, 헌데 이것은 Maven을 이용한 효율적인 프로젝트 관리방안이라는 원래 주제 취지에 전혀 부합되지 않는다고 필자는 생각된다.

마치 메뉴얼을 나열한 것에 불과하다는 생각이다.
메뉴얼이라 하더라도 내가 작성한 것은 maven 기능의 빙산의 일각도 안되는 미미한 설명일 뿐이다.
하지만 maven에 대한 기본개념을 잡았으니 앞으로 있게될 스터디 프로젝트때 어떻게 maven을 적용할 것이며 어떤 룰과 플러그인으로 우리에게 최적화된 build를 만들것인가에 대한 것은 차차 같이 논의해 볼 사항이라고 생각된다.

항상 남이 만든거만 베껴오는 버릇만 있어서 내가 직접 그 글을 읽고 전달하려 하니 정말 이게 힘든일이구나 라는 것을 느꼈다.
앞으로 maven에 대한 이슈와 기술들에 대해 더 공부하고 어떻게 효율적으로 project에 적용할 수 있을지 더 노력해야 할 것 같다.

참고문헌

 

번호 제목 글쓴이 날짜 조회 수
19 Jenkins의 Restful API file 황제낙엽 2020.08.11 50
18 jenkins에서 tfs에 접속하여 브랜치와 변경집합으로 소스를 다운로드하는 예제 황제낙엽 2020.08.11 44
17 (Jenkins) Application Server의 Continuous Integration 구성의 필요성 file 황제낙엽 2020.05.20 26
16 Nexus Repository 황제낙엽 2020.05.20 28
15 Jenkins Rest API 사용기 file 황제낙엽 2020.03.26 859
14 Ant 로 Java Application 실행시 Target 에 파라미터를 입력하여 Arguments 로 전달하여 실행시키기 file 황제낙엽 2012.06.05 2020
13 ANT에서 Classpath 설정시 순서 주의 황제낙엽 2010.07.10 364
12 Ant에서 "${java.class.path}"이 의미하는바 황제낙엽 2010.07.10 56
11 ANT 를 이용한 RMI 컴파일 ( rmic 로 stub 생성하기 ) 황제낙엽 2010.05.26 43
10 [Maven] 로컬 레파지터리에 사용자 jar 추가하기 file 황제낙엽 2010.04.20 217
» Maven 툴을 이용한 효율적인 프로젝트 관리 방안 황제낙엽 2007.01.30 148
8 Jakarta Ant II탄 상세편 황제낙엽 2007.01.24 26
7 Jakarta Ant I탄 기본편 황제낙엽 2007.01.24 17
6 1900개가 넘는 java컴파일시 Ant의 설정 황제낙엽 2007.02.28 678
5 Ant 의 사용 용례와 사용법 file 황제낙엽 2007.07.16 39
4 Ant로 UTF-8 엔코딩하기 (프로젝트 변환) 황제낙엽 2007.07.03 73
3 ANT 작성예제 (build.xml , build.properties) file 황제낙엽 2007.05.16 36
2 Eclipse에서 Ant+xdoclet 를 이용한 web.xml, struts-config.xml 자동생성 황제낙엽 2007.03.03 125
1 Eclipse 의 auto compile 과 ANT의 compile 황제낙엽 2007.02.28 279