posted by 아겔-_- 2010.07.18 03:49


 이 문서에서는 간략히 leiningen이 무엇인지 설명하고, 이를 이용하여 기본적인 클로져 개발 사이클에 필요한 내용을 HOWTO형식으로 정리한다. 그리고 실용적으로 개발하기위한 TDD, REPL, 이클립스 연동과 같은 부분들 또한 설명하도록 한다.





leiningen?

 leiningen은 클로져 프로젝트 관리도구다.

 자바의 Apache Maven와 같은 위상의 도구로서 다음과 같은 작업을 기본적으로 지원한다.

 * 초기 스켈레톤 생성
 * Maven, Clojars등의 원격저장소를 통한 외부라이브러리 의존성관리
 * 빌드, 테스트, 패키징 등 일반적인 개발사이클에서 필요한 작업의 자동화
 * ...그밖에 다양한 기능들을 플러그인을 통하여 지원

 Maven의 pom.xml와 같이 project.clj을 통하여 훨씬 간결하고 선언적으로 프로젝트를 관리하도록 지원한다.


 Leiningen은 단일한 스크립트로서 시스템에 설치하며, 유닉스/리눅스/OSX에서의 설치는 단순히 'lein' 쉘 스크립트를 받아 'lein self-install'로 완료하며, 윈도의 경우에는 별도의 문서를 참고하여 'self-install' 대신 직접 두세단계의 절차를 진행하여 설치한다.
 
 Leiningen은 어떤 GUI 도구나 IDE이 아니라 CMD.exe이나 유닉스쉘을 통해 접근하는 스크립트이므로 이 문서에는 쉘이나 콘솔에서 명령을 실행하는것을 기준으로 설명하겠다.

 
 * Leiningen : <http://github.com/technomancy/leiningen>
 * Apache Maven : <http://maven.apache.org/>
 * Clojars : <http://clojars.org/>









프로젝트 시작

 프로젝트는 new명령으로 다음처럼 생성한다.
 
 > lein new net.adaltan/foobar


 
 new명령은 현재 디렉토리에 프로젝트 스켈레톤을 생성하고, 'net.adaltan'은 그룹ID, 'foobar'은 프로젝트ID이다.
 결과적으로 'net.adaltan.foobar'을 최상위 네임스페이스로하는 프로젝트를 생성한다.
 
 
 
 
 
 




의존성

 개발을 하면서 외부 라이브러리를 참조하여야 할 경우, .jar 파일을 classpath에 추가하거나 Maven의 경우 pom.xml에 이를 기재하였다. 이와 같이, project.clj에 외부 의존성을 지정할수있다.
 
 기본적으로 생성한 프로젝트의 project.clj은 보통 다음과 같다.

(defproject net.adaltan/foobar "1.0.0-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.1.0"]
                 [org.clojure/clojure-contrib "1.1.0"]])


 :dependencies 벡터에 원하는 의존성을 추가하여준다. 여기에 적는 방법은 "group-id/artifact-id version"와 같은 형식으로 적는다. 예를 들어, 다음과 같은 형태로 지정한 pom.xml은...
 
    <dependency>
        <groupId>com.lowagie</groupId>
        <artifactId>itext</artifactId>
        <version>2.1.3</version>
    </dependency>

 다음의 project.clj의 의존성 벡터로 표현한다.
 
    [ ... [com.lowagie/itext "2.1.3"] ... ]

 매우 간단하다. :dependecies은 애플리케이션의 실행시간에 참조할 classpath에 추가하는 의존성이다.
 
 leiningen 자체도 의존성을 표현하는데, 이는 leiningen의 플러그인을 지정할때 사용한다. 이때는 :dev-dependencies을 키로 하며, dependecies와 같은 형식으로 지정한다. SLIME연동, 이클립스 프로젝트 연동, ...등과 같이 leiningen의 기능을 플러그인을 통해 확장할때 :dev-dependencies을 이용한다.
 
 Clojars이나 Maven repository에서 검색하여 의존성을 지정하면된다.
 
 의존성을 project.clj에 지정하였다면 "lein deps" 명령으로 이를 다운로드 받아와 적용한다.
 
 > lein deps
 ...

 추가적인 jar 저장소를 지정이 가능한데, 이들에 대해서는 이후 보일 예제 project.clj을 참고하라.

 
 
 



빌드, 패키징...

 일반적인 애플리케이션 빌드 과정에 속하는 단계들이므로 별도의 설명없이 명령들을 나열하겠다.

 프로젝트를 컴파일
 > lein compile
 
 프로젝트 클린
 
> lein clean

 프로젝트 jar로 패키징
 > lein jar
 
 프로젝트 jar로 패키징 (모든 의존성을 포함한 하나의 uberjar로 생성)
 > lein uberjar


 jar, uberjar의 경우 생성할 결과 파일이름, 디렉토리를 지정할수있으며, uberjar의 경우 standalone jar으로 만들수있으므로 메인 클래스를 지정 가능하다. 이들 키워드에 대해서는 이후에 설명할 예제 project.clj을 참고하라.
 
 프로젝트를 빌드하여 시스템 로컬 저장소에 설치
 > lein install


 프로젝트 빌드시에 소스파일 이외에도 classpath으로 복사하는 리소스 파일등이 있다면 :resources-path을 통해 해당 경로를 지정 가능하다.

 또한, .java 파일들을 프로젝트에 함께 포함하여 클로져+자바로 프로젝트를 개발한다면 lein-javac을 이용하여 이를 함께 컴파일하도록 한다.

 * lein-javac : <http://github.com/antoniogarrote/lein-javac>






REPL, 테스트

 코딩을 하고 자신이 작성한 모듈을 점진적으로 테스트하고 디버깅하려면 REPL을 이용하는게 바람직하다.
 이를 위해 다음처럼 실행한다.

 > lein repl


 위와 같이 한 다음 use을 이용하여 자신이 개발한 네임스페이스를 불러와 테스트하면된다.

 user> (use 'net.adaltan.foobar.core)


 또한, leiningen 기본 프로젝트는 항상 단위테스트 파일을 생성하는데 이들 파일은 src의 레이아웃과 같이 test 디렉토리에 위치한다. 예를 들어, src/net/adaltan/foobar/core.clj을 위한 테스트 파일인 test/net/adaltan/foobar/core_test.clj 파일은 다음과 같이 생성해준다.

(ns net.adaltan.foobar.core-test
  (:use [net.adaltan.foobar.core] :reload-all)
  (:use [clojure.test]))

(deftest replace-me ;; FIXME: write
  (is false))

 이는 clojure.test(<http://richhickey.github.com/clojure/clojure.test-api.html>)을 이용한 단위테스트다.

 함수를 작성하고 이에 대한 테스트를 적용하려면 이렇게 단위테스트를 작성하고 다음의 명령으로 테스트해본다.

 > lein test






예제 project.clj

 이 섹션에서는 예제 project.clj을 보여 어떠한 키워드들을 기본적으로 지원하는지 보여 설정가능한 기능을 파악하도록한다.

<http://github.com/technomancy/leiningen/blob/master/sample.project.clj>

;; 프로젝트 이름 "sample", group-id은 "org.example".
(defproject org.example/sample "1.0.0-SNAPSHOT" ; 버젼은 "1.0.0-SNAPSHOT"
  ;; Clojars에 업로드하였을때 설명으로 표시될 내용, 이를 통해 검색이 가능함
  :description "A sample project"
  ;; Clojars에서 표시할 프로젝트 URL
  :url "http://example.org/sample-clojure-project"
  ;; 프로젝트 메일링리스트. 혹시 다수개라면 리스트로 묶어서 여러개를 나열하도록한다.
  :mailing-list {:name "sample mailing list"
                 :archive "http://example.org/sample-mailing-list-archives"
                 :other-archives ["http://example.org/sample-list-archive2"
                                  "http://example.org/sample-list-archive3"]
                 :post "list@example.org"
                 :subscribe "list-subscribe@example.org"
                 :unsubscribe "list-unsubscribe@example.org"}
  ;; 프로젝트 라이센스 및 배포정보.
  ;;   다수개의 라이센스를 채택할경우 :license 키 대신에 :licenses을 이용해 나열한다.
  ;;   :distribution은 :repo이거나 :manual인데 공개 저장소에 올려도 좋다면 :repo을 선택한다.
  :license {:name "Eclipse Public License - v 1.0"
            :url "http://www.eclipse.org/legal/epl-v10.html"
            :distribution :repo
            :comments "same as Clojure"}
  ;; 의존성
  :dependencies [[org.clojure/clojure "1.1.0"]
                 [org.clojure/clojure-contrib "1.1.0"]
                                 ;; :exclusions 키를 이용하여 포함하지 않을 네임스페이스를 지정함
                 [log4j "1.2.15" :exclusions [javax.mail/mail
                                              javax.jms/jms
                                              com.sun.jdmk/jmxtools
                                              com.sun.jmx/jmxri]]]
  ;; 의존성을 다운로드할때 lib/ 디렉토리를 묵시적으로 삭제하고 다시 받을지 지정.
  :disable-implicit-clean false
  ;; 개발시 필요한 의존성을 나열. uberjar으로 jar로 묶었을때 이들은 포함하지 않는다.
  :dev-dependencies [[org.clojure/swank-clojure "1.2.1"]]
  ;; 자바연동과 :gen-class을 위해 AOT 컴파일할 클래스를 나열. :namespaces은 :aot와 같다.
  :aot [org.example.sample.SampleClass]
  ;; uberjar로 묶었을때 "main"으로 적용할 네임스페이스
  :main [org.example.sample]
  ;; reflection call에 대한 경고?
  :warn-on-reflection true
  ;; 기본 저장소를 사용할지?
  :omit-default-repositories true
  ;; 추가적인 저장소를 지정
  :repositories { "java.net" "http://download.java.net/maven/2"
                  "jboss" "http://repository.jboss.com/maven2/"}
  ;; 기본적인 프로젝트 파일 경로와 다르게 설정하려면 다음의 경로들을 설정할것
  :source-path "src/main/clojure"
  :library-path "target/dependency"
  :test-path "src/test/clojure"
  :resources-path "src/main/resources"
  :native-path "src/native" ; JNI, Native 의존성 파일들의 위치
  :jar-dir "target/" ; 프로젝트 jar 파일 생성 디렉토리
  :jar-name "sample.jar" ; 'lein jar'로 생성한 파일의 이름
  :uberjar-name "sample-standalone.jar" ; 'uberjar'로 생성할 파일의 이름
  ;; JVM에 전달할 인자
  :jvm-opts "-Xmx1g")

 기본설정이 있으므로 위에 나타난 키워드들을 모든 project.clj에 적을 필요는 없지만, 필요한 경우에는 이를 오버라이드하여 설정가능하므로 알아두는게 좋다.






Apache Maven, 이클립스(counterclockwise)와의 연동

 'pom' 명령으로 쉽게 pom.xml을 생성하여 Apache Maven 프로젝트로 전환이 가능하다.
 클로져 프로젝트는 Apache Maven을 통해 Leiningen을 통해 관리하듯이 관리가 가능하다.

 * Clojure Maven Plugin : <http://github.com/talios/clojure-maven-plugin>

 하지만, Leiningen만큼 많은 플러그인을 통한 기능을 지원하지는 않는것으로 보인다.

 마지막으로 가장 많이 사용하는 자바 개발도구인 이클립스와 이클립스를 위한 클로져 플러그인인 counterclockwise을 위한 프로젝트 파일을 생성하는 lein-eclipse을 소개한다.

 다음의 URL을 참고하여 :dev-dependencies에 lein-eclipse을 추가한 다음 다음처럼 하여 이클립스 프로젝트 파일을 생성한다.

 * counterclockwise : <http://code.google.com/p/counterclockwise/>
 * lein-eclipse : <http://clojars.org/lein-eclipse>

 > lein deps
 > lein eclipse

 이렇게 프로젝트를 생성한 다음엔 Import하여 바로 이클립스에서 개발이 가능하다.







신고

티스토리 툴바