PPT 깔끔하게 요약 정리한 내용 (Git관련)
Eclipse를 활용한 효율적인 팀 개발 및 배포 전략
0. 목차
0. 소개
이 문서는 오픈 소스 자바 어플리케이션을 개발하기 위해 이클립스가 어떻게 사용될 수 있는지에 대해 설명한다.
팀 개발을 위해서 CVS 등의 버전관리 툴과 함께 이클립스를 사용하는 방법에 대하여 알아보고, Ant, Maven 등을 통한 빌드 자동화 시스템에 대해서 알아보도록 한다. Eclipse 는 버전 3.1 을 이용하였다.
CVS 와 Ant 등을 이용한 개발 프로세스는 다음과 같다.

1. CVS 등을 통한 효율적인 팀 개발
소프트웨어 개발 환경에서 기술적으로 가장 중요한 것은 소스 버전 관리이다. 개발팀에서 작성하는 모든 소스는 버전 관리가 되어야 한다. 프로그램 소스는 물론이고 스크립트, SQL, 각종 문서, 설정 파일들까지 포함해야 한다. 소스 버전 관리의 목적은 작업 기록의 보존을 통해 문제가 발생했을 때 원인을 추적하거나 이전 상태로 되돌리기 위한 것이다. 또한 이러한 정보로 버그 추적이나 감사(audit), 퍼포먼스, 품질 관리 목적에 사용할 수도 있다.
보통 소스 버전 관리를 위해서 CVS(Concurrent Versions System)를 많이 사용한다. CVS는 80년대부터 개발되어 오고 있으며, 오픈 소스 개발을 위한 사실 상의 표준이 되어 버린 버전 제어 도구이다. CVS를 좀 더 개선한 Subversion도 있고 여러 가지 상용 툴도 있지만 여전히 CVS가 오픈소스 공동체에서 가장 많이 쓰이며 IDE와도 잘 통합되어 있다.
소스 관리는 사실 SCM(Software Configuration Management)의 일부분이기도 하다. SCM은 소스에 대한 버전 관리 뿐 아니라 소프트웨어의 기능성 수준에 대한 변동 기록, 버그 수정 내역, 요구사항의 변화 등을 종합적으로 관리하는 것을 말한다. 소프트웨어의 새 버전이 발표되면 보통 릴리스 노트(Release Notes)가 같이 배포되는데 이런 것이 SCM의 대표적인 산출물이다.
상용 SCM 툴도 많지만 보통은 소스 관리에 CVS를 쓰고 이외에는 이슈 트래커를 쓰는 것으로 SCM의 대부분의 영역이 커버됩니다. 소스 외의 형상 관리는 사실 업무 요청 관리와 기능적으로 아주 비슷하기 때문이다. 오픈소스와 친한 사람이라면 버그질라(bugzilla)를 접해본 적이 있을 것이다. 버그를 보고하고 버그가 수정되는 과정을 기록으로 남기고 조회할 수 있게 해주는 시스템이다.
여기서는 eclipse 에서 제공하는 CVS 기능과 기타 plugin 을 통해 소스코드를 개발 팀과 공유하는 기능을 알아보고, 효율적인 버전관리 방법에 대해 살펴보도록 하자.
1.1 CVS 및 기타 버전관리툴 소개
1.1.1 CVS 개념
먼저 몇 가지 기본적인 CVS 개념을 살펴보자.
 | CVS vocabulary
Repository The repository is where the files are stored, often on a server. Sometimes also called a depot (e.g. with Perforce). Commit A commit (or, more rarely, install, submit, check-in or ci) occurs when you copy the changes you made on the local files to the directory (the version control software takes care of knowing which files changed since the last time the two were synchronized). Change A change represents a specific modification to a document under version control. The granularity of the modification considered a change varies between version control systems. Change List / Change Set On many version control systems with atomic multi-change commits, a changelist identifies the set of changes made in a single commit. This can also represent a sequential view on the source code, allowing source to be examined as of any particular changelist ID. Check-Out A check-out (or checkout or co) copies a working copy from the repository (it can be seen as the opposite of an import). Update An update (or sync) copies the changes that were made to the repository into your working directory (it can be seen as the opposite of a commit). Merge / Integration A merge or integration brings together (merges) concurrent changes into a unified revision. Revision A revision or version is one version in a chain of changes. Import The term import is used to describe copying the local directory tree in its entirety from your machine to the repository. Export An export is similar to a check-out except that it creates a clean directory tree without version control metadata (often used prior to publishing the contents). Conflict A conflict occurs when two changes are made by different parties to the same document or place within a document. Since the software may not be intelligent enough to decide which change is 'correct', a user is required to resolve the conflict. Resolve The act of user intervention to address a conflict between different changes to the same document. |
CVS 저장소
CVS 서버는 다수의 모듈(modules)을 포함하고 있는 저장소(repository)를 관리한다. java.net의 저장소의 경우 개별 프로젝트는 자신만의 모듈을 가지고 있다.
모듈은 디렉토리와 함께 버전이 부여된 파일을 포함하고 있다. 버전이 부여된 파일은 하나의 파일에 대한 이력을 나타내며, 다수의 개정 이력(revisions)을 포함한다. 파일에 변경이 가해질 때마다, 새로운 개정본(revision)이 생성된다. .java, .xml, .properties 등의 텍스트 기반 파일의 경우, 각각의 개정본은 이전 개정본에서 개정된 내역만을 포함하며, .exe, .jar, .pdf 등의 이진 파일의 경우, 각각의 개정본은 완전한 파일 내용을 포함하게 된다.
CVS는 또한 개정본 생성자와 개정본에 대한 주석, 그리고 개정본 번호도 함께 기록한다. 개정본 번호들은 자동으로 갱신되는데, 대부분의 경우, 파일의 첫번째 개정본 번호는 1.1이고 이후에는 1.2 등의 순서로 갱신된다.
저장소 접속과 권한
CVS 클라이언트는 다양한 프로토콜을 사용하여 서버에 접속할 수 있다. 가장 일반적으로 사용되는 프로토콜은 다음과 같다:
- pserver, CVS를 위한 간단한 프로토콜로 java.net을 이용하여 일반적인 작업을 하는 정도에 적합하다.
- ssh, 추가적인 보안을 위해 SSL을 사용하지만, 보통은 인증서 생성과 같이, 대개 약간의 추가적인 설정 작업이 요구된다.
접근 권한은 모듈 단위로 설정된다. java.net 저장소의 경우, java.net에 로그인한 모든 사용자는 누구라도 모든 프로젝트 모듈들에 대해서 읽기 권한을 갖게 된다. 프로젝트 모듈에 대한 쓰기 권한은 모듈을 등록한 소유주나 프로젝트 개발자들에만 부여된다.
작업을 수행하기 위한 파일 사본 가져오기(checkout)
저장소에 접속한 이후에는 하나 이상의 모듈을 가져올 수 있다. 이를 checkout 이라고 하는데, *체크아웃*을 하면 CVS에서 여러분의 컴퓨터로 모듈의 최신 내용이 복사되어, 여러분은 모듈 복사본을 가지고 원하는 작업을 수행할 수 있게 된다.
동기화(synchronize), 갱신(update), 작업 공간의 수정 사항 반영(commit)
CVS 클라이언트를 사용하여 로컬 파일과 저장소 파일 사이의 *동기화*를 할 수 있다. 동기화 과정에서 로컬 파일과 CVS의 최신 모듈 사이의 차이점을 볼 수 있다. 이러한 차이들은 다음과 같이 분류 할 수 있다:
- Incoming changes - 저장소에 변경이 생겼지만 로컬 파일에는 반영이 안된 경우.
- Outgoing changes - 여러분의 로컬 파일에 변경이 생겼지만 저장소에는 반영이 안된 경우.
CVS 클라이언트에서 저장소의 변경 사항(incoming changes)의 일부 혹은 전부를 반영하여 로컬 파일을 갱신(update)할 수 있다. 모듈에 대한 쓰기 권한을 가지고 있는 경우, 로컬 파일의 변경 사항(outgoing changes) 중 필요한 만큼을 저장소에 반영(commit)하는 것도 가능하다.
충돌(conflict)
CVS는 자동적으로 저장소의 변경 사항과 작업 공간의 변경 사항 가운데서 충돌하는 내용을 감지한다. 개발자 A와 B가 동시에 같은 파일을 수정하고, A가 먼저 이를 저장소에 반영(commit)하고 난 후에 곧바로 B가 자신이 변경한 내용을 저장소에 반영하려고 할 때 이러한 충돌이 발생한다.
아래 다이어그램을 보자. 숫자로 된 화살표는 일의 발생 순서를 나타낸다. Alice가 Bob보다 먼저 변경사항을 저장소에 반영하고 Bob이 동기화를 하게 되면 Bob은 상충이 발생했다는 것을 알게 된다.
충돌나는 변경 사항 해결하기
파일에서 상충되는 변경 사항이 발견 될 경우, 파일을 저장소에 반영하기 전에 상충되는 변경 사항에 대해 병합(merge, integrate)을 수행해야 한다. CVS는 한 파일의 서로 다른 행의 변경 사항들에 대한 병합은 자동으로 수행한다. 다만 같은 행에서 변경이 발생할 경우, 개발자가 직접 병합 을 실행해야 한다. 나중에 보겠지만 이클립스는 개발자의 병합을 돕는 매우 훌륭한 GUI를 제공한다.
수정중인 작업자 추적
CVS에서는 동시 수정 가능 잠금 방식(optimistic locking)을 사용하여 누구라도 CVS 저장소에 접근에 동시에 파일을 수정 가능하지만, 누가 어떤 것을 수정하고 있는지 알아낼 수 있도 있다. 파일 수정 작업을 시작할 경우, 클라이언트가 이 사실을 서버로 알리도록 설정될 수 있다. 만약 누군가 파일을 수정하려고 하면 이미 그 파일이 수정 중이라는 경고를 받을 수 있다. 이것은 수정 사항의 충돌을 피하는 유용한 방법이다.
버전 관리 도구에 따라 저장소에 위치한 파일을 한 사람이 가져가는 경우 해당 파일에 대해 잠금(locking)을 적용한다. 잠금 상태에서는 체크아웃 한 사람이 파일을 다시 저장소로 보내기 전까지는 아무도 파일에 대한 수정 작업을 할 수 없게 된다.
이렇게 엄격하게 잠금을 하는 방식의 버전 관리의 단점은, 종종 다른 개발자들의 수정 작업이 끝날 때까지 대기해야 한다는 점이다. 또한 체크아웃하고 나서 체크인하지 않을 경우, 자신도 모르게 수정도 하지 않는 파일을 잠궈 버리게 된다.
CVS는 잠금을 필요로 하지 않는 동시 수정 가능 방식을 사용한다. 개발자들은 CVS의 충돌 사항을 발견하는 기능을 통해, CVS에서 가져온 파일을 동시에 수정할 수 있다. 충돌나는 사항이 발견될 경우, CVS에서는 이를 해결해주는 도구를 제공한다. 이러한 방식은 위험해 보일 수 있지만, 실제 개발 환경에서는 매우 유용한 방식이다.
아예 충돌이 나는 일 자체를 피하고 싶다면 CVS가 제공하는 파일에 대한 작업자 추적 정보를 이용할 수 있다.
태그(tagging)
개발할 때 여러분은 종종 다음과 같은 이정표(milestones)들에 도달하게 된다:
- "기본 시스템은 작동중이고 모든 테스트는 통과 했음"
- "account 작업 전의 리팩토링"
- "account 작업 후의 리팩토링"
- "릴리즈 1.4"
- "발견된 D113에 대한 수정 완료"
이러한 이정표를 표시해두는 것(marking)은, 변경 사항을 추적하고, 필요한 경우, 이전 상태로 파일들을 되돌릴 수 있도록 보장해주는데 있어 필수적이다.
CVS에서는 모듈에 태그를 붙임으로써 이정표를 표시하게 된다. 태그는 모듈 안에 있는 각각의 파일들에 대한 단일 개정본(single revision)을 취합한다.
태그의 이름은 이정표와 관련있다. ALL_TESTS_PASS와 같은 것은 직관적인 예다.
사용자는 모듈의 태그가 부여된 개정본을 저장소에서 가져올 수 있다. 설령 태그가 삭제되거나 변경되었다 하더라도 태그가 붙었던 버전의 모듈 내용물은 항상 같을 것이다.
분기(branches)와 HEAD
분기(branch)는 일련의 독립적인 개발 흐름을 유지하기 위해 쓰인다. 기본적으로 저장소의 내용과 작업 공간의 내용의 동기화를 위해 update나 commit 명령을 사용하면, HEAD라고 불리는 기본 분기(trunk 라고도 불리운다) 상에서 작업이 이루어진다.
사용자는 필요에 따라 새로운 분기를 생성할 수 있다. 분기는 항상 루트 태그(root tag)라 불리는 태그부터 반드시 시작되어야 하며, 루트 태그의 이름에는 특별한 규칙이 없다.
분기를 사용하는 목적인 대개 공식 배포(release)의 병렬적인 개발을 관리하기 위함이다. 예를 들어 어플리케이션의 1.4 버전을 공식 배포(Release) 했고 계속해서 HEAD 분기에서 2.0 버전을 개발하기 시작했다고 해보자. 만약 치명적인 버그가 1.4 에서 발견되었다는 사용자의 보고가 있다면 RELEASE1-4-0을 루트 태그로 사용하여 분기를 만들 수 있다.
분기 상에서 작업을 시작했을 때, 모듈 내용은 1.4 정식 배포 버전과 완전 동일할 것이다. 이후부터 저장소와의 동기화는 해당 분기에만 적용될 것이다.
태그는 HEAD에서 정의되는 것과 마찬가지로 다른 분기에서도 정의 될 수 있다. 가능하다면 한 수준 정도(one level)의 분기를 고수하는 것이 가장 좋지만, HEAD 이외의 분기에서도 또 다른 분기를 만들 수 있다.
 | 태그가 부여된 버전은 변하지 않는다.
모듈의 태그가 부여된 버전에 대해서는 변경 사항을 저장소에 반영할 수 없고, 수정을 하려면 루트 태그를 사용하여 분기를 생성해야 한다. CVS에서는 분기에 속한 변경 사항들만 저장소에 반영 할 수 있다. HEAD는 기본 분기로 특수한 형태의 분기이다. |
분기의 병합(merge)
필요한 경우에 변경 사항을 특정 분기에서 다른 분기로 병합 할 수 있다. 예를 들어 위 그림에서, 버그를 수정한 1.4.1의 변경 사항을 2.0.0 정식 배포판에 포함시키기 위해서 HEAD 분기로 병합을 할 수 있다. 두 개의 분기 사이의 병합은 저장소와 작업공간의 변경 사항 동기화와 유사하며, 마찬가지로 상충이 발생하는 경우는 이클립스 GUI를 활용하면 좀 더 수월하게 해결할 수 있다.
이력(history) 및 차이점 보기(diff)
CVS는 개별 파일이나 파일 조합의 변경 이력을 볼 수 있는 다양한 방법을 제공한다. 또한, CVS를 이용하면 각각의 수정본, 태그 그리고 분기들 사이의 차이를 볼 수 있다.
참고 자료
- 이클립스 3.1 도움말의 CVS를 이용한 팀 프로그래밍은 개념 수준의 개요를 제공한다.
- CVS를 이용한 오픈 소스 개발이라는 책은 매우 좋은 내용을 담고 있지만 명령 행을 통해 CVS를 사용하는 것을 전제하고 있다. 기본적인 개념을 설명하는 장에서 CVS에 관련된 거의 대부분의 사항을 다루고 있다.
1.1.2 기타, SubVersion, VSS(Visual Source Safe), Perforce
1.2 eclipse 설정과 기본 사용법
cvsnt설치 및 eclipse 에서 cvs연결하는 방법은 다음의 사이트들을 통해 익힐수 있다.
제대로 연결 되었다면 CVS Repositories perspective 에서 다음 화면을 볼수 있다.

1.3 SCM Best Practices
지금까지는 사용법에 관해 알아보았다. 실제 팀내 개발이 제대로 이루어지려면 모든 개발자들이 사용법 숙지를 기본으로 해야 한다.
프로젝트가 커지고 모듈이 많아지고 릴리스가 계속 되고 있는 복잡한 제품을 관리하고 있다면 이런 것 외에 알아야 할 것이 더 있다.
업계에서 알려진 효율적인 버전관리법에 대해 알아보도록 하자.
1.3.1 GUI 툴 사용
자동화와 관련된 것이 아니라면 콘솔창은 매우 불편하다. 우리의 두뇌를 명령어 익히는 작업에서 보다 고차원적인 일을 하도록 유도해야 한다.
eclipse를 사용한다면 대부분의 SCM 이 plugin 형태로 지원되므로 history, diff, merge등을 편하게 사용할 수 있다. Windows 사용자라면 TortoiseCVS(추천), WinCVS 등을 이용하면 revision graph 등 CVS 를 좀 더 편하게 사용할 수 있고, Araxis Merge 같은 상용 merge 툴을 사용해도 좋다.
1.3.2 개발자 작업공간(Workspace or Sandbox)
개발자가 빌드, 테스트, 디버그를 하는 공간이다.
- 워크스페이스를 공유하지 말라.
- 관리되지 않는 워크스페이스에선 작업하지 말라.
- 코드라인과 항상(자주) 동기화 시켜라.
- 자주 체크-인(check-in) 하라. process 를 수립하는 것이 좋다.
1.3.3 소스 코드 라인(Codelines)
소스 파일들의 의미있는 집합을 얘기한다.
- 각 코드라인마다 정책을 정하라.
- 코드라인마다 책임자를 두어라.
- 메인라인(mainline, trunk)을 유지하라.

1.3.4 브랜칭(Branching)
새로운 코드라인을 만드는 것을 의미한다.
- 필요할 때만 브랜치 하라.
- 단순 복사가 브랜치는 아니다.
- 정책이 틀려질때 브랜치하라.
- 최대한 늦게 브랜치하라.(충분히 검토하고 하라)
- 코드 동결시키기 보다는 브랜치하라.

1.3.5 변경사항 전파(Propagation)와 병합(Merging)
한쪽 코드라인에서 변경된 사항을 다른 쪽에도 반영시키는 것을 말한다.
- 원본에 대한 변경은, 브랜치 된 이후 가장 적게 변화된 브랜치에서 하라.
(ex. 릴리즈 브랜치에서 버그를 코친후 변경사항을 메인라인에 병합(merge) 한다.) - 일찍, 자주 전달하라.
- 적합한 사람에 병합을 맡겨라
(ex. 소스(source) 코드라인 책임자 또는 대상(target) 코드라인 책임자)
1.3.6 소프트웨어 빌드(Builds)
소스 파일을 제품으로 바꾸는 과정이다.
- 소스와 툴이 제품이 된다.
- 원본 소스를 모두 체크-인 하라.
- 빌드된 오브젝트들을 소스와 분리하라.
- 공통적인 빌드 툴을 사용하라.
- 자주 빌드하라.
- 빌드 로그와 산출물을 모두 저장하여 유지하라.
1.3.7 조직에서 제도화하기(Process]
위의 모든 과정들의 규칙.
- 변경 패키지를 추적하라. (변경 패키지란, 특정 버그나 기능추가로 변경된 파일들의 묶음을 의미)
- 변경 패키지의 전달(propagation) 내역도 추적하라.
- 요구사항과 변경 패키지를 구별하라.
- SCM에 있는 모든 프로세스, 정책, 문서, 제품, 컴포넌드, 코드라인, 브랜치, 작업 에 책임자를 두어라.
- 문서는 항상 최신버전으로 유지하고, 활용하라.
1.3.8 결론
"툴은 잘 사용했을 때 좋은 것이다".
단순히 CVS , Subversion, Clearcase, perforce, accurev, 등의 SCM 툴을 설치하고 명령어 몇개를 사용한다고만 해서 제대로 쓰고 있다는 것은 아니다. 프로그래밍 언어만 안다고 해서 프로그래밍을 잘 안다고 할 수 없듯이, SCM 툴을 활용하는 데도 그만한 시간과 노력이 필요하다.
 | 이슈 추적 시스템
여기에서 직접 소개하지는 않았지만 Issue(bug) tracking system(버그 추적 시스템, 이슈 추적 시스템)의 사용도 고려해 봐야한다. 버그를 추적하고 메모를 기록하고 작업 목록을 유지하는 훌륭한 도구이다. 모든 문제점과 기능 요청 사항을 한 곳에 모을 수 있으므로 프로젝트 관리에 도움을 준다. 유명한 것으로 버그질라, Mantis, TUTOS, Atlasian의 JIRA등이 있다. |
2. 빌드 및 배포 자동화
프로젝트 컴파일은 귀찮은 일이다. 믿을 만하고 반복가능하게 만들어야 한다. 일관성과 정확성을 보장하는 훌륭한 방법은, 팀이 하는 모든 일을 자동화하는 것이다. XP(Extreme Programming)의 지속적인 통합(Continuous Integration)에서는 빌드 자동화를 가장 중요하게 다룬다. 빌드는 비어 있는 디렉토리 하나를 가지고 프로젝트를 밑바닥에서부터 만드는 과정으로, 무엇이건 여러분이 생산해 내고 싶은 최종 제품을 만들어 낸다.
 | 프로젝트 빌드의 전형적인 단계
자동화의 기본적인 개념 및 프로세스는 비슷하다. (0.소개 부분의 그림 참조) - Check-out : 저장소(repository)에서 소스코드를 가져온다. update 를 통해 가져오기도 한다.
- Build, test : 툴에서 제공하는 파일(ex. build.xml, makefile, project.xml 등)을 이용하여 빌드한다. 회귀테스트 등을 수행한다.
- Deploy : 실제 사용자에게 보내질 전체 제품을 만든다.(ex. CD, 웹사이트에 배포) 예제와 문서 등이 포함되기도 한다.
- Test : 테스트를 실행한다.
|
잘 관리되고 있는 프로젝트는 매일 빌드(daily builds)를 한다. 매일 야밤에 빌드를 수행하여 그 성공/실패 결과를 이메일등을 통해 개발자들에게 알리거나 다음날 아침에 확인할 수 있게 한다.
이런 빌드를 자동화하기 위해 여러가지 툴을 사용할 수 있는데, 유닉스/리눅스 환경에선 오래 전부터 make를 사용해 왔다. Makefile 이라는 파일에 빌드를 위한 스크립트를 작성한후 make 명령을 실행하면 빌드가 된다.
하지만 make에서 지원하는 기능이 너무 빈약해서 빌드가 복잡할 경우 Makefile에 모든 빌드 과정을 기술해야 하기 때문에 Makefile이 복잡해져서 유지보수가 힘들다는 문제가 있다. 그래서 자바 진영에서는 make를 넘어서는 툴을 만들고자 했고 그래서 2000년 7월 Ant가 등장했다. 그러나 Ant 역시 한계가 있었기 때문에 Ant를 한 단계 더 발전시킨 Maven이 등장했다. 현재 대부분의 오픈소스 자바 프로젝트는 Ant나 Maven 둘 중의 하나를 빌드 도구로 사용하고 있다.
2.1 Ant
Ant는 구조적으로는 make를 그대로 이어 받았다. build.xml에 빌드 설정을 해두고 ant 명령을 실행하면 빌드 작업이 수행된다. Ant는 자주 하는 작업인 컴파일, 압축, 복사, 메일, SCM 등의 태스크를 미리 갖추어 놓았기 때문에 make보다 훨씬 간단하게 빌드 설정을 할 수 있다. 전체 태스트 목록은http://ant.apache.org/manual/tasksoverview.html 에서 확인하기 바란다.
Ant의 표준 빌드 파일인 build.xml의 예를 보자.
test 패키지에 있는 HelloAnt.java 파일을 컴파일하고, jar 압축하고, 배포하고, 실행하는 작업을 수행한다.
<?xml version="1.0" encoding="euc-kr"?>
<project name="Hello Ant" default="dist">
<property file="build.properties"/>
<property name="base.dir" value="."/>
<property name="dist.dir" value="dist"/>
<property name="build.dir" value="build"/>
<property name="src.dir" value="src"/>
<property name="jar.file" value="${version}_helloworld.jar"></property>
<property name="dist.file" value="${version}_helloworld.zip"/>
<target name="prepare">
<tstamp>
<format property="DSTAMP" pattern="yyyy.mm.dd" />
-->
</tstamp>
<echo message="Build Start!! ======> ${DSTAMP}-${TSTAMP}" />
</target>
<target name="clean" depends="prepare">
<delete dir="${dist.dir}"/>
<delete dir="${build.dir}"/>
</target>
<target name="compile" depends="clean">
<mkdir dir="${build.dir}"/>
<javac deprecation="off" srcdir="${src.dir}" destdir="${build.dir}"
listfiles="no" failonerror="true">
<classpath>
<pathelement path="${base.dir}/lib" />
<fileset dir="${base.dir}/lib">
<include name="*.jar" />
</fileset>
</classpath>
</javac>
</target>
<target name="mkjar" depends="compile">
<mkdir dir="${dist.dir}"/>
<jar destfile="${dist.dir}/${jar.file}"
basedir="${build.dir}" />
</target>
<target name="dist" depends="mkjar">
<copy todir="${dist.dir}/lib">
<fileset dir="lib" />
</copy>
<copy todir="${dist.dir}/src">
<fileset dir="src" />
</copy>
<zip destfile="${DSTAMP}_${TSTAMP}_${dist.file}">
<fileset dir="${dist.dir}/." />
</zip>
</target>
<target name="run">
<java classname="test.HelloAnt">
<classpath>
<pathelement location="${dist.dir}/${jar.file}" />
<pathelement path="${base.dir}/lib" />
<fileset dir="${base.dir}/lib">
<include name="*.jar" />
</fileset>
</classpath>
</java>
</target>
</project>
build.xml을 이용한 실행은 콘솔창에서
식으로 입력하면 된다.
Eclipse 에서는 Ant 과 통합되어 있으므로 다음처럼 실행할 수 있다.
| Eclipse 에서 ant 빌드 |
|---|
 |
사실 Ant 개발자들은 Ant가 make보다 훨씬 선언적으로 빌드를 정의할 수 있다는 점을 장점으로 내세우지만 이건 사실 javac, copy와 같은 태스크를 미리 자바 클래스로 코딩해 놓았기 때문에 그런 것일 뿐 실제적으로는 Make의 메커니즘과 큰 차이가 없다. 그런 반면 build.xml을 선언적으로 작성할 수 있게 하기 위해 build.xml의 문법에 스크립트적인 요소를 최소한으로 줄였고 또 XML 자체가 프로그래밍이 필요한 부분을 기술하기에는 적합하지 않기 때문에 Ant의 파워는 오히려 make보다 낮아진듯 하다.
그리고 실제로 자바 클래스 컴파일에 파일 몇 개 복사하는 정도라면 아주 간단하게 빌드 파일을 작성할 수 있지만, 복잡해지기 시작하면 build.xml은 점점 이해하기 힘든 코드가 됩니다. 그래서 Ant에서는 build.xml에 주석을 충분히 달아놓을 것을 권고하고 있다.
하지만 복잡해서 주석을 많이 필요로 하는 상황은 좋지 않다. 주석은 Sun 에서도 권장하고 있지 않고, 마틴파울러의 리팩토링에서는 나쁜 냄새라고 얘기한다. 되도록 주석없이 쉽게 이해할 수 있도록 빌드 스크립트를 유지 해야 한다.
하지만 프로젝트 규모가 커지고 기능들의 추가/제거가 많아지면 빌드의 구조도 바뀌고 확장 된다. 결국 Ant도 make보다는 조금 사정이 나아졌지만 본질적으로는 make와 동일한 문제를 갖고 있다.
2.2 Maven 2
Maven은 이런 점들을 해결하고 나아가 종합적인 *프로젝트 관리*까지 지원한다. Maven은 현재 버전 2.2까지 나와 있다, Maven 1.x 버전과 2.x 버전간에는 개념을 제외한 파일 호환성이 없으므로, 기존 프로젝트 유지보수 등 특별한 이유가 있지 않는 한 2.x를 사용하길 권장하고 있다. 여기서도 Maven 2를 기준으로 설명한다. (Maven 1 보다 달라진 점은 http://maven.apache.org/maven1.html#changed 에서 보기 바란다.)
Project object model(POM)
Maven 2 프로젝트의 핵심은 POM(project object model)이다. POM 은 프로젝트에 대한 상세한 설명과 버전 정보, SCM, 의존성, 리소스, 팀 멤버, 구조 등을 포함한다. POM은 프로젝트의 홈디렉토리에 XML 파일(pom.xml) 형식으로 저장된다.
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.javaworld.hotels</groupId>
<artifactId>HotelDatabase</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Maven 2 Quick Start Archetype</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Maven 2 디렉토리 구조
Maven은 대부분의 것을 표준화 하기 위해 노력하는데, 이것이 장점이다. 다른 Maven 프로젝트에서 일했던 개발자라도 쉽게 구조와 조직에 익숙해 질수 있도록 해준다. 디렉토리 구조나 코딩 규칙 등를 다시 만드느라 시간을 쓸 필요가 없다.
Maven 2는 디렉토리 구조를 꼭 지켜주기를 권고하는 데, 이유는 다음과 같다.
- POM 파일을 작고 간단하게 해준다.
- 프로젝트를 이해하기 쉽게 하고, 인수 인계를 편하게 해준다.
- plug-ins 과 통합을 쉽게 해준다.
Maven 2 의 표준 디렉토리 구조는 다음과 같다. 프로젝트의 홈디렉토리에 pom.xml 이 존재하고 두 개의 하위 디렉토리가 있다. src 에는 모든 source가, target 에는 생성된 모든 것이 들어간다.
| Maven 2 표준 디렉토리 구조 |
|---|
 |
src의 하위 폴더는 명확한 목적이 있다.
- src/main/java: Your Java source code goes here (strangely enough!)
- src/main/resources: Other resources your application needs
- src/main/filters: Resource filters, in the form of properties files, which may be used to define variables only known at runtime
- src/main/config: Configuration files
- src/main/webapp: The Web application directory for a WAR project
- src/test/java: Unit tests
- src/test/resources: Resources to be used for unit tests, but will not be deployed
- src/test/filters: Resources filters to be used for unit tests, but will not be deployed
- src/site: Files used to generate the Maven project Website
프로젝트 라이프사이클(Project lifecycles)
프로젝트 라이프사이클은 Maven 2 의 핵심영역이다. 대부분의 개발자는 컴파일(compile), 테스트(test), 배포(deploy)등의 빌드 단계(phase) 개념은 알 것이다. Ant에는 이같은 이름의 target 이 있다. Maven 1 에서는 이와 관련되 plug-in 을 직접 호출한다. 예를 들어 소스를 컴파일하려면 java plug-in 을 사용한다.
Maven 2 에서는, 이 개념이 잘 알려지고 잘 정의된 라이프사이클 단계(phase)의 집합으로 표준화 되었다. Maven 2 사용자는 plug-in 호출없이 라이프사이클 단계(phase)를 호출하면 된다.
| Maven 2 라이프사이클 단계(phase) |
|---|
 |
중요한 라이프사이클 단계는 다음과 같다.
- generate-sources: Generates any extra source code needed for the application, which is generally accomplished using the appropriate plug-ins
- compile: Compiles the project source code
- test-compile: Compiles the project unit tests
- test: Runs the unit tests (typically using JUnit) in the src/test directory
- package: Packages the compiled code in its distributable format (JAR, WAR, etc.)
- integration-test: Processes and deploys the package if necessary into an environment where integration tests can be run
- install: Installs the package into the local repository for use as a dependency in other projects on your local machine
- deploy: Done in an integration or release environment, copies the final package to the remote repository for sharing with other developers and projects
다른 단계에 대해서는 여기 를 참조하기 바란다.
Maven 2 의 라이프사이클 단계에 익숙해지면, 어떤 Maven 프로젝트에 가더라도 두렵지 않을 것이다.
라이프사이클의 단계를 호출하면 이전의 단계도 역시 호출한다. 따라서 라이프사이클 단계는 갯수에 제한은 있지만, 이해하기 쉽고, 잘 조직되었으며, Maven 2 프로젝트에 쉽게 적응할 수 있게 도와 준다.
Transitive dependencies
Maven 2 에서 눈에 띄는 부분이 transitive dependency 관리이다. Linux의 urpmi 를 사용해 봤다면 어떤 개념인지 알것이다. Maven 1 에서는 필요한 모든 JAR 파일을 직/간접적으로 선언해 줬어야 한다. Hibernate 을 쓰려면 에 어떤 JARs 들이 필요한지 알고 있는가? Maven 2 에서는 이런 걸 몰라도 된다. 그냥 어떤 라이브러리가 필요한지 Maven 에게 알려주기만 하면 알아서 처리해준다.
프로젝트에 Hibernate 을 사용한다고 생각해 보자. 그냥 다음처럼, pom.xml 의 dependencies 섹션에 새로운 dependency 를 추가하기만 하면 된다.
<dependency>
<groupId>hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
이게 전부다. Hibernate 3.0.3 을 실행하기 위해 다른 JARs(버전에 따라)를 찾아다닐 필요가 없다. Maven 이 알아서 해 준다.
Maven 2 의 dependencies 부분은 Maven 1 과 유사하다. 가장 중요한 차이점은 다음에 설명할 scope 태그이다.
Dependency scopes
실제 엔터프라이즈 애플리케이션에서 보면, dependencies 있는 모든 것이 배포될 필요는 없다. 어떤 JARs 는 유닛 테스트에만 필요할 수 있고, 또 어떤 것은 애플리케이션 서버에서 런타임 시에만 필요할 수 있다. Maven 2 에서는 dependency scoping 이라는 테크닉을 이용해서 특정 JARs 가 정말 필요할 때만 사용될 수 있게 한다. 필요하지 않을 때는 classpath 에서 제거된다.
Maven 은 4가지의 dependency scopes 를 제공한다 :
- compile: A compile-scope dependency is available in all phases. This is the default value.
- provided: A provided dependency is used to compile the application, but will not be deployed. You would use this scope when you expect the JDK or application server to provide the JAR. The servlet APIs are a good example.
- runtime: Runtime-scope dependencies are not needed for compilation, only for execution, such as JDBC (Java Database Connectivity) drivers.
- test: Test-scope dependencies are needed only to compile and run tests (JUnit, for example).
프로젝트 커뮤니케이션(Project communication)
어느 프로젝트에서나 중요한 부분은 내부 커뮤니케이션이다. 만병통치약은 아니지만, 프로젝트 웹사이트에 정보를 집중화시키면 팀내의 가시성을 향상시킬 수 있다. Maven은 프로젝트에 대한 각종 문서가 집약된 사이트를 생성하도록 해준다. 적은 노력으로 수준높은 프로젝트 웹사이트를 재빠르게 만들어 올릴 수 있다.
보통 Maven 사이트는 다음 정보를 만든다 :
- General project information such as source repositories, defect tracking, team members, etc.
- Unit test and test coverage reports
- Automatic code reviews and with Checkstyle and PMD
- Configuration and versioning information
- Dependencies
- Javadoc
- Source code in indexed and cross-referenced HTML format
- Team member list
- And much more
Maven 으로 관리되는 프로젝트는 여기 http://maven.apache.org/powered.html 에서 볼수 있다.
3. 토론
- 이 외의 다른 방법으로 관리를 하고 있는가?
- 팀내에 이런 툴을 도입하는데 따른 문제는 없었는가?
- 진정 효율적인가?
- 전사적으로 또는 모든 개발자가 사용하기 위해 어떤 노력을 해야 하는가?
출처: http://www.javajigi.net/pages/viewpage.action?pageId=2240
참고문헌