클로져는 정말 멋지게도 언어레벨에서 Concurrency 를 지원합니다. 물론 그래서 어렵다는 평을 받긴 하지만 이미 개념을 알고 있는 사람들은 이보다 더 편할 수가 없습니다. 게다가 자바와의 연계성이 뛰어난 점 때문에 클로져는 정말 멋진 언어입니다. 


최근 무어의 법칙이 더이상 통하지 않고 CPU 의 코어수만 늘어나는 상황하에서 멀티쓰레드 프로그래밍을 모른다면 진정한 컴퓨팅 파와를 쓸 수가 없습니다. 그렇기 때문에 저는 예전부터 클로져를 눈여겨 왔었는데요. 그 클로져의 Concurrent Programming 에 관한 괜찮은 글을 찾아서 여기에 소개합니다. 


http://blakesmith.me/2012/05/15/understanding-clojure-concurrency-part-1.html


위는 간단하 개요 소개의 글입니다. 정말 쉽게 설명했습니다. 아쉽지만 영어입니다 ㅎㅎ 


http://blakesmith.me/2012/05/25/understanding-clojure-concurrency-part-2.html


각각의 프로그램 언어에서 지원하는 개념들과 예제입니다. 차분히 읽어 가시면 어려울게 별로 없으니 클로져에 관심이 있다면 꼭 한번 보실만 합니다. 



저번 포스트 에서 Emacs 버젼을 24 로 업그레이드 한 후에 가장 큰 문제가 생겼습니다. ClojureCommon Lisp 개발환경이 동시에 안되는 문제가 발생했습니다. 

몇번의 노력끝에 결국 Emacs 에서도 편하게 설치하는 쪽을 받아들이게 됐습니다. 기존에는 "이맥스 유저는 이래야 해!!" 라는 말도 안되는 자존심으로 수동으로 이거 저거 설치하는 쪽이였으나 이제 대세를 받아들이기로 했습니다. 

클로져 (Clojure) 개발 환경 설정 


예전 포스트 에서 쉽게 설치하는 방법을 소개한 적이 있었습니다.  그러나 swank-js 를 만들었던 당사자가 더 훌륭한 솔루션이 나왔다고 GG 를 쳐버렸습니다. -0- (당신을 믿고 포스팅을 했었다고!!! ㅜ.ㅜ ) 

nRepl 을 설치해줍니다.  

nrepl.el is an Emacs client for nREPL, the Clojure networked REPL server. It's a great alternative to the now deprecated combination of SLIME + swank-clojure.


설치법은 간단합니다. Emacs 24 버젼부터는 Package 관리가 기본으로 포함되어 버렸습니다. 대세는 게을러지기 아니겠습니까? 
.emacs 파일에 다음과 같은 내용을 추가합니다. 


(require 'package)
(add-to-list 'package-archives
    '("marmalade" . "http://marmalade-repo.org/packages/"))
(package-initialize)


그리고 재 시작후 , M-x list-packages 로 출력해서 nrepl 을 찾아서 설치해 주거나 , 설명 페이지에서 추천하는 대로 

M-x package-install [RET] nrepl [RET]



로 설치해주시면 됩니다. 추가로 편리하게 사용하기 위해서 세팅 몇가지만 해주시면 됩니다. 

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


이제 클로져 관련 파일을 열고나서 M-x nrepl-jack-in  를 입력하시면 익숙한 REPL 버퍼가 보일 것입니다. (속도도 Swank-js 보다 빨라졌습니다)


Common Lisp 개발 환경 설정 (SLIME 설정)
 
이제 이것 설치도 쉽게 하는 방법이 있습니다. SBCL 은 이미 설치되어 있다고 가정합니다. (참조사이트 : http://www.mohiji.org/2011/01/modern-common-lisp-on-linux/

일단 quicklisp 파일을 다운 받아야 합니다. http://beta.quicklisp.org/quicklisp.lisp 에서 받아줍니다. 적당한 곳에 옮겨주시고요. 

$ sbcl


sbcl 을 실행시켜줍니다.

user> (load "/path/to/quicklisp.lisp")


sbcl 상에서 위와 같이 quicklisp 파일이 받아진 것을 읽어들입니다.  

user> (quicklisp-quickstart:install)
user> (ql:add-to-init-file)
user> (ql:quickload "quicklisp-slime-helper") 


그리고 위와 같이 연속으로 실행시켜줍니다.  이러면 자연스럽게 SLIME 까지 설치가 됩니다. 이제 Emacs 에게 위치를 알려주는 일만 해주면 됩니다. 

(setq inferior-lisp-program "sbcl")
(load (expand-file-name "~/quicklisp/slime-helper.el"))

이제 M-x slime 을 입력하면 자연스럽게 SLIME REPL 이 뜨게 됩니다.  

May The Source Be With You !!!
 
ClojureScript 라고 들어 보셨는가요? 자바 스크립트 (Javascript) 는 대단히 편리한 언어이고 렉시칼 스코프 (Lexical Scope)를 지원해서 Anonymous Function 이나 Closure 를 지원하는 등, 고급 추상적인 언어가 지녀야할 몇가지 장점을 지니고 있습니다. 또한 문법도 쉽고 그래서 빠르게 전파되고 있습니다. 게다가 V8 엔진을 쓰는 Node.js 등 이제는 속도면에서도 자바(Java)에 필적하고 있습니다. (예전 제 포스트 참조) 

그러나 자바스크립트는 또한 약점이 존재합니다. 자바스크립트 지지자들도 여러번 지적하는 사항이라고 하는데요 (사실 저는 잘 모르겠습니다 -ㅅ- ). 지나치게 단순하고 어딘가 허술하며, 확장시키기 좋지 않으며 어떤 기능을 구현하기 위해서 불편하게 추가해야 하는 사항이 많다는 것입니다. 그런 이유로 커피스크립트 (CoffeeScript) 같은 것이 나와서 자바스크립트의 그러한 문제점들을 보완하고 있다는 것입니다. 

그래서 숨어있던 리습(Lisp)의 추종자들이 구글이 제공하는 Google Closure Compiler 의 힘을 바탕으로 해서 강력한 어둠의 Lispy Magic 을 이용해서 커피 스크립트 같은 것을 만들어 냈습니다. 그것이 바로 ClojureScript 입니다. 

예전에 폴 그레이엄이 말하길 '자신이 개발해야 하는 언어로 가장 생산성 있게 개발하고 싶다면, 그 언어를 이용해서 Lisp 해석기를 만들고 그 리습으로 코딩을 하고 나중에 원래 언어로 컴파일 하라.' 라는 이야기를 한 적이 있습니다. 바로 그렇게 만들어 버린 것입니다. 개발자들은 ClojureScript 로 개발을 하고 Google Closure Compiler 로 컴파일을 하면 자바스크립트 파일이 튀어 나옵니다. 이 파일을 이용해서 Client Side 의 작업과 심지어는 Server Side (Node.jsJavascript 로도 바꾸어 줍니다) 작업까지도 일원화 할 수가 있습니다. 

이름에서도 유추할 수 있듯이 사용하고 있는 Lisp 의 방언 (dialect) 은 바로 Clojure 입니다. 

개요만 찾아서 보시고 바로 튜토리얼로 넘어가면 좋을 것 같습니다. 

무엇이든 지 간에 실제로 짜보기 전에는 공부가 됐다고 말할 수가 없을 것입니다. 그런 의미로 조금 복잡한 프로그램을 만들어 본다면, 거의 할 수 있는 최고의 공부가 될 것입니다. 거기다가 처음부터 차근 차근 만들어 볼 수 있다면? 

그런 의미에서 Nethack 이라는 게임을 클로져로 만들어 보는 것이 정말 괜찮은 공부가 될 수 있을 것입니다.  
저도 우연히 발견한 사이트 인데 정말 많은 공부가 되고 있습니다. 실전 Clojure 공부라고 볼 수 있습니다.

http://stevelosh.com/blog/  

1 부터 차례로 따라하시면 됩니다. 
요즘 Protocol 을 사용해서 (defprotocol) 무엇인가 만들어 보는 중인데, 알 수 없는 이상한 버그가 종종 발생합니다. 분명히 돌아가는 코드인데, 코드를 살짝 변경했는데, method 를 찾을 수 없다는 에러. 답답해서 별 짓을 다 해봤지만 원인을 알 수가 없었는데, 우연히 발견한 방법으로 에러를 추정할 수가 있었습니다. 

target/ 디렉토리 안에 들어있는 class 파일들을 캐싱하다가 definition 을 못 찾는 것으로 추정됩니다. 뻔하게 돌아가야 하는데 안 돌아간다면 project 디렉토리에서

$ lein clean



한 번 실행해 주고 다시 컴파일을 해 준다면 정상적으로 돌아 갈 것입니다. (이 걸 몰라서 몇 주째 고생했음 ㅜ.ㅜ)

ps.
 명령을 실행하기 전에 clojure  연결은 다 끊어 주셔야 합니다. 
 
요즘 Node.js 로 프로젝트를 진행중입니다.  엄청나게 빠르고 편하게 개발할 수가 있는 좋은 개발 환경인데 단 하나의 장점이 너무 빠르게 개발해서 기획이나 클라이언트 개발과 페이스를 맞출 수 없는 단점 아닌 단점이 있습니다. 게다가 페어 프로그래밍 방식으로 일하는 친구마저 있으니 속도가 상상을 할 수 없을 만큼 빠릅니다. (최근에는 진짜 어려운 일이 아니면 페어도 그만두었습니다. 여유를 좀 가지고 일하자고 해서 ㅎㅎ )

그러다 보니 딴 짓을 하게 되더군요. 지금 열심히 만들고 있는 게, DB 에 테스트 데이터를 랜덤으로 만들어서 집어넣는 프로그램을 만들었습니다. 아주 쓸만하더군요. (생각보다 괜찮음 ㅋㅋ) 

그중에서 현재 날짜를 가지고 와서 주어진 포맷팅 형식으로 변환하여 String 타입으로 리턴하는 함수를 만들어 봤습니다. (갑자기 친구놈이 디비 스킴을 바꿔서 ..)



CLisp 이나 C++ 에서는 쉽게 설정하는 함수 인자 기본 값 (Default) 설정하는 방법에 관한 예제입니다. 일단 쉽게 설정하는 방법이 없는 듯 합니다. 

1 user=> (defn bar
2          ([a b]   (bar a b 100))
3          ([a b c] (* a b c)))
4 #'user/bar
5 user=> (bar 5 6)
6 3000
7 user=> (bar 5 6 2)
8 60

이 와 같은 식으로 가장 인자 수가 많은 함수를 내부에서 호출하는 형식으로 꾸며줍니다. 그 외에 다른 방식으로는 :keyword 형식으로 호출하는 등등이 있으나 위에 쓰여지는 방법이 그나마 다른 언어(Language)에 익숙한 방법이라 사료됩니다. 

defn 설명 페이지 


Parallelism Versus Concurrency

Lest our discussion of concurrency and parallelism lead you to think they are the same thing, let’s disentangle the two notions.

Concurrency is the coordination of multiple, usually interleaved threads of execution that are accessing or modifying some shared state.

Parallelism involves state as well, but usually in the inverse. Being an optimization technique used to efficiently utilize all of the available resources (usually computational, but sometimes other resources, like bandwidth) to improve the performance of an operation, approaches to parallelization generally aim to maximize the window of exclusive access to state (or, often, chunks of state) so as to minimize coordination overhead. Rather than involving interleaved threads of execution, the multiple evaluations of a parallelized operation run simultaneously—sometimes on different CPU cores, other times on different physical machines entirely.

-- Clojure Programming 책 중에서 .. 


쉽게 설명하자면 

'동시성' 은 공유되는 자원에 대한 접근이나 변경을 주도하는 쓰레드들을 배치하는 작업에 관한 용어이고 

'병렬성' 은 공유될 수 있는 자원에 대한 최적화된 사용에 관한 용어입니다. 보통 쓰레드 배치나 이런 것은 당연히 적용하지만 잡-스케쥴링 (Job - scheduling) 을 통해서 다중 CPU 일 경우 각각의 CPU 나 코어 에서 작업을 분배시켜서 돌리게 하거나 또는 아예 다른 기계에서 작업을 돌리게 한다던지 와 같은 오히려 작업 분배에 관한 용어로 쓰일 때가 많습니다. 



 

재미로 만들어(만들었 다기 보다는 포팅- Porting) 본 스팸 필터 입니다. 

https://github.com/crazia/spam-filter

여기에 올려두었습니다. (''

 Practical Common Lisp 이라는 책에 보면 폴 그레이엄이 구상한 스팸 필터에 관한 간단한 예제가 있습니다.  그 알고리즘을 바탕으로 책에서는  CLisp 으로 구현한 예제가 있습니다. (Chapter 23 - Practical: A Spam Filter)

위 내용을 바탕으로 해서  Programming Clojure 라는 책을 쓴 Stuart Halloway 가 간단하게  PCL (Practical Common Lisp) 에 있는 예제들을 Clojure 로 옮긴 것들이 있습니다. (practical-cl-clojure)

그런데 몇몇의 내용을 살펴보면 Clojure 예전 버젼에 맞춰서 소스가 만들어져 있고 무엇보다도 클로져의 가장 중요한 기능인 병렬성에 맞게 코딩이 되어 있지 않더군요. 따라서 그 부분을 보강하고 Halloway 가 구현 하지 않고 끝을 낸 부분을 추가하는 중에 있습니다. (train 이나 score 같은 함수들) 

클로져를 따로 공부하시는 분들에게 도움이 될까 해서 github 에 공개를 해둡니다. 
클로져를 이용해서 웹 어플리케이션 간단한 것을 만들어 보고 싶은 욕망이 있을 것입니다. 만들어 보고 싶은 욕망은 의외로 간단하게 해결이 됩니다. 바로 컴포져(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 옵션은 어떤 스크린이 있는지 리스트를 확인하라는 명령입니다.

 이상입니다.
   

+ Recent posts