제공 : 한빛 네트워크
저자 : Matthieu Riou
역자 : 백기선
원문 : Introducing Raven: An Elegant Build for Java
이성적으로
모든 자바 프로젝트가 통과해야 할 일련의 단계들이 있다. 빌드 시스템 작성과 보통 그 전에 해야 하는 빌드 시스템 선택하기이다. 상당히 오랜 기간 자바진영에서는 이 분야에 대해 괄목할만한 개선이 보이지 않았다. Ant 1.1은 2000년 7월에 배포가 되었고 Maven은 2004년에 만들어졌다.
혁신의 부재는 이상하게 느껴질 수도 있다. 하지만 규모가 큰 프로젝트(대부분은 일정이 지연된다.)에서 빌드 스크립트를 작성하는 것은 상당한 시간소비를 야기한다. 작성한 빌드가 보통은 사용자에게 전달되지 못하고, 그 것을 유지하기 위해 투자한 시간이 마치 버려진 것처럼 보이기도 합니다. 하지만 차선책으로 새로운 빌드 시스템을 선택하면 진짜로 낭비한 시간이 돼 버린다. 따라서 자연스럽게, 최고의 빌드는 여러분이 스크립트를 작성하고, 디버깅하고 유지보수 하는 대부분의 시간을 단축시켜 주는 것이어야 한다.
필자는 자바 진영에서 사용할 수 있는 솔루션들에 크게 실망을 했기 때문에, Raven을 사용해보기 시작했다. 다른 개발자들에게 들어보니 나 혼자만 그런 것은 아닌 것 같다.
자 이제 좀 논쟁거리가 될 만한 말을 해야겠다. Ant와 Maven 둘 다 장단점이 있지만, 이런 툴들은 완전한 스크립트 환경에 비하면 장난감에 불과하다. 조건, 반복, 예외, 복합 데이터 구조를 생각해보자. (미처 생각해보지 못한 자세한 부분까지 생각해보면)이들 대다수는 매우 변칙적이고 특이한 형태로 대부분의 프로젝트에 등장한다. 이 문제들을 해결하기 위해 가장 강력한 해결책은 무엇일까? 단순한 XML 문법일까 아니면 완전 강력한 스크립트 언어일까? (Turing을 언급하려는 것은 아니다.) Copy source, target을 사용할 것인가 아니면 XML 3줄을 작성할 것인가? 툴이 해결해 줄 수 있는 영역 밖에서는 어떤 대책을 가지고 있는가?
실용적인 것 얻기
Raven은 Ruby 라는 동적 언어와 그것의 뛰어난 빌드 툴인 Rake에 기반하고 있다. 걱정하지 말아라. Raven을 사용하거나 본 글을 읽기 위해서 이 둘을 모두 알아야 할 필요는 없다. 이 들에 대해서 거의 배우지 않고도 간단하게 시작할 수 있다. Rake는 그 자체만 보면 Ant와 비슷하다. 태스크와 그들 사이에 의존성을 정의할 수 있다. 대신 문법이 훨씬 아름답다. 예제를 보자:
task "hello" do
puts "Hello"
end
task "world" => "hello" do
puts "World"
end
Rake를 설치했다면, Rakefile 이라는 이름의 파일에 위의 코드를 넣고 해당 파일이 위치한 디렉터리에서 커맨드 라인에 rake world를 실행하라. 여러분이 예상한 대로 동작할 것이다. 수행해야 할 문장이 한 문장일 때는 do ... end 대신에 { ... } 블록을 사용해서 문법을 좀 더 간단 명료하게 할 수 있다. 하지만 이 예제는 보통의 경우처럼 한 줄 이상의 코드를 task 바디 부분에 가진다는 가정하에 작성했다. 그리고 task 블록에 외부 라이브러리에 의존하거나, 객체를 생성하고 메소드를 호출하는 등의 상당량의 Ruby 코드 (심지어 JRuby를 사용해서 자바 코드를)넣을 수 있다. 빌드는 여러분이 원하는 만큼 복잡해질 수도 있고 간단해 질 수도 있다.
Rake가 가진 단점은 일반 Ruby 코드를 감싸는 것과 같은 매우 단순한 task만 제공하고 있다는 것이다. 내부 코드에서 무엇을 할지 알려주어야 한다. 바로 이 부분에서 Raven이 빛을 발한다. 자바 개발자들의 삶을 좀 더 편하게 해주기 위해서, Raven은 일반적인 자바 빌드 시스템에서 필요로 하는 컴파일이나 jar 의존성 인식과 같은 것들을 구현한 태스크들을 추가했다. 라이브러리처럼 클래스나 메서드의 집합체는 아니지만 자바 빌드에 적합하며 재사용 가능한 태스크들의 집합이다. 따라서, 여러분이 하려는 빌드들을 본 글의 남은 부분에서 Raven으로 구현되어 있는 모습을 볼 수 있을 것이다.
그런데 잠시만, Raven을 어떻게 설치해야 하는지 아무것도 이야기 해주지 않은 것 같다. 가장 빨리 시작할 수 있는 길은 JRuby(자바로 구현한 Ruby 인터프리터)로 패키징한 Raven을 사용하는 것이다. 필요로 하는 모든 것들이 그 안에 들어있다.
- JRuby로 패키징한 Raven 배포판을 다운로드 한다.
- 디스크에 압축을 풀고 그 위치를 JRUBY_HOME 환경 변수를 설정한다.
- PATH 환경 변수에 %JRUBY_HOME%bin을 추가한다.
- 커맨드 창에서 jruby –v 를 실행하여 설치가 잘 됐는지 확인한다.
Ruby 인터프리터(좀 더 빨리 시작할 수 있다.)를 사용하는 구체적인 설치는 Raven 웹 사이트를 참조하라.
간단한 프로젝트
Raven을 어떻게 사용하는지 보여주기 위해서, 간단하지만 실제로 사용하고 있는 예를 가지고 시작할 것이다. Apache Commons Net을 빌드 할 것이다. Apache Commons Net 라이브러리는 FTP나 SMTP와 같은 다양한 네트워크 프로토콜에 대한 클라이언트 코드를 구현했다. 지금은, 이 프로젝트의 빌드가 Ant 기반으로 되어 있고 조금 복잡하다. 따라서 내가 Raven을 선물해주기 위해 매우 적절한 대상으로 보인다.
Raven은 특정 태스크들의 집합체(에 무언가를 더 할 수 있지만, 이것에 대해서는 뒤에서 살펴볼 것이다.)며, 모든 빌드를 Rake에서 관리한다. 따라서, 내가 보여줄 모든 코드는 Rakefile이라는 이름의 파일의 일부로 이 파일은 Commons Net의 최상위 폴더에 위치해야 한다. Rake를 실행하면, Rake는 항상 해당 스크립트 파일을 찾을 것이다.
다음은 초기화와 종속성 관리를 위한 첫 번째 조각이다.
require "raven"
require "rake/clean"
CLEAN.include ["target", "dist"]
dependency "compile_deps" do |task|
task.deps << "oro-oro"
end
처음 두 줄은 Raven 과 Rake clean을 담당하는 서브 태스크를 로딩한다. Ruby에서 require 명령은 import와 비슷하게 (Raven 처럼)전체 라이브러리를 읽어올 수도 있고 단일 파일을 읽어올 수도 있다. 세 번째 줄은 Rake에게 clean 테스트를 사용해서 어떤 디렉터리를 제거할 지 알려준다.
5 에서 7 번째 줄은 Raven의 dependency 태스크 사용법을 보여준다. Commons Net은 Jakarta ORO 라이브러리에 의존성을 가지고 있기 때문에, 그것을 dependency에 추가해 주었다. 이것은 단순하게 어떤 라이브러리 집합이 필요한지 기술하는 것이다. 태스트를 호출하면, 기본 Raven 저장소에서 라이브러리를 다운로드 하는 트리거가 동작한다. 그리고 그것에 종속성이 생기면 적절한 클래스패스를 생성하는데 이것은 나중에 살펴볼 것이다. 여기서 또 알아두어야 할 것은 한 번에 여러 라이브러리를 지칭할 수 있으며 버전 넘버를 줄 수도 있다. (Raven은 기본으로 가장 최신 버전을 사용한다.) 이런 모든 종류의 라이브러리 선언들은 dependency 태스트에서 유효하다.
task.deps << ["springframework-spring-core",
{ "taglibs-standard" => "1.1.2" }]
task.deps << ["axis2-kernel", "axis2-codegen"]
라이브러리 이름은 반드시 Maven 이름 규칙을 따라야 한다. 대시로 구분하는 groupdId와 artifactId를 구분한다. Raven 저장소를 브라우저로 열고 어떤 라이브러리들을 사용할 수 있는지 볼 수 있다. 비슷한 것이 없다면 이름의 일부만을 사용할 수도 있다. 이제 종속성을 다루는 것은 끝났다. 컴파일을 어떻게 하는지 살펴보자.
javac "compile" => "compile_deps" do |task|
task.build_path << "src/java"
end
jar "commons-net.jar" => "compile"
Javac 태스크는 Raven이 제공하는 또 다른 태스크다. 이 녀석이 무엇을 하는지 매우 쉽게 알 수 있다. => 표시는 해당 태스크가 의존성을 가지는 선수 요소를 나타낸다. 이것을 사용해서 Raven은 여러분 대신에 클래스패스를 자동으로 사용할 것이다. 빌드 경로를 설정하고 있는 부분을 주목해야 한다. 이 부분이 동작하려면 명시적으로 Commons Net이 소스파일을 src/java 밑에 두고 있어야 한다. 만약 src/main/java 밑에 있다면 별도의 설정이 필요하지 않기 때문에, 한 줄짜리 농담처럼 짧아질 것이다.
javac "compile" => "compile_deps"
끝으로, 컴파일이 완료되면, 모든 것들을 jar로 패키징한다. 이것은 jar 태스크의 역할이다. 만들어진 jar 파일 이름은 태스크 이름과 동일한 이름을 가진다. 이렇게 파라미터의 수를 줄일 수 있다.
지금까지의 설명으로, 여러분은 Commons Net 소스 배포버전 최상위 폴더에 위치한 10줄 가량의 Rakefile을 알 수 있게 되었다. 빌드를 실행하려면, rake commons-net.jar 를 실행하면 되며 target 디렉터리에 모든 것들이 만들어 질 것이다. Jar 파일을 빌드 할 때 기본으로 실행할 태스크를 다음과 같이 추가할 수 있다.
task "default" => "commons-net.jar"
역자 백기선님은 AJN(
http://agilejava.net)에서 자바 관련 스터디를 하고 있는 착하고 조용하며 점잖은 대학생입니다.
요즘은 특히 Spring과 Hibernate 같은 오픈소스 프레임워크를 공부하고 있습니다. 공부한 내용들은 블로그(
http://whiteship.tistory.com)에 간단하게 정리하고 있으며 장래 희망은 행복한 개발자입니다.