클로져를 이용해서 웹 어플리케이션 간단한 것을 만들어 보고 싶은 욕망이 있을 것입니다. 만들어 보고 싶은 욕망은 의외로 간단하게 해결이 됩니다. 바로 컴포져(Compojure) 를 이용하면 쉽게 만들 수가 있습니다.

예전 포스트 를 보고 클로져 개발환경이 세팅되어 있다고 가정합니다.

compojure 로 만들어진 예제를 다운 받습니다.

    $ cd ~/work (없으면 만들어 줍니다)
    $ git clone git://github.com/weavejester/compojure-example.git


 
    $ cd compojure-example
    $ lein deps
    $ lein ring server


이렇게 하고 좀 오랜 시간을 기다리면 Port 3000 번에 Jetty 를 이용한 어플리케이션 서버가 떠 있는 것을 확인 하실수 있습니다. ( http://localhost:3000 에 브라우져로 접속하면 바로 확인 가능)

쉽게 만들었으면 배포하고 서비스하고 싶은 것이 개발자의 마음 아니겠습니까?

요즘 화두가 되고 있는 nginx 를 설치하고 그 웹서버와 위에서 만든 (실은 다운 받은) 예제를 연결시켜 보겠습니다. 

먼저 nginx 를 설치해줍니다.

    $ sudo apt-get install nginx


또 한번 외쳐줄 필요가 있습니다. 우분투 만세!!!

이제 nginx 의 설정파일은 손 봐줍니다. 정확히는 site 설정 정보가 되겠습니다.

 
   $ sudo emacs /etc/nginx/sites-available/default


   
   
server {

        #       root /usr/share/nginx/www;
        root /home/crazia/work/compojure-example/resources/public;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ /index.html;
        # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
                proxy_pass      http://127.0.0.1:3000;
    }

    location ~*    ^.+.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov|avi|wmv|mp3)$
    {
        break;
    }

   
    ....


   $ sudo /etc/init.d/nginx restart


위에 진하게 표시한 부분을 바꾸어 주시거나 추가해 주시면 됩니다. 간단하게 설명드리자면 기본적으로 정적인 파일들은 nginx 을 통해서 서비스가 되고 동적으로 만들어지는 파일들은 proxy_pass 를 이용해서 Jetty 가 떠 있는 3000 번 포트로 포워딩 하라는 뜻 입니다.

이제 접속을 유지하지 않더라도 항상 Jetty 가 떠 있게 만드는 방법을 알아볼 차례입니다. 이거 저거 설정해주기 귀찮더군요.

     $ lein ring server

이 부분을 screen 을 이용해서 계속 떠 있게 유지해줄 생각입니다. 일단 기존에 떠 있던 프로세스를 죽여줍니다. (간단하게 Ctrl-c 눌러줍니다)

그리고 스크린을 설치해줍니다.

     $ sudo apt-get install screen


다시한번 외쳐줄까요? 우분투 만세!!

     $ screen -S clj


이러면 가상 터미널이 한개 만들어 집니다. clj 라는 이름으로 말이죠

     $ cd ~/work/compojure-example
     $ lein ring server


Ctrl-a d (컨트롤 a 를 동시에 누른 다음에 d 누름) 이러면 화면에서 떨어져 나갑니다. 이상태에서 접속을 끊는다 하더라도 clj 라고 만들어진 가상 터미널 (스크린) 은  유지 됩니다.

다시 clj 로 돌아가고 싶으면

     $ screen -list
     $ screen -r clj


 -list 옵션은 어떤 스크린이 있는지 리스트를 확인하라는 명령입니다.

 이상입니다.
   

"리습은 그것을 마침내 손에 넣게 되었을 때 경험하게 되는 심오한 깨달음을 위해서라도 배울 가치가 있다. 리습을 이용할 일이 그렇게 많지 않다고 할지라도 그 경험은 그 자체만으로도 당신을 훨씬 훌륭한 프로그래머로 만들어 줄 것이다." - 에릭 레이먼드 (Eric Raymond)


제가 아는 선배가 송재경 (바람의 나라, 리지니 만드신 분) 씨와 인터뷰를 할 일이 있었는 데 그 분이 하신 말씀 중에

"프로그램은 어떠한 언어로 짜는 것이 중요한 것이 아니다. 가장 중요한 것은 전산적인 마인드를 소유하는 것이다"

라는 이야기를 들었다고 합니다. 상당히 많은 시간이 지났지만 이 이야기가 가지고 있는 의미는 아직까지도 도움이 됩니다. 저 또한 후학들을 만나면 이와 같은 이야기를 많이 합니다.

왜 리습을 공부해야 하는가? 전산적인 마인드를 소유하는데 도움이 되는 연습을 가장 열심히 할 수 있는 언어였다고 자신할 수가 있습니다.

저는 예전에도 리습을 공부하였고, 지금도 공부중이고 앞으로도 공부할 것입니다. 조금만 떨어지면 멀어져 버리는 전산적인 마인드를 붙잡기 위해서 입니다. (요즘은 리습 방언인 클로져에 빠져 있습니다)


lein 을 이용한 프로젝트 생성시 요즘 clojure 버젼이 1.4.0 이 기본인 것처럼 만들어 집니다. 아무 생각 없이 slime 을 연결해서 코딩을 시작하면 무엇인지 이상한 기분이 들기 시작합니다. 바로

doc

함수 입니다. doc 함수가 동작을 안합니다. "1.2.1" 버젼에서는 제대로 동작했는데 갑자기 동작안하기 시작합니다. 정확히는 "1.3.x" 와 "1.4.0" 에서 동작을 안합니다.

REPL 상에서

user> (use 'clojure.repl)


만 해주면 그 다음부터 사용이 됩니다.
저번 포스트 에서 Clojure 와 Emacs 와 Slime 을 연동 시키는 방법을 했다면, 이제 실제 클로져 코드를 짜야 하는 일이 생길 것입니다. 그런데 전역으로 환경을 세팅하는 것이 아니기 때문에 다른 클로져 라이브러리를 포함 시키는 것이 어려울 것 같습니다. 특히 clojure-contrib 같은 거의 필수 라이브러리는

lein 으로 만들어준 프로젝트의 project.clj 를 열어줍니다.

$ lein new test-project
$ cd test-project
$ emacs project.clj


다음 과 같이

                 [org.clojure/clojure-contrib "1.2.0"]


의 내용을 :dependencies 에 추가해 줍니다.


아래에 붙어 있는 congomongo 는 무시하세요.

$ lein deps


를 실행해 주면 clojure-contrib 를 다운 받아서 프로젝트 환경에 추가해 줍니다.
이제 Emacs 상에서 Clojure 랑 Slime 을 연동시켜서 원하는 코드를 만들어 주면 됩니다.

M-x clojure-jack-in


In console 'Terminal'

$ sudo apt-get install clojure
$ sudo apt-get install leiningen
$ sudo apt-get install slime

install clojure-mode

$ cd ~/.emacs.d
$ git clone git://github.com/technomancy/clojure-mode.git

append to dot-emacs

$ emacs ~/.emacs

add followings below

    (add-to-list 'load-path "~/.emacs.d/clojure-mode/")
    (require 'clojure-mode)

install swank-clojure

$ lein plugin install swank-clojure 1.3.3

make clojure test project
   
$ mkdir ~/tmp
$ cd ~/tmp
$ lein new test-project
$ cd test-project
$ lein deps

invoking slime for clojure
   
$ emacs project.clj

in Emacs

M-x clojure-jack-in

enjoy yourself!!

for Korean

클로져를 Emacs 랑 연동하는 방식이 여러가지가 존재합니다. 이거 저거 다 해봤는데 잘 안되더군요. 개인적으로 apt-get 을 이용해서 개발환경 관리하는 것을 즐겨하는 편인데 잘 뒤져봐도 이것에 관한 내용들이 없더군요. 그래서 정리해 봤습니다.

터미널 창에서 다음 과 같이 입력해 줍니다. emacs 는 이미 설치가 되어 있다고 가정합니다.

$ sudo apt-get install clojure
$ sudo apt-get install leiningen
$ sudo apt-get install slime

클로져 모드 (Clojure-mode) 를 설치해줍니다. git 는 이미 설치가 되어 있다고 가정합니다.

$ cd ~/.emacs.d
$ git clone git://github.com/technomancy/clojure-mode.git

.emacs 를 열어서

$ emacs ~/.emacs

다음과 같은 내용을 추가해 줍니다. 이는 클로져 모드를 쓸 수 있게 해주는 것입니다.

    (add-to-list 'load-path "~/.emacs.d/clojure-mode/")
    (require 'clojure-mode)

 스웽크 클로져(swank-clojure)를 설치해줍니다. slime 이 연결하는 대상이 될 것입니다.

$ lein plugin install swank-clojure 1.3.3

테스트용으로 clojure 프로젝트를 만들어 봅니다.
   
$ mkdir ~/tmp
$ cd ~/tmp
$ lein new test-project
$ cd test-project
$ lein deps

실제로 slime 하고 연동해 보기로 합니다.
   
$ emacs project.clj


clj 파일을 열어서 Emacs 상에서 다음과 같이 명령을 입력해 주면

M-x clojure-jack-in

바로 Slime 하고 연동되서 뜨는 것을 확인하실 수 있습니다.
 


Closure 라고 들어 보셨는지요. 최근에 차기 Java 버젼에 포함되기를 많은 사람이 희망하였다가 무산 되서 실망의 글로 자바 관련 게시판들이 도배가 됐다고 합니다. 자 그러면 대체 클로져 (Closure) 란 무엇인지 위키피디아의 내용을 보기로 하겠습니다.


http://en.wikipedia.org/wiki/Closure_%28computer_science%29


어려운 내용들로 도배가 되어 있습니다. 쭈욱 살펴보자면 함수를 리턴하는 언어들에게서 흔히 찾아 볼 수 있는 개념이고요. Anonymous Function 하고 착각하면 안된다고 쓰여져 있습니다. 즉 'function value' 개념이라는 식으로 언급이 되어 있습니다.


가장 쉽게 설명하자면 '지역 변수' 가 코드가 쓰여질 때의 값으로 지정되는 것이 아니라 '클로져'가 생성될 때 그 '값'에 접근이 가능하다? 라는 식으로 쓰여질 수가 있겠습니다. 


이러니 저러니 해도 여러가지 형태가 결부되어 있지만 , 


1. 함수가 리턴 되어야 하고

2. variable 이 관련 있어야 한다. (이것이 관련 없으면 클로져가 아니다)



아무래도 정말 쉽게 설명 하자면 예제 케이스를 가지고 설명하는 것이 가장 쉬울 것 같습니다.

EDITED. 2012 - 09 - 04

아무래도 Lisp 의 방언인 Clojure 로 설명을 하니 못 알아먹겠다는 원성이 자자해서 위키 페이지에 나와 있는 자바스크립트를 예로 설명드리겠습니다. 

function derivative(f, dx) {
  return function (x) {
    return (f(x + dx) - f(x)) / dx;
  };
}

함수 derivative 는 f 라는 함수와 dx 라는 변수를 입력받아서 , x 라는 인자를 받아서 처리하는 함수를 리턴하는 함수 입니다. 

리턴되는 function(x) 는 derivative 에서 넘겨받은 f 와 dx 에 영향을 받을 수밖에 없겠지요? 즉 리턴되는 function (x) 는 어찌 되었든 간에 derivative 에서 넘겨 받는  f 와 dx 에 의존해서 구현이 달라집니다. 이때 function(x) 가 derivative 의 인자인 f 와 dx 와 클로져 (Closure) 다 라고 합니다. 



클로져 (Clojure) 로 만든 Closure 의 예입니다. (발음이 같은 이유는 바로 관련이 있기 때문입니다)  함수를 세분해서 설명하겠습니다.


user> (defn make-greeter [greeting-prefix]
        (fn [username] (str greeting-prefix ", " username)))
#'user/make-greeter


make-greeter 라는 함수를 리턴하는 함수입니다. greeting-prefix 값을 받아서 그 값을 이용하는 함수를 리턴합니다.
defn 은 함수 선언을 지정하는 함수이고요. fn 은 내부 함수 선언용입니다. (str greeting-prefix ", " username) 이 부분 greeting-prefix 랑 " , " username 을 합쳐서 리턴하는 명령 입니다.

user> (def hello-greeting (make-greeter "Hello"))
#'user/hello-greeting

make-greeter 에 "Hello" (greeting-prefix 값이 "Hello" 가 됩니다) 를 인자로 넘겨서 반환되는 함수를 hello-greeting 으로 지정해 줍니다.

user> (hello-greeting "crazia")
"Hello, crazia"
user>

완성된 함수를 테스트 해봅니다.  greeting-prefix 값이 변함에 따라서 전혀 다른 함수가 만들어 집니다. "Hello" 대신 "Hi " 를 넣어서 리턴되는 함수는 또 다른 함수가 되겠지요? 자 이때!!!

make-greeter 에 의해서 리턴되는 함수가 greeting-prefix 값 에 대한  closure 다

이라고 합니다.


저번 포스트 에서 Clojure 와 Slime 을 연결해봤습니다. 그런데 REPL 에서 색이 안 이쁘더군요. 간단하게 색을 집어넣는 방법을 설명하겠습니다.

.emacs 파일을 열어서 맨 아래쪽에 다음 과 같은 내용을 추가해 줍니다.

(add-hook 'slime-repl-mode-hook (defun clojure-mode-slime-font-lock () (let (font-lock-mode) (clojure-mode-font-lock-setup))))

끝!!


드디어 올것이 오고 말았군요.
clojure 를 emacs 에서 slime 과 연결시키는 방법에 관한 것입니다. 물론 제가 쓰고 있는 osx snow leopard 에서 말이죠. 기존에 제가 쓰고 있던 환경은 emacs 에 slime 을 clbuild 버젼으로 연결시켜서 쓰고 있었습니다. 나름 편해서 애용하고 있었는데 이번에 clojure 를 연결 시켜야 해서 살짝 골치가 아프군요.

참고로 clbuild 를 쓰게 되면 slime 관련 설정 부분입니다. (.emacs 에 있음)

(setq load-path (cons "/Users/crazia/work/clbuild/source/slime" load-path))
(setq load-path (cons "/Users/crazia/work/clbuild/source/slime/contrib" load-path))
(setq slime-backend "/Users/crazia/work/clbuild/.swank-loader.lisp")
(setq inhibit-splash-screen t)
(load "/Users/crazia/work/clbuild/source/slime/slime")
(setq inferior-lisp-program "/Users/crazia/work/clbuild/clbuild lisp")
(setq slime-use-autodoc-mode nil)
(slime-setup '(slime-fancy slime-tramp slime-asdf))
(slime-require :swank-listener-hooks)

복잡하지요? 그래서 자동화 방법을 애용하기로 하겠습니다.

ELPA 라는 것이 있습니다. Emacs Lisp Package Archive 의 약자입니다. 말 그대로 Emacs 에서 쓰기 편하게
여러가지들을 제공하는 패키지 시스템이라고 할 수 있습니다.

http://tromey.com/elpa/install.html  여기에서 제공하는 방식으로 하면 인터페이스 버퍼가 뜹니다.

 
(let ((buffer (url-retrieve-synchronously
           "http://tromey.com/elpa/package-install.el")))
  (save-excursion
    (set-buffer buffer)
    (goto-char (point-min))
    (re-search-forward "^$" nil 'move)
    (eval-region (point) (point-max))
    (kill-buffer (current-buffer))))


이 내용을 *scratch* 버퍼에 가져다 두고 C-j 를 입력하면 된다고 쓰여져 있는데, AquaEmacs 의
경우에는 *scratch* 버퍼가 lisp-interaction-mode 로 설정되어 있지 않습니다.  따라서

M-x lisp-interaction-mode


바꾸시고 위의 내용을 복사해서 넣고 C-j 를 입력하세요. 반응이 만약 없다면 C-xC-e 로도 되는것 같더군요. 어찌 됐건 위의 코드를 실행하면 명령을 하나 쓸수가 있습니다.
M-x package-list-packages


를 입력하면 조금 시간이 걸리지만 설치할 수 있는 목록 리스트가 주르륵 나옵니다. 그중에서 swank-clojure 를 찾아서 'i' 를 눌러서 체크를 하시고 'x' 를 눌러서 설치가 됩니다.
그러면 .emacs 파일 아래쪽에 다음과 같은 코드가 자동으로 추가됩니다.

 (when
     (load
      (expand-file-name "~/.emacs.d/elpa/package.el"))
   (package-initialize))


package-initialize 가 실행된다면 기존에 설정되어 있는 slime 설정하고 충돌이 일어납니다. 멋지게 해결할 수 있는 방법이 없으니.. 귀찮지만 clbuild 로 웹 개발할 때와 클로져 공부 할 때를 구분시키는 방법으로 잠시동안 머물러야 겠습니다. 위에 있는 코드를 삭제하고 맨 위에 있는 clbuild 용 slime 설정 코드도 삭제해 줍니다.

(defun select-lisp-program (n)
  (interactive "n1:clbuild 2:clojure : (enter number)  ")
  (cond ((= n 1) (progn
                   (setq load-path (cons "/Users/crazia/work/clbuild/source/slime" load-path))
                   (setq load-path (cons "/Users/crazia/work/clbuild/source/slime/contrib" load-path))
                   (setq slime-backend "/Users/crazia/work/clbuild/.swank-loader.lisp")
                   (setq inhibit-splash-screen t)
                   (load "/Users/crazia/work/clbuild/source/slime/slime")
                   (setq inferior-lisp-program "/Users/crazia/work/clbuild/clbuild lisp")
                   ;;(setq inferior-lisp-program "clj")
                   (setq slime-use-autodoc-mode nil)
                   (slime-setup '(slime-fancy slime-tramp slime-asdf))
                   (slime-require :swank-listener-hooks)
                   (add-hook 'lisp-mode-hook (lambda () (slime-mode t)))
                   (add-hook 'slime-repl-mode-hook (lambda () (slime-mode t)))
                   (add-hook 'inferior-lisp-mode-hook (lambda () (inferior-slime-mode t)))

                   (define-key slime-mode-map (kbd "C-,") 'forward-sexp)
                   (define-key slime-mode-map (kbd "C-.") 'backward-sexp)
                   (define-key slime-mode-map (kbd "TAB") 'slime-indent-and-complete-symbol)
                   (define-key slime-mode-map (kbd "RET") 'paredit-newline)
                   (define-key slime-mode-map (kbd "<return>") 'paredit-newline)

                   (setq slime-complete-symbol-function 'slime-fuzzy-complete-symbol)
                   ))
        ((= n 2)
         (when
             (load (expand-file-name "~/.emacs.d/elpa/package.el"))
           (package-initialize))))
  )


그리고 두가지를 한 함수로 합쳐서 제공합니다. 위의 내용을 .emacs 에 추가하시면 다음 emacs 를 실행시키고 상황에 따라서

M-x select-lisp-program


명령으로 1 (clbuild) 나 2 (clojure) 로 개발 모드를 바꿔가면서 사용하실 수 있습니다. 물론 이렇게 귀찮게 안하고 lisp 을 clbuild 가 아닌 독자적으로 설치해서 적용하면 되지만 귀찮은걸 어쩌겠습니까 -0- clbuild 가 너무 편한걸..




새로운 것을 좋아하는 후배가 책까지 사주면서 보라고 해서 결국 Clojure를 설치하기로 했습니다.

Mac port 이용해서 Clojure 설치 하기

가장 쉬운 방법으로는 mac port 를 이용하는 방법이 있습니다.

sudo port install clojure


하면 너무나 손쉽게 설치됩니다. 하나 이러한 방법을 쓰면 나중에 Emacs 나 Slime 과 연동 설치할 때 귀찮아 지기 때문에 수동 설치를 하려고 마음을 먹었습니다.

Clojure 수동 설치 하기

참조 사이트: http://riddell.us/ClojureOnUbuntu.html

위에 링크한 사이트는 제가 여러모로 도움을 받는 사이트 입니다. 역시나 클로져도 우분투에 편하게 설치하는 방법이 잘 정리되어 있더군요. "그냥 사이트 보세요" 만큼 형편없는 도움말은 없겠지요. 그래서 거기 나와 있는 내용을 조금 제 형편에 맞게 정리해 볼까 합니다.

기본적으로 osx 는 자바가 설치되어 있습니다. 따로 설치해줄 필요 없으니 자바는 생략하고요. git 역시 설치되어 있다고 가정하지요. 제가 어떻게 설치했는지 기억이 안나는 군요..Maven 은 설치해준 기억이 없는데 설치되어 있는것을 보니 어영 부영 설치되어 있는 것 같습니다.

Ant 설치
 Ant 는 아마 기본으로 설치가 안되어 있을 것입니다.

~$ sudo port install ant


Clojure 설치

최신 버젼을 받아서 컴파일 해서 설치해 주면 됩니다.

~$ mkdir ~/work

~$ cd ~/work

~$ git clone git://github.com/richhickey/clojure.git

~$ cd clojure

~$ ant

~$ mkdir ~/.clojure

~$ cp clojure.jar ~/.clojure


저는 기본적으로 코드 관련 소스들은 work 밑에 저장하기 때문에 work 를 썼습니다. 코드 관련 폴더를 하나씩 만들어서 쓰시는 것을 추천합니다.

Clojure 테스트 하기

~$ cd ~/.clojure

~$ java -cp clojure.jar clojure.lang.Repl

user=> (+ 1 41)

42


Ctrl-d 를 누르면 REPL 을 빠져 나갑니다.

clojure-contrib 설치 하기

clojure-contrib 은 공식적인 클로져 확장 라이브러리 모음집 같은 것입니다.

~$ cd ~/work

~$ git clone git://github.com/richhickey/clojure-contrib.git

~$ cd clojure-contrib

~$ mvn package

~$ cp target/clojure-contrib*.jar ~/.clojure/clojure-contrib.jar


자 이제는 편하게 REPL 로 들어가는 스크립트를 만드는 순서 입니다.

~/.bash_profile 아래쪽에 다음과 같은 내용을 채워 넣습니다.

export CLOJURE_EXT=~/.clojure

PATH=$PATH:~/work/clojure-contrib/launchers/bash

alias clj=clj-env-dir


저장하고 Terminal 창을 종료했다가 다시 열어서 테스트를 해 줍니다.

~$ clj
Clojure 1.2.0-master-SNAPSHOT
user=> (System/getProperty "java.class.path")
"/Users/crazia/.clojure/clojure-contrib.jar:/Users/crazia/.clojure/clojure.jar"
user=>


위처럼 clojure.jar 와 clojure-contrib.jar 의 위치가 표시되면 만사 OK 입니다.

Clojure 와 clojure-contrib 의 업데이트는 굳이 설명할 필요가 없을 듯 하여 생략합니다.

+ Recent posts