자동화 빌드 도구 메이븐

1장 빌드(Build) 개념



빌드(Build)란 무엇일까? 위키(Wikipedia)의 정의에 따르면 소프트웨어 빌드(Software Build)란

소스코드 파일을 컴퓨터에서 실행할 수 있는 독립 소프트웨어 가공물로 변환하는 과정을 말하거나

그에 대한 결과물을 읽컫는다.

소프트웨어 빌드에 있어 가장 중요한 단계 중 하나는 소스코드 파일이 실행 코드로 변환되는 컴파일 과정이다.

이와 같은 정의와 함께, 같이 빌드 자동화라는 단어가 나온다.

이 단어들의 의미를 좀 더 파헤쳐 보자.


프로그래밍 언어를 사용하여 소스코드를 작성하고 나면 실행하기까지 여러 작업들이 필요하다.

디렉터리를 만들고, 파일을 이동하고, 컴파일을 하고, 테스트를 수행하고,

압축을 하거나 해제하고, 웹 서버나 형상 관리 서버에 올리거나 하는 등의 여러 작업들을 수행해야 한다.

경우에 따라서는 소스코드는 단 한 줄 변경했는데 이러한 작업들을 여러 번 반복해야 할 때가 있다.

매우 비효율 적이기도 하며, 개발자들이 늦게 퇴근하는 이유가 되기도 한다.


빌드는 컴파일보다 훨씬 많은 것을 의미하는데, 테스트를 포함한 소스코드 컴파일 작업,

zip 파일이나 자바의 jar 파일 혹은 윈도우의 exe 파일 같은 배포할 수 있는

컴포너트를 묶는 패키징 작업, 파일과 디렉터리를 만들고 복사하고 지운는 작업, 자동화된 테스트 작업 버전관리 도구 사용,

Java Doc등의 문서 생성, 각종 서버에 배포 작업, 자동화된 검사도구(FindBugs, CheckStyle PMD 등)를 통한 코드 품질 분석

등의 많은 작업을 수행 할 수 있다.

이런 작업들은 자동화된 절차에 따라 수행할 수 있어야 한다.

소프트웨어 빌드 프로세스 크게 개인 빌드 작업과 통합 빌드 작업으로 나눌 수 있다.

개발 빌드 작업
① 개발자가 소프트웨어 소스코드를 작성한다.
② 개인 빌드를 통해 실행 가능한 바이너리 코드를 생성한다.
③ 정상 코드 여부를 판별 후 형상 관리 서버로 작성한 코드를 커밋한다.


통합 빌드 작업
① 개발 서버는 항상 관리 서버로부터 최신의 소스코드를 체크아웃한다.
② 주기적으로 빌드 프로세스를 통해 코드 빌드 후 동작 가능한 소프트웨어 패키지를 생성한다.
③ 운영 서버로 패키징한 소프트웨어를 배포한다.


프로그램을 빌드하는 과정은 보통 다른 프로그램을 제어하는 프로그램인 빌드 도구에 의해관리된다.

빌드 도구로는 메이크(Make), 그래들(Gradle), 앤트(Ant), 메이븐(Maven), SBT등이 있다.

빌드 도구를 사용하여 자동화 시스템을 구축해 놓는다면 반복 작업과 잦은 야근에서 벗어날 수 있다.



2장 메이븐(Maven) 개념



메이븐(Maven)은 유대어로 '지식 축적기'라는 뜻이며, 숙달한 사람, 프로라는 의미로도 사용된다.

소프트웨어를 개발할 때 반복되는 작업을 자동화하는 빌드 도구다.


메이븐은 빌드 관리 작업의 단순화와 소프트웨어 프로젝트에 참여하는 개발자들에게 공통 인터페이스를 제공하여

프로젝트를 더욱 풍성하게 해주기 때문에 빌드 도구보다는 더 다양한 것들을 포함하고 있다.

따라서 도구라는 표현보다는 플랫폼이라는 표현이 더 어울릴 듯 하다.


메이븐은 확장 가능한 구조와 나름의 정형화된 구조를 지녀 단지 몇 가지 명령어만 익히면

별도의 스크립트에 대한 지식 없이도 명령어를 사용할 수 있다.

메이븐을 사용하면 표준 프로젝트를 일관된 방식과 구조로 관리하고 배포하고 운영할 수 있다.

1) 정형화된 구조와 절차를 제공


메이븐은 컴파일(Compile), 패키징(Package), 테스트(Test) 등의 작업 프로세스를 저영화하고 이를 준수하도록 강제한다.
프로젝트의 소스 트리 구성도 src/main/java 등과 같은 정형화된 패턴을 따르도록 가이드해준다.

모든 프로젝트에 일관된 디렉터리 구조와 빌드 프로세스를 유지할 수 있게 된다.

따라서 표준에 따른 개발이 가능해진다.


예를 들어 앤트와 같은 빌드 도구는 절차적이어서 항상 컴파일(Compile), 패키징(Package), 테스트(Test)처럼

모든 과정을 사용자가 코딩으로 무엇을 할 것인지와 언제 할 것인지에 대하여 정확하게 알려주어야 하며,

프로젝트를 처음 생성한 개발자에 따라 디렉터리 구조와 빌드 명령이 일관되지 않을때가 많다.


메이븐은 공식적인 관례(Convention)를 가지고 있기 때문에 소스코드의 위치, 바이트 코드의 위치, jar파일의 위치등을 알 수 있다.

2) 디펜던시 라이브러리 해결을 자동화


디펜던시 라이브러리 혹은 라이브러리 디펜던시는 라이브러리 의존성(혹은 종속성)을 의미한다.

어플리케이션을 실행하기 위해 필요한 라이브러리들과 그 라이브러리들이 필요로 하는 라이브러리들을 가리킨다.


pom.xml에 기술할 때 태그 이름도 디펜던시(dependency)이기 때문에 용어의 통일성을 위해서 앞으로

의존성 혹은 종속성을 디펜던시라 기술한다.


JDK가 제공하는 기본적인 라이브러리 이외에 외부 라이브러리를 사용하려면,

먼저 라이브러리를 제공하는 사이트에서 버전에 맞게 다운로드하고,

관계되는 다른 라이브러리들도 다운로드하여 클래스 패스에 등록하는 번거로운 작업을 해야 한다.


메이븐을 사용하면 의존 관계인 라이브러리 관리가 용이하다.

오픈소스 라이브러리의 경우 메이븐 중앙 저장소(Central Repository)에 모두 저장하고 있어서,

디펜던시가 있는 라이브러리의 명세만 입력해주면, 자동으로 다운로드하여 클래스 패스를 걸어주기 때문에,

따로 처리할 필요가 없다. 메이븐의 가장 큰 장점 중 하나다.

3) 라이브러리 관리가 용이


일반적으로 프로젝트 진행 시 사용되는 라이브러리를 형상 관리(SCM : Software Configuration Management) 도구에서 관리하는데,

이는 불필요한 공간을 낭비할 뿐만 아니라, 버전별로 관리하는 것도 어렵다.

메이븐에서는 라이브러리 전용 저장소(Repository)를 제공하므로, 통합 관리가 가능하다.

4) 라이프 사이클


메이븐은 라이프 사이클을 가지고 있어서 라이프 사이클에 따라서 연속적으로 단계를 실행한다.

플러그인의 골(Goal)에 따라 라이프 사이클의 각 단계가 실행된다.

5) 다양한 플러그인 사용


메이븐은 기본적으로 플러그인으로 기능을 확장한다.

플러그인을 사용하면 IDE 프로젝트로 자동 변환, 데이터베이스 통합, 다양한 리포트 생성 등의 작업을 수행할 수 있다.

메이븐은 pom.xml 파일에 위의 내용을 기술한다.


메이븐은 javac, jar, java등과 같은 기본 명령어는 이미 내장되어 있어 별도로 스크립트를 작성할 필요가 없다.

그 이유는 메이븐의 경우 디렉터리 구조가 정형화되어 있기 때문이다.

따라서 항상 메이븐에서 제시하는 구조를 따라가야 한다.

물론 이러한 점은 유연성이 떨어진다고 할 수 있지만,

다양한 프로젝트들이 메이븐의 표준에 맞추어 개발되어 있다면 어떠한 프로젝트에 가서 진행하더라도 쉽게 해당 프로젝트에 적응할 수 있다.

그 뿐만 아니라 메이븐이 제시하는 구조는 이미 자바 프로젝트에서는 표준 구조이므로

오히려 메이븐의 구조에 맞추어 개발하면 보다 표준에 맞는 구조 설계가 가능하다.


메이븐을 사용하여 단위 테스트를 작성하고 실행하고 싶다면,

테스트 코드를 작성하여 ${basedir}/src/test/java에 두고,

JUnit을 test scope의 디펜던시로 추가하고 메이븐의 test 명령을 실행하면 된다.

메이븐은 메이븐 플러그인 형태로 공통 프로젝트 태스크들을 내장으로 가지고 있기 때문에 이러한 것이 가능하다.


메이븐에 대한 래퍼런스 사이트를 참고하면 더 많은 정보들과 다양한 플러그인들에 대한 문제를 해결할 수 있다.

http://maven.apache.org/plugins/index.html

https://github.com/mojohaus




3장 메이븐(Maven) 특징



메이븐을 사용하는 프로젝트에서는 디펜던시 관리, 로컬 및 원격 저장소 사용,

빌드 로직의 전역적인 재사용, 툴에 대한 이식성과 통합 등의 기능을 사용할 수 있으며,

프로젝트 산출물( Project Artifacts )등의 쉬운 검색과 필터링이 가능하다.

1) 프로젝트 객체 모델

( Project Object Model )

POM은 메이븐의 기본이 되는 모델을 의미한다.

이 모델의 일부는 메이븐 엔진에 이미 내장되어 있고

다른 부분은 pom.xml 파일명을 통해 선언적으로 만들어 주어야 한다.

2) 디펜던시 관리 모델

( Dependency Management Mode )

메이븐은 프로젝트의 디펜던시가 어떻게 관리되는지 파악한다.

디펜던시 관리 모듈은 메이븐에 내장되었고 대부분의 요구사항들을 채택했다.

이 모델이 제대로 동작하는 것으로 검증됐으며 현재 주요 오픈소스 프로젝트에 의해 배포된 모델이다.

3) 빌드 라이프 사이클과 각 단계

( Project Life Cycle and Phases )

POM에 관련된 개념으로 빌드 라이프 사이클과 각 단계들이 있다.

메이븐을 사용할 때 대부분의 작업은 플러그인을 사용하여 수행한다.

메이븐은 잘 정의된 단계들과 빌드 사이클에 따라 이러한 플러그인들을 조율한다.

메이븐을 사용하기 위해 필요한 주요 개념들을 살펴보기로 하자.

#1.플러그인과 골

메이븐 플러그인은 한 개 이상의 골( Goal )들의 집합이다. 골( Goal )은 일종의 작업 단위인 셈이다.

메이븐 코어는 몇 개의 XML 문서들이 해석되고, 어떤 라이프 사이클을 관리하는지,

어떤 플러그인들이 어떻게 작동하는지 알지 못한다.

대부분의 책임을 메이븐 플러그인들에게 위임하돌도록 설계되어있다.

플러그인들은 라이프 사이클에 영향을 미치고 플러그인이 가진 골을 실행시킬 수 있도록 허용한다.

메이븐 내의 동작들은 플러그인 골에서 일어난다.

메이븐의 많은 기능들은 플러그인들로 구현되어 있고, 이러한 플르거인들은 메이븐의 저장소에서 찾아서 사용할 수 있다.




컴파일 되는 과정이나 jar 파일을 만드는 과정 등을 메이븐이 스스로 알아서 하는 것이 아니라

Compiler 플러그인과 Jar 플러그인에서 수행하며 메이븐은 필요 시 플러그인을 다운로드하고

메이븐 중앙 저장소에서부터 주기적으로 업데이트를 해준다.

메이븐 엔진은 플러그인을 통해 파일을 다루는 거의 모든 작업을 수행할 수 있다.

플러그인은 pom.xml 파일을 사용하여 설정하고 기술한다.

플러그인들은 디펜던시 관리 시스템에 의해 아티팩트( artifact ) 로 처리되고
( articat : 단어의 의미는 산출물이다. 여기서는 프로젝트로 이해한다. )

빌드 작업을 할 때 필요하게 되면 다운로드된다.

각각의 플러그인은 다양한 라이프 사이클 각 단계들과 연관이 있을 수 있다.

메이븐 엔진은 라이프 사이클 동안 필요에 따라 플러그인을 호출하는 상태머신( state machine )을 가지고 있다.

#2. 라이프 사이클( Life Cycle )

메이븐은 빌드 라이프 사이클이라고 하는 절차를 가진다.

이는 프로젝트 비륻에 관련된 각 단계( Phases )의 순서를 의미한다.

메이븐은 여러 다른 라이프 사이클을 지원하지만 기본 라이프 사이클을 많이 사용한다.

플러그인의 골( goal )은 라이프 사이클 단계에 포함될 수 있다.

각 단계에는 골이 없을 수도 있고 다수의 골을 가질 수 도 있다.

그림은 각 단계에 골이 연결된 것을 보여준다.

메이븐에 내장된 빌드 라이프 사이클은 여러 단계로 구성되어 있다.

아래 표는 라이프 사이클의 단계를 설명한다.


라이프 사이클 단계의미
validate현재 설정과 POM의 내용이 유효한지 확인한다.
이 과정은 pom.xml 파일 트리 구조를 검증한다.
initialize빌드 사이클에서 수행할 주요 작업 이전에 초기화를 할 수 있는 순간을 의미한다.
generate-sources코드 생성기가 이 다음의 단계들에서 컴파일되고 처리할 소스코드를 생성하기 시작한다.
process-sources소스의 파싱, 수정, 변경을 제공한다.
일반 코드 또는 생성된 코드 모두 여기서 처리된다.
generate-resources소스코드가 아닌 리소스를 생성하는 단계.
보통 메타데이터 파일과 설정 파일을 포함한다.
process-resources이전 과정의 ‘소스코드가 아닌 리소스’ 파일들을 다룬다.
이 단계ㅓ에서 리로스 파일들이 수정, 변경 재배치 된다.
compile소스코드를 컴파일 한다.
컴파일된 클래스들은 타깃 디렉터리 트리 구조에 저장된다.
process-classes클래스 파일 변환과 개선 단계를 다룬다.
바이트코드 위버( weaver )와 instrument 도구가 동작하는 단계다.
generate-test-sources단위 테스트 코드를 생성하는 모조( Mojo )가 동작하는 순간을 의미한다.
process-test-sources컴파일하기 전에 테스트 소스 코드에 필요한 작업을 수행한다.
이 단계에서 소스코드는 수정, 변환 또는 복사될 수 있다.
generate-test-resources테스트 관련 리소스 생성을 허용한다.
process-test-resources테스트 관련 리소스의 처리, 변환, 재배치가 가능하다.
test-compile단위 테스트 소스 코드를 컴파일 한다.
test컴파일된 단위 테스트를 실행하고 그 결과를 표시한다.
package실행 가능한 바이너리 파일들을 JAR나 WAR 같은 배포용 압축 파일로 묶는다.
pre-integration-test통합 테스트를 준비한다.
이 경우 통합 테스트는 실제 배치 환경의 코드를 테스트하는 것을 말한다.
이 단계에서 위에서 묶은 압축 파일을 서버에 배치할 수 있다.
integration-test실제 통합 테스트를 수행한다.
post-integration-test통합 테스트 준비 상태를 해제한다.
이것은 테스트 환경의 리셋 또는 재초기화 과정을 포함할 수 있다.
verify배치 가능한 압축 파일의 무결성과 유효성을 검증한다.
이 과정 이후에 압축 파일들이 설치된다.
install압축 파일을 로컬 메이븐 디렉터리에 추가한다.
이로 인해 이것에 의존하는 다른 모듈들을 사용할 수 있게 된다.
deploy압축 파일을 원격 메이븐 디렉터리에 추가한다.
더 많은 사용자들이 이 아티팩트( artifact )를 사용할 수 있게 된다.

코디네이트( Coordinates )

코디네이트는 메이븐 프로젝트를 식별하기 위한 유일한 값들을 정의한다.

코디네이트는 공간 내에서 특정 위치를 위한 주소이다.

각 프로젝트를 인식할 수 있는 주소와 같은 역할을 한다.

한 프로젝트가 다른 프로젝트에 연관되어 있거나 디펜던시나 플러그인으로 사용되는 경우 또는

부모 프로젝트에 연관되는 경우 해당 프로젝트를 정확하게 지칭할 수 있게 해 준다.

코디네이트는 아래의 정보들로 구성된다.

groupId

프로젝트 제작을 책임지는 개체 또는 조직을 나타낸다.

이름 명명 규칙에 따르면 groupId는 조직의 도메인을 뒤집어서 표기한다.

예를 들어 com.saakmiso.example는 groupId가 될 수 있다.

artifactId

실제 프로젝트 즉, 아티팩트( artifact )의 이름을 의미한다.

각 프로젝트를 식별하기 위해 사용된다.

version

프로젝트의 특정한 배포 버전을 의미한다.

지원되는 형식은 mmm.nnn.bbb-qqqqqqq-dd 와 같다.

여기서 mmm은 메이저 버전 숫자,

nnn은 마이너 버전 숫자,

bbb는 버그 수정 레벨을 나타낸다.

가끔 qqqqqq( 수식어 ) 또는 dd( 빌드 숫자 )를 버전 번호에 추가로 붙일 수 있다.

packaging

프로젝트는 기본적으로 jar 형식으로 패키징되며 프로젝트가 만드는 아티팩트로 결정된다.

만약 war라고 준다면 웹 어플리케이션을 의미한다.

샘플 플로젝트의 pom.xml 파일의 프로젝트 코디네이트 정보는 아래와 같다.

<!-- pom.xml 프로젝트의 코디네이트 정보 -->

이러한 코디네이트 정보를 통해 특정 프로젝트의 아티팩트를 이용할 때

다른 프로젝트에 디펜던시를 추가할 수가가 있다.

생성한 프로젝트를 외부에서 사용하기 위해서는 프로젝트를 메이븐 저장소에 배포하고,

사용하고자 하는 프로젝트의 pom.xml에 디펜던시를 아래와 같이 추가하면 된다.

<!-- pom.xml 파일의 디펜던시 설정 -->

저장소( Repositories )

메이븐 저장소는 보통 디렉터리 트리구조이기 때문에 아티팩트들이 어떻게 저장되어 있는지 쉽게 볼 수 있다.

메이븐을 압축 / 해제했던 디렉터리의 사이즈를 살펴보면 몇 메가바이트에 불과하다.

필요 시 원격 저장소로부터 패치되며, 핵심 플러그인인들이나 디펜던시를 가진 플러그인이나 라이브러리들을 다운로드 한다.

메이븐이 원격 저장소로부터 아티팩트를 다운로드하면 로컬 저장소에 저장되고 다시 다운로드하지 않는다.

메이븐 로컬 저장소는 사용자 컴퓨터에에 있는 디렉터리를 의미한다.

로컬 저장소는 고성능 로컬 캐시처럼 동작하며 디펜던시 분석 결과인 아티팩트들을 다운로드하여 저장해둔다.

저장소의 변경을 원하다면 [Window] → [Preferences] → [Maven] → [User Settings]에 들어가서 상세항목들을 변경한다.

/<groupId>/<artifactId>/<version>/<artifactId>-<version>.<packaging>

메이븐의 중앙 저장소의 구조는 http://repol.maven.org/maven2/를 통해 살펴볼 수 있다.

기업 내에서 특정 프로젝트에서 사용할 특수한 라이브러리를 사용하고자 할 때

기업 내부의 네트워크 안에 위치한 커스텀 저장소에 이러한 라이브러리를 설정하면 된다.

아래 그림은 기업 내에서 커스텀 저장소 없이 개발하는 경우와 기업 내부에 커스텀

저장소를 이용하여 개발발하는 경우를 비교해 보여 준다.

기업 내부 사내( in-house ) 개발일 경우 사내 개발 모듈로부터 배포한

아티팩트를 포함하고 있는 커스텀 저장소( 추가적인 원격 저장소 )를 설정할 수 있다.

settings.xml에 있는 요소를 사용하여 원격 저장소를 추가할 수 있다.

디펜던시 관리

프로젝트에 포함된 디펜던시는 pom.xml 파일안에 요소를 사용하여 표현하며

POM의 일부로 메이븐에서 사용하게 된다.

프로젝트 디펜던시들은 저장소에 저장된다.

디펜던시 분석이 성공하려면 아티팩트를 담고 있는 저장소로부터 필요로 하는 아티팩트들을 찾는 것에 달려있다.

POM에 있는 프로젝트의 디펜던시 정보를 기반으로 디펜던시 분석기는 다음 순서에 따라 디펜던시를 분석하려고 시도한다.

  1. 로컬 저장소에서 디펜던시를 확인한다.
  2. 원격 저장소 리스트에서 디펜던시를 확인한다.
  3. 1과 2가 실패하면 에러를 보고한다.

기본적으로 두 번째 단계와 관련된 원격 저장소는 세계 어느 곳에서나 접근 가능한 중앙 집중화된 메메이븐 저장소다.

대부분의 오픈소스 프로젝트에서 사용하는 아티팩트들은 이 곳에 포함되어 있다.

또한 앞서 소개한 커스텀 저장소가 추가적인 원격 저장소로 더해 질 수 있다.

pom.xml 파일 구조

pom.xml 파일은 프로젝트의 세부 메타 데이터 정보를 포함하며 크게 10개의 항목으로 구성된다.

프로젝트의 일반 정보, 버전 및 설정 관리, 빌드 환경, 라이브러리 저장소 및 디펜던시 등의 내용을 포함하고 있다.

pom.xml 구조를 간략하게 정히하면 다음과 같다.

General Information프로젝트 이름, 설명, 버전 정보 등을 기술
Organization프로젝트 조직 정보(이름, 홈페이지 URL 등)
Project Team and Collaborations tools형상 관리 서버, 이슈 트랙커, 통합 빌드 서버 정보 등
Build인코딩 정보등 빌드 라이프 사이클 환경 설정, 각종 플러그인 설정
Reporting리포트 생성 기능을 설정
Distribution Management배포 환경 설정
Profiles이기종 환경에서의 이식성을 높여 주기 위한 빌드 설정
Properties프로젝트 프로퍼티( property : 속성 )를 설정

디펜던시 관리를 위해서는 내부에 항목을 사용하여 정의할 수 있다.

<!-- JUnit 디펜던시 추가 -->

디펜던시 필수 선언 항목은 세 가지다

항목설명
groupId부분적인 프로젝트나 조직에서의 라이브러리 집합을 식별하기 위해 제공된다.
artifactId프로젝트의 실제 이름으로 groupId와 합쳐져서 프로젝트 식별에 쓰인다.
version선언한 디펜던시 아티팩트의 버전으로 프로젝트 내에서 사용하는 아티팩트의 일관성을 지킬 수 있다.

추가로 scope 항목을 정의할 수 있는데, JDK나 웹 어플리케이션 서버( WAS )에서 제공하는 라이브러리와

디펜던시에서 선언한 라이브러리 간의 충돌이 일어나는 경우가 발생할 수 있으므로 사용하고자 하는

라이브러리의 스코프( scope )를 명시해야 하는 경우가 있다.

디펜던시 scope적용범위
compile기본값으로 모든 클래스 패스에서 사용 가능하다.
provided컴파일과 유사하나 패키지에는 포함되지 않는다.
컨테이너나 JDK에서 제공한다.
예) 웹 어플리케이션을 위한 Servlet API, JSP, API
runtime컴파일러라가 아닌 런타임 시에 사용된다.
예) JDBC Drivers
test테스트 단계에서만 사용된다.
예) JUmit
systemprovided와 유사하나 개발자가 직접 JAR 파일을 제공해야 하고
저장소에서 지정한 dependency를 차지 않는다.

디펜던시 scope가 선언되지 않았다면 기본값으로 complie이 적용된다.

디펜던시 분석은 먼저 로컬 저장소에서 디펜던시를 확인하고, 다음으로 원격 저장소 리스트에서 디펜던시를 확인하고,

둘다 실패하면 디펜던시 에러를 발생시킨다.

좋은 웹페이지 즐겨찾기