Maven은 빌드에 필요한 라이브러리를 아래와 같이 설정합니다.

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.2</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>commons-fileupload</groupId>
   <artifactId>commons-fileupload</artifactId>
   <version>1.1.1</version>
  </dependency>
  <dependency>
   <groupId>commons-lang</groupId>
   <artifactId>commons-lang</artifactId>
   <version>2.1</version>
  </dependency>
  ... ...
 </dependencies>

Buildr에서는 compile.with 메소드에 라이브러리 목록을 넘겨주어 의존성을 추가할 수 있습니다.

compile.with "org.apache.axis2:axis2:jar:1.2", "commons-fileupload:commons-fileupload:jar:1.1.1"

compile.with 메소드가 받아들이는 의존 라이브러리의 몇가지의 타입이 존재합니다.

1. 아티팩트(artifact) 스펙
위의 메이븐 pom에서 xml 형식으로 지정한 coordinates를 다음의 형식으로 지정합니다.

groupId:artifactId:packaging:version
groupId:artifactId:packaging:classifier:version

* 위의 형식 뿐 아니라 다양한 포맷으로 스펙을 지정할 수 있습니다. 여기를 참조하세요.

이렇게 아티팩트 스펙 형식으로 의존성을 추가하면 Buildr은 메이븐 레포지토리에서 해당 라이브러리를 다운받아 로컬에 설치합니다.

2. 다른 프로젝트
만일 다른 프로젝트의 패키징 결과물에 의존하고 있다면 그 프로젝트를 넘겨주면 됩니다.

compile.with projects('another-project', 'other-project')

3. 파일/디렉토리 경로
로컬에 존재하는 파일/디렉토리의 경로를 지정하여 의존성을 추가할 수 있습니다.
메이븐에서는 로컬 파일을 의존성에 추가하기 위해서 스코프를 system으로 잡고 파일의 절대경로를 지정하였습니다.

  <dependency>
   <groupId>weblogic</groupId>
   <artifactId>weblogic-api</artifactId>
   <version>10.0</version>
   <scope>system</scope>
   <systemPath>C:/bea/weblogic/xxx/wls-api.jar</systemPath>
  </dependency>

Buildr에서는 프로젝트에 대한 상대경로 또는 절대경로 모두 가능합니다.

compile.with 'C:/bea/weblogic/xxx/wls-api.jar' , 'lib/my-api.jar',

경로 지정시 path_to 메소드를 활용하는 것이 좋습니다.

compile.with path_to(:source,:main,:webapp,'WEB-INF/lib/my-api.jar')

또한 glob 패턴을 적용하여 파일을 지정할 수 있습니다. 메이븐도 이런게 되면 참 좋겠습니다.

compile.with FileList[path_to(:source,:main,:webapp,'WEB-INF/lib/*.jar'), 'lib/**/*.jar']

여기서 Rake::FileList를 사용했습니다. FileList 없이 그냥 패턴 문자열을 그대로 compile.with 메소드에 넘겨줄 수도 있습니다. 그러나 그렇게 되면 패턴 문자열이 해석없이 그대로 compile.dependencies에 할당되기 때문에 (단위테스트 등에서) 예상치못한 문제가 발생할 수 있습니다. 그래서 FileList를 통해 패턴을 해석한 뒤에 compile.with에 넘겨주는 것을 권장합니다.

Buildr에서는 놀랍게도 파일뿐 아니라 디렉토리도 의존성에 추가하는게 가능합니다.

compile.with '../AAA/classes'

다만, 이렇게 디렉토리를 의존성에 추가하고 war로 패키징하게 되면 war에 그 디렉토리가 포함이 됩니다. 이때는 그 디렉토리를 아래처럼 패키지 libs에서 빼주면 됩니다.

DEPEND_CLASSES =  '../AAA/classes'
compile.with DEPEND_CLASSES
package(:war).libs -= artifacts(DEPEND_CLASSES)

Posted by 에코지오
,
Ant는 세밀하게 빌드를 설정하기 좋지만 미리 정해진 규칙이나 프로세스가 없기 때문에 알파부터 오메가까지 모든걸 내가 다 정의해야한다. Ant는 나에게 거의 모든 자유를 주며, 복잡한 빌드환경에서도 적절히 대처할 수 있는 온갖 task를 제공해준다. 그러나 자유에는 책임이 따르듯이 규칙과 절차를 만드는 몫은 나에게 있다. 이렇게 수립된 규칙과 절차는 프로젝트마다 제각각이어서 대부분 호환되지 않는다.

Maven은 Ant에 비해 자유도는 낮지만 규칙을 잘 따른다면 작업을 쉽게 처리할 수 있다. 널리 통용되는 빌드 프로세스(phase)와 수행할 작업이 내장되어 있어서 설정을 최소화해주며, 의존관계의 라이브러리를 체계적으로 다루는 능력이 뛰어나다.(원래 ant는 의존성 관리를 하지 않으나 ivy를 이용하면 ant에서도 충분히 maven처럼 의존성 관리를 할 수 있다고 함) 그러나 Ant만큼 풍부하고 작은 단위의 task는 제공하지 않고 대신, plugin이라는 다소 큰 덩어리의 도구를 제공한다. 딱딱한 빌드절차, 묵직한 plugin 등 커스터마이징이 극히 제한적이어서 국내 SI프로젝트처럼 국제적인 상식(?)을 잘 따르지 않는 복잡한 빌드환경에서는 적합하지 않을 수 있다.

그럼 Ant의 높은 자유도와 Maven의 다듬어진 프로세스를 두루 갖는 그런 빌드 도구는 없는가?
최근 Ant와 Maven을 뛰어넘기 위해 스크립트 언어 기반으로 빌드를 설정하려는 시도가 많아지고 있다.

찾아보면 BuildrGradle, Raven 대충 이정도 나온다.

Raven과 Buildr은 ruby 언어로, Gradle은 groovy 언어로 빌드를 작성한다.
Raven은 Ant 스타일과 비슷하고 Buildr과 Gradle은 Maven의 사상이 들어있다.

자 이쯤에서 하나 골라잡아 공부해야 하는건가?
Posted by 에코지오
,

리소스설정치환에 관한 글에서 얘기했듯이 개발소스와 빌드결과물은 여러 서버를 거치면서 생성,이동,배치됩니다.

개발자 로컬PC -> (소스저장소) -> 빌드서버(CI서버) -> 개발서버 -> 테스트서버 -> 스테이징서버 -> 운영서버

1차원적으로 일렬로 나열하니까 간단해보이지만 실상 어플리케이션의 이동경로는 2차원적으로 설계됩니다. 마치 수학자 오일러가 최단코스를 찾기 위해 고민한 것처럼 우리는 각각의 서버를 거점으로 하는 배포 코스를 그려가야 합니다.(배포의 위상수학? 배포 아키텍처? 음.. 배포 아키텍처가 맘에 드는군요.)

하지만 너무나 다양하고 특이한 개발환경이 많이 존재하기 때문에 배포코스에 관한 뚜렷한 공식은 없는것 같습니다. 아래에 저의 경험과 동료들의 의견 그리고 온라인상의 자료 등을 종합해서 크게 3가지의 대표적인 코스를 그려보았습니다. 어떤게 베스트 프랙티스다라고 딱부러지게 말씀은 못드리겠군요.
(코스에서 테스트서버는 포함하지 않았습니다. 솔직히 제가 겪었던 모든 프로젝트에서는 개발서버가 테스트서버,스테이징서버의 역할을 모두 겸하고 있었습니다.)

1.빌드서버 중심 코스
-개발서버용, 스테이징서버용, 운영서버용 어플리케이션(빌드산출물)을 모두 빌드서버에서 생성
-어플리케이션은 빌드서버에서 각각의 서버로 전송되어 배포됨


-또는 스테이징 서버에 전송된 어플리케이션을 다시 운영서버로 전송하기도 함



 

2.소스저장소 중심 코스
-개발서버, 스테이징서버, 운영서버가 개별적으로 소스장소로부터 전체 개발소스를 취합하여 각각의 환경에 맞는 어플리케이션을 빌드하여 배포

 

3.혼합된 코스
-개발서버용 어플리케이션은 빌드서버에서 생성하여 개발서버로 전송되어 배포
-스테이징서버용은 스테이징 서버가 소스저장소로부터 전체 개발소스를 취합하여 빌드하고 배포
-스테이징서버에서 빌드된 어플리케이션을 운영서버로 전송

자, 어떤 코스를 타시렵니까?

Posted by 에코지오
,

우리가 개발하는 어플리케이션은 최종 사용자에게 서비스되기 전에 여러 장소를 거칩니다.

개발자 로컬PC -> (소스저장소) -> 빌드서버(CI서버) -> 개발서버 -> 테스트서버 -> 스테이징서버 -> 운영서버

불행히도 이렇게 거쳐가는 서버에 따라서 환경/정책이 조금씩 다르기 때문에 우리는 개발소스에 포함된 설정값을 다르게 가져가야 합니다. 예전에는 소스가 서버 사이를 옮겨갈 때마다 수작업으로(vi 에디터로) 설정값을 매번 편집을 해주거나, 설정값을 포함한 파일을 빼고(!) 소스를 옮기기도 했습니다. 여러분은 어떤 방법으로 설정값을 다르게 세팅하셨습나요?

여기, 좀더 우아한 방법이 있습니다.

아, 그보다 먼저 설정값을 포함하는 파일을 보통 뭐라고 부르나요? 설정파일 ? 리소스 파일?  뭐라고 부르든 리소스는 주로 xml, properties 로 작성을 하죠. 레일스에서는 yml인가 이런것도 있더군요. 암튼.

그리고 로컬,개발,운영 등 서버의 환경차이에 따라 리소스 파일을 변환하여 개발소스에 포함된 설정을 달리하는 일을 뭐라고 부르나요? 환경맞춤작업? 리소스변환리소스 설정치환? 이런 거에 대한 공식적인 용어를 들은 바가 없어서 일단은 제목에 리소스 설정치환이라고 임의대로 용어를 지었습니다. 혹시 널리 사용되는 용어를 아시는 분은 알려주세요. 언뜻 profile, portable이란 단어가 떠오르기는 합니다만....

아래는 제가 알고 있는 리소스 설정치환 방법 2가지입니다.(리소스 필터링, 리소스 교체 역시 모두 제가 임의로 만든 용어입니다. 더 나은 용어가 있으면 알려주세요.)

1. 리소스 필터링(filtering)
-빌드시에 설정 파일의 내용을 수정하는 방법
-설정값을 하드코딩하지 않고 정해진 형식의 변수로써 설정. 예를 들어 ${jdbc.password}.
-빌드 실행시 설정 파일 내의 변수는 환경에 적합한 설정값으로 치환됨
-환경별 설정값은 빌드스크립트에 보관됨(메이븐의 경우 프로파일 엘리먼트에)

2. 리소스 교체(replacing)
-빌드시에 기준(로컬) 설정 파일을 다른 파일로 교체하는 방법
-설정 파일을 환경별로 분리하여 관리
  (1)환경별로 별도 디렉토리를 만들어 기준 파일과 동일한 파일명으로 관리.
      예를 들어 /web/WEB-INF/web.xml 이 로컬PC용 설정파일이면,
      개발서버용 web.xml은 /conf/dev/web.xml에 두고 관리.
  (2)기준 설정파일과 동일한 경로에, 다른 파일명으로 관리.
      예를 들어 개발서버용 web.xml을 /web/WEB-INF/web.xml.dev 라는 이름으로 관리.
-빌드실행시 환경에 맞는 파일을 골라서 (1)빌드디렉토리로 이동하거나 (2)파일명을 변경하여 기준 설정 파일을 덮어씀

1번 리소스 필터링의 경우 메이븐에서는 Profile 기능과 Resource Filtering 기능을 이용하면 되고, 2번 리소스 교체의 경우 메이븐에서는 Profile 기능과 Ant의 copy 타스크 조합으로 처리할 수 있습니다. 아래는 메이븐에서 2번을 처리한 예제입니다.

... ...
<plugin>
 <artifactId>maven-antrun-plugin</artifactId>
 <executions>
  <execution>
   <id>resource-changing</id>
   <phase>process-resources</phase>
   <goals><goal>run</goal></goals>
   <configuration> <tasks>
          <copy todir="${project.build.outputDirectory}"
                    overwrite="true" preservelastmodified="true" verbose="true">
               <fileset dir="${project.build.outputDirectory}"
                          includes="**/${conf.replace.pattern}" />
                   <mapper type="glob" from="${conf.replace.pattern}" to="*" />
          </copy>
    </tasks></configuration>
  </execution>
...... .......
..... ........
 <profiles>
  <profile>
   <id>env-dev</id>
   <activation>
    <property><name>env</name><value>dev</value></property>
   </activation>
   <properties>
    <conf.replace.pattern>*.dev</conf.replace.pattern>
   </properties>
  </profile>
  <profile>
   <id>env-prod</id>
   <activation>
    <property><name>env</name><value>prod</value></property>
   </activation>
   <properties>
    <conf.replace.pattern>*.prod</conf.replace.pattern>
   </properties>
  </profile>
 </profiles>
Posted by 에코지오
,
지금 몸담고 있는 회사의 빌드관련 문서에는 build와 deploy를 이렇게 정의하고 있다.

- build : 소스를 실행가능한 모듈로 변환하는 것
- deploy : 빌드되어 실행가능한 결과물을 컨테이너에서 인식가능한 곳에 배치하는 것

build는 별로 어렵지 않지만 deploy는 곰곰이 생각할 것이 많다. 내용상으로는 install과 비슷하고, 번역용어가 distribution과도 헷갈린다.

실행가능한 모듈을 실행 가능한 곳에 위치시키고 설정하는 것. install 아닌가?  그러나 OS위에서 작동하는 소프트웨어에 대해서는 대개 install이라 하고, WAS같은 컨테이너에서 작동하는 어플리케이션이나 재사용 레포지토리에 저장되는 서비스모듈에 대해서는 deploy라고 구별하는 것 같다 (이게 소프트웨어와 어플리케이션/라이브러리의 차이에서 오는 개념구분인지도 모르겠다).

deploy 및 관련된 deployables, distribution의 개념에 대해 개인적으로 다시 정리해보았다.

* deployables(deployable application)
- 정의 : WAS같은 컨테이너 상에서 실행가능한 어플리케이션.
- 설명 : 자바 환경이라면 war,ear,jar 같은 것들을 말한다. deployables는 소프트웨어 distribution의 한가지 형태라고 봐도 될듯하다. 근데 이걸 뭘로 번역할지는? 배포물? 배포가능 어플리케이션?

* deploy (deployment)
- 정의 : deployables를 실제 컨테이너에 배치(arrange? allocate?)하는 작업. 
- 설명 : 간단히 말하면 어플리케이션을 WAS 상에 설치(install)하는 것이다. deploy는 전개,배치,배포 등으로 다양하게 번역되며 보통 디플로이 또는 배포라고 부른다. 그러나 배포라는 표현은 distribution과 혼동을 일으키기 쉽기 때문에 개인적인 의견으로는 배치가 더 맞는 표현이 아닐까 생각하지만, 역시나 배치라고 불렀을 때 batch와 발음이 같아서 또다른 혼동을 일으킬 수 있다.

* distribution
- 정의: 누군가에게 전달(delivery)하기 위해 패키징된 소프트웨어. (또는 그것을 전달하는 행위)
- 설명 : 사전적으로는 분배,배분,배포의 의미를 가지며 보통 배포본, 배포판으로 번역한다.

ps1. IBM 프로젝트 자동화 아티클에서는 deployment를 전개로, distribution을 배포로 번역했다. 헷갈리게시리...-.-;

ps2. 위의 deploy 정의는 웹어플리케이션 개발 영역에서나 통하는 정의이고 이전에 언급했듯이 메이븐에서는 아티팩트를 저장소에 올리는 걸 deploy라고 부른다. 그래서 메이븐 처음 접하면서 오해하게 되는 게, 메이븐이 웹어플리케이션을 WAS에 배포하는 것까지 간단하게 해준다고 생각한다. 천만에 만만에 콩떡인거다.

ps3. 소스를 컴파일/빌드하여 생긴 결과물을 뭐라고 부르는게 좋을까?
빌드결과물? 빌드산출물? 배포본? 에셋? 바이너리? 실행파일번들? 빌드모듈?
개인적으로는 빌드산출물이 맘에 든다.

ps3. deployables. 이건 deployable thing을 표현한 단어인데, cargo에서 아이디어를 따왔다. 실제 이런 단어가 있는건 아닌거 같다.
Posted by 에코지오
,

Hudson을 이용한다면 scm 저장소로부터 (메이븐 타입의) 프로젝트를 체크아웃/업데이트 받아서 특정 goal/phase를 실행하는 일은 Hudson의 몫이다. 그러니까 보통은 메이븐타입 프로젝트의 pom.xml로는 프로젝트 자체를 scm에서 체크아웃/업데이트 받는 일은 안한다.

하지만 허드슨을 이용하지 않고 허드슨의 일을 대신해서 프로젝트 소스를 scm으로부터 받아와서 빌드/배포 goal을 실행해야 하는 상황이라면 어떻게 해야할까?

scm에서 체크아웃/업데이트하고 프로젝트 안의 pom.xml에 대해 goal을 실행하는 쉘스크립트를 짜는 것도 한가지 방법일 수 있지만 이러한 작업을 하는 또다른 pom.xml을 만드는 것도 가능하다. maven-scm-plugin 플러그인을 이용하면 된다.

 <scm>
  <connection>scm:svn:https://127.0.0.1:8443/svn/XXX</connection>
 </scm>
 ... ...
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-scm-plugin</artifactId>
    <version>1.1</version>
    <configuration>
     <connectionType>connection</connectionType>
     <scmVersionType>tag</scmVersionType>
     <scmVersion>${scm.version}</scmVersion>
     <checkoutDirectory>workspace</checkoutDirectory>
     <workingDirectory>workspace</workingDirectory>
     <skipCheckoutIfExists>true</skipCheckoutIfExists>
     <username>user</username>
     <password>user</password>
     <goals>clean test war:inplace</goals>
     <profiles>env-staging</profiles>
    </configuration>
 </plugin>

위처럼 pom.xml을 만들어 놓고 scm:bootstrap 을 실행하면 메이븐은 checkoutDirectory로 지정된 workspace에 프로젝트 소스를 체크아웃 받는다. 체크아웃 받은 뒤에 프로젝트 root의 pom.xml에 대해서 <goals/>에 나열된 goal을 실행해준다.

mvn -Dscm.version=RB123 scm:bootstrap
Posted by 에코지오
,
단위 테스트케이스 소스가 없는 프로젝트를 메이븐으로 빌드할 때 test phase(단계)는 불필요한 것이 명백하다.

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
      <skip>true</skip>
    </configuration>
  </plugin>

또는 maven.test.skip 프로퍼티 값을 true로 설정하면 test phase에 바인딩된 surefire:test 골의 실행을 skip할 수 있다.

메이븐에서 deploy는 개발 결과 아티팩트(jar,war,....)를 리모트의 메이븐 저장소에 등록하는 것을 의미한다.
공통라이브러리나 오픈소스 라이브러리를 개발한다면 이게 deploy의 의미에 합당할지도 모르겠다.
그러나 아마도 대부분의 JEE 기반 웹시스템 개발 프로젝트에서 deploy는 웹어플리케이션을 WAS에 배치하는 것을 의미한다. test 단계를 skip한 것처럼 deploy 단계에 기본으로 바인딩된 deploy:deploy 골도 skip할 수 있다.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-deploy-plugin</artifactId>
    <configuration>
     <skip>true</skip>
    </configuration>
</plugin>

이렇게 deploy:deploy를 skip해놓고 anttrun 등으로 WAS에 배포본을 올리는 작업을 deploy phase에 바인딩하면 deploy가 진정 내가 원하는 deploy로 바뀌게 된다.

한가지 또 아쉬운게 있다면 deploy 단계 이전에 실행되는 install 단계도 skip할 수 있으면 좋겠는데 안타깝게도 install:install 모조에는 skip 옵션이 없다. 젠장.
Posted by 에코지오
,
요즘은 이상하게 애자일을 실천하는데 필요한 도구들에 관심이 간다.

빌드, 단위테스트, 버전관리, 버그관리, 자동화된 통합 등등.

CVS를 통한 버전관리를 제외하곤 사실상 거의 손놓은지 꽤 되었는데,

그동안 이런저런 툴들이 많이 쏟아져나왔다.


Younghoe.Info :: [리뷰] Continuous Integration으로 결점을 조기에 발견하기
http://younghoe.info/681


FindBugs : PMD랑 비슷한 놈인가?
http://findbugs.sourceforge.net/

Hudson : CruiseControl보다 직관적이라는데...
https://hudson.dev.java.net/

Java Power Tools : 새로 나온 책.
http://www.wakaleo.com/java-power-tools
Posted by 에코지오
,