모든 지식은 시스템 내에서 단일하고, 애매하지 않고, 정말로 믿을만한 표현 양식을 가져야 한다. 



DRY - 반복하지 마라 (Don't Repeat Yourself)

어떻게 중복이 생기는가? 

- 강요된 중복
  개발자들은 다른 선택이 없다고 느낀다. 환경이 중복을 요구 하는 것처럼 보인다. 

- 부주의한 중복

  개발자들은 자신들이 정보를 중복하고 있다는 것을 깨닫지 못한다.

- 참을성 없는 중복
  중복이 쉬워 보이기 때문에 개발자들이 게을러져서 중복을 하게 된다.

- 개발자간의 중복
  한 팀에 있는 (혹은 다른 팀에 있는) 여러 사람들이 동일한 정보를 중복한다.



위 내용은 예전에 읽었던 '실용주의 프로그래머' 라는 책에서 나와 있는 드라이 원칙 입니다. 소프트 웨어를 작성할 때 필히 반복되는 환경에 대해서 어떻게 피할 수 있는 가에 관한 언급이 있는 부분입니다.

이 책을 읽은지도 어언 몇년이 흘렀는지 모릅니다. 그 책을 읽고 느낀바가 있어서 여러가지 공부를 하게 됐으니 이 책은 나에게 새로운 길을 열게 도와준거나 다름 없습니다.

CS (Computer Science) 에 들어온지 어언 18년 , 직장으로서 프로그래밍을 한지는 15년 가까이 되가는 지금 예전에 스쳐지나갔던 내용들이 더욱 큰 의미를 가지고 다가오고 있습니다.  그래서 간만에 찾아본 소프트웨어 개발시 염두에 두어야 하는 드라이 (DRY) 원칙이였습니다. 


 
페르마의 작은 정리 

n 이 소수고 , a 가 n 보다는 작고 0 보다는 큰 정수라면 ,   a^n  (a 의 n 승) 은 a modulo n 으로 맞아 떨어진다. 


 
이러한 페르마 검사가 확실하게 소수를 판별하지는 못한다. 역이 성립이 안하기 때문이다.  즉 소수가 아닌데도 페르마의 작은 정리를 만족시키는 값이 존재하기 때문이다. 이러한 수를 카마이클 수 (Carmichael number)라고 하는데, 이 수는 아주 드물다는 점 밖에 알려진 성질이 없다.  1000,000,000 아래에는 25개가 있고 예를 들자면 (561, 1105, 1729, 2465, 2821, 6601) 사실 아주 큰 값을 마구잡이로 골라 그 수가 소수인지 알아볼 때, 페르마 검사가 틀릴 확률은 우주선 (우주에서 온 방사선 cosmic radiation )이 '올바른' 알고리즘을 돌아가는 컴퓨터를 고장나게 만들어 틀린 답을 내놓게 하는 확률보다 작다. 

처음에는 틀렸기 때문에 알맞지 않은 알고리즘이라고 했다가, 나중에는 쓸 만하다고 하였는데, 바로 이런 판단 기준의 차이가 수학(과학)과 공학(기술)의 차이를 말해주는 좋은 보기다. 

                                                                          - 컴퓨터 프로그램의 구조와 해석 중에서..

완벽하게 설명이 되어야 하는 학문의 영역과 , 완벽하지는 않지만 쓸만하군 (공학) 바로 그 차이가 요즘 물질 문명의 바탕이 된 게 아닌가 싶습니다. 적어도 이러한 페르마의 작은 정리와 소수를 생성하는 그 검사로 인해서 RSA 암호화 알고리즘이 시작됐다고 하니까 말입니다. 주변에서 찾아보면 이러한 과학과 공학의 차이를 느낄 수 있는 예들이 종종 있습니다. 즉 공학은 현실과의 타협입니다. 적당한 선에서 말이죠 
 
 

문혜군이 말했습니다. "참, 훌륭하도다. 기술이 어찌 이런 경지에 이를 수 있을까?"

요리사가 칼을 내려놓고 대답했습니다. "제가 귀히 여기는 것은 도입니다. 기술을 넘어선 것입니다. 제가 처음 소를 잡을 때는 눈에 보이는 것이 온통 소뿐이었습니다. 삼 년이 지나자 통째인 소가 보이지 않게 되었습니다. 지금은 신(神)으로 대할 뿐, 눈으로 보지 않습니다. 감각 기관은 쉬고, 신(神)이 원하는 대로 움직입니다. 하늘이 낸 결을 따라 큰 틈바귀에 칼을 밀어 넣고, 큰 구멍에 칼을 댑니다. 이렇게 정말 본래의 모습에 따를 뿐, 아직 인대나 건을 베어 본 일이 없습니다. 큰 뼈야 말할 나위도 없지 않겠습니까? 

훌륭한 요리사는 해마다 칼을 바꿉니다. 살을 가르기 때문입니다. 보통의 요리사는 달마다 칼을 바꿉니다. 뼈를 자르기 때문입니다. 저는 지금까지 19년 동안 이 칼로 소를 수천 마리나 잡았습니다. 그러나 이 칼날은 이제 막 숫돌에 갈려 나온 것 같습니다. 소의 뼈마디에는 틈이 있고 이 칼날에는 두께가 없습니다. 두께 없는 칼날이 틈이 있는 뼈마디로 들어가니 텅 빈 것처럼 넓어, 칼이 마음대로 놀 수 있는 여지가 생기는 것입니다. 그러기에 19년이 지났는데도 칼날이 이제 막 숫돌에서 갈려 나온 것 같은 것입니다."


'장자' 의 포정각우 (庖丁解牛) 부분에 나온 내용입니다. (리눅스에서 글을 쓰니 '포' 자가 깨지는 군요..) 
단지 소잡는 기술이지만 그를 행함에 있어서 '도'를 느낀다는 이야기에 백정이지만 일국을 다스리는 군주가 감탄을 한다는 내용입니다.  

여기에 나와 있는 내용을 보자니 문득 '도'를 수련하는 단계에 대한 생각이 떠 오릅니다.

불교 용어에서 건너와 무도 수행의 단계를 표현하는 말로 정착되었다. ‘수(守)’란 ‘가르침을 지킨다’라는 의미. 스승의 가르침을 받들어 정해진 원칙과 기본을 충실하게 몸에 익히는 단계를 말한다. ‘파(破)’는 원칙과 기본을 바탕으로 하면서도 그 틀을 깨고 자신의 개성과 능력에 의존하여 독창적인 세계를 창조해 가는 단계이다. 그렇지만 이 시기의 수련은 다분히 의식적이고 계획적이고 작위적인 수준에 행해지는 것이 특징이다. 다음 단계인 ‘리(離)’는 파의 연속선상에 있지만, 그 수행이 무의식적이면서도 자연스러운 단계로 질적 비약을 이룬 상태이다. 자신도 모르게 ‘파(破)’를 행하되, 모든 면에서 법을 잃지 않고, 규칙을 벗어나지 않는 경지에 이름을 뜻한다. 수련의 최후단계이다. 


개발기술 또한 단지 방법의 영역이지만 그것을 열심히 갈고 닦는 다면, 도의 경지를 못 느낄 수 없다는 것입니다. 소잡는 기술 이 '도'의 경지로 승화 될 수 있다 하지 않았습니까? 

개발 기술의  '수(守)' 란 닥치는 대로 기술을 익히는 단계와도 비슷하다고 하겠습니다. 이런 저런 기술을 마구 마구 공부하고 배우는 단계입니다. 

개발 기술의 '파(破)' 란 전 단계인 '수' 에서 익힌 기본 기술을 기반으로 해서 의식적으로 자유롭게 개발을 할 수 있는 상태며 

개발 기술의 '리(離)' 란 무의식적으로 어떠한 프로그램을 짜야 겠다고 생각하면 자연스럽게 개발을 할 수 있는 단계가 아닐까 추측하고 있습니다. 

일조 일석에 이러한 개발의 도를 느낄 수는 없을 것 같습니다. 저 또한 이러한 마음가짐으로 개발을 수련할려고 마음 먹고 공부하지만 아직도 '수'의 단계를 못 벗어 난게 아닐 까 생각이 듭니다. (하지만 거듭되는 깨달음으로 최근 '파'에 근접한 거 같다는 느낌을 받았습니다)

다만 이제 개발을 시작하시는 분들은 바로 '파' 단계로 진입을 할 수는 없다는 것입니다. 지루하고 괴로운 '수'의 과정을 거쳐야지만 파격을 시도할 수 있는 단계로 진입을 하는 것이지요. 추사 김정희의 추사체는 서도에 처음 뛰어드는 사람에게 절대 안 보여주는 이유도 같을 것입니다. 실제로 김정희는 자기 자식에게 보여줄 책은 인쇄한 것과 다름 없는 수준의 정자체로 글을 남겼습니다.

아무리 기술의 발전이 빠르고, 자신의 마음이 급해도 자기 수준에 걸맞는 기술을 익힐려면 지루하고 괴로울 지라도 반복, 반복해서 자신의 몸에 체득시키는 것이 중요하다는 것을 말씀드리고 싶습니다. 혹시 누가 알겠습니까? 개발의 최종단계에 이르러 '도'를 느끼고 개발을 끝내면 온 몸에 기운이 차 오르는 '양생(養生)'의 단계를 느끼게 될지..

 

In programming, the best way to learn is often to begin experimenting as soon as possible. A full theoretical understanding can come later. - OnLisp 중에서 


프로그래밍을 배우는데 있어서 가장 좋은 방법은 가능한한 빠르게 실험을 해보는 것이다. (빠르게 예제를 짜보는 것) 전반적인 이해는 흔히 그 다음에 온다는 것이 폴 그레이엄이 말하는 방법입니다. 

종종 프로그래밍을 공부한다고 치면 매뉴얼 부터 세세히 읽어가기 시작하면 금새 꿈나라로 가는 경우가 다반사기 때문에 후다닥 목차정도만 훓고 빠르게 이거 저거 짜보는 게 확실히 빠른 이해가 됩니다.

나이키 광고가 가지는 뜻이 절실해 지는 군요 프로그래밍 에서도.

Just Do It

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

 이상입니다.
   

boost 1.46 버젼, 쓰리프트 0.8.0 버젼을 기반으로 하여 스크라이브를 설치하는 방법입니다. 스크라이브는 현재 4년간 소스에 변동이 없습니다. 따라서 최신 라이브러리 기반으로 컴파일 할려고 하면 알려준 방법대로 되지 않습니다. 

1. 부스트 설치 

$ sudo apt-get install libboost-all-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev 


언제나 우분투 (Debian) 계열 (민트도 우분투 계열이라고 볼 수 있으니..) 이라고 가정하고 이야기 할 것입니다. 

2. 쓰리프트 (Thrift) 설치 

$HOME/work 밑에 설치한다고 가정하고 

       $ cd ~/work 
       $ git clone git://git.apache.org/thrift.git
       $ cd thrift
       $ ./bootstraph.sh
       $ ./configure
       $ make
       $ sudo make install
       $ cd lib/py
       $ sudo python setup.py install


 fb303 부분을 컴파일 해줘서 설치합니다. 
       
 

       $ cd ../../contrib/fb303
       $ ./bootstraph.sh
       $ ./configure
       $ make
       $ sudo make install 


만약 이 방법으로 컴파일 안될 시에는 

$ ./configure CPPFLAGS="-DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H  -DBOOST_FILESYSTEM_VERSION=2 -DHAVE_NETDB_H=1 -fpermissive"


해주고 make 와 make install 을 해주면 됩니다. 


3. 스크라이브 설치 

       $ cd ~/work
       $ git clone http://github.com/facebook/scribe.git
       $ cd scribe
       $ CPPFLAGS="-DBOOST_FILESYSTEM_VERSION=2" ./bootstrap.sh
       $ ./configure CPPFLAGS="-DHAVE_INTTYPES_H -DHAVE_NETINET_IN_H  -DBOOST_FILESYSTEM_VERSION=2 -DHAVE_NETDB_H=1 -fpermissive"
       $ make 

       

아마 에러가 발생할 것입니다. 

       undefined reference to boost::system::generic_category()'

위와 같은 에러인데 라이브러리 링크 순서를 바꾸어 주면 거짓말 처럼 해결이 되더군요. 

     

 $ cd src
 $ g++  -Wall -O3  -o scribed store.o store_queue.o conf.o file.o conn_pool.o  scribe_server.o network_dynamic_config.o dynamic_bucket_updater.o  env_default.o -L/usr/local/lib -L/usr/local/lib -L/usr/local/lib -lfb303 -lthrift -lthriftnb -levent -lpthread  libscribe.a libdynamicbucketupdater.a  -L/usr/lib
       -lboost_system-mt -lboost_filesystem-mt 


원래 붉은 색으로 표시 된 부분이 문장 앞 부분에 존재하는데 이를 뒷 부분으로 돌리면 됩니다. 

 
저번 포스트 에서 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


 프로젝트를 만들다 보면 너무나 당연하게 디펜던시를 가지는 프로젝트가 생기게 됩니다. 메이븐은 기본적으로 디펜던시를 가져다가 프로젝트를 만들게 되는데 이를 수동으로 해야할 필요가 생긴다면  어떻게 할 것인가? 하는 문제가 있습니다. 즉 

   

    A : 실행파일이 만들어지는 프로젝트 
    B : A 가 참조하는 프로젝트
    C : B 가 참조하는 프로젝트 

    A - 
      - B -
          - C

    

이런식으로 아마 만들어지는 것이 지금까지의 일반적인 방법일 것입니다. 그러나 메이븐에서 관리를 하게 된다면 조금 다른 식이 됩니다. 

   

P (parent) 가 존재해서 

    P - A
      - B
      - C 



같은 식으로 프로젝트가 존재하게 됩니다. 그리고 각각은 pom.xml 만 수정해서 프로젝트를 관리할 수가 있습니다. 

    그래서 수정해야 할 부분을 따라하기로 만들어 보기로 합니다. 

    1. 부모 (parent) 프로젝트 생성 
 


$ mvn archetype:generate

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 186: 186
Choose org.apache.maven.archetypes:maven-archetype-quickstart version: 
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6: 6
Define value for property 'groupId': : upper
Define value for property 'artifactId': : top
Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0
Define value for property 'package':  upper: : 
Confirm properties configuration:
groupId: upper
artifactId: top
version: 1.0.0
package: upper
 Y: : Y



186 번을 선택하시고 나머지 것들은 대충 입력해서 만들어 줍니다. 제 경우에는 

     

       groupId: upper
       artifactId: top
       version: 1.0.0


 와 같이 만들어 줬습니다. 

    2. 부모 프로젝트의 pom.xml 변경 
       
       

       <groupId>upper</groupId>
       <artifactId>top</artifactId>
       <version>1.0.0</version>
       <packaging>pom</packaging>


      <packaging> 태그의 내용을 pom 으로 바꿉니다.


    3. 하위 프로젝트 생성 
      1. 에서 생성한 프로젝트의 위치로 이동합니다.

$ cd $PARENT-BASE
$ mvn archetype:generate
 
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 186: 186
Choose org.apache.maven.archetypes:maven-archetype-quickstart version: 
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6: 6
Define value for property 'groupId': : upper
Define value for property 'artifactId': : project-a
Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0
Define value for property 'package':  upper: : 
Confirm properties configuration:
groupId: upper
artifactId: project-a
version: 1.0.0
package: upper
 Y: : Y

       
다음 프로젝트 생성 

$ mvn archetype:generate

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 186: 186
Choose org.apache.maven.archetypes:maven-archetype-quickstart version: 
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6: 6
Define value for property 'groupId': : upper
Define value for property 'artifactId': : project-b
Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0
Define value for property 'package':  upper: : 
Confirm properties configuration:
groupId: upper
artifactId: project-b
version: 1.0.0
package: upper
 Y: : Y


그리고 다음 프로젝트도 생성해줍니다. 
       

$ mvn archetype:generate
 
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 186: 186
Choose org.apache.maven.archetypes:maven-archetype-quickstart version: 
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6: 6
Define value for property 'groupId': : upper
Define value for property 'artifactId': : project-c
Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0
Define value for property 'package':  upper: :   
Confirm properties configuration:
groupId: upper
artifactId: project-c
version: 1.0.0
package: upper
 Y: : Y


각각에 맞춰서 생성해줍니다. 

이렇게 하면 자동으로 부모 프로젝트의 pom.xml 이 변경됩니다. 

 

 <modules>
    <module>project-a</module>
    <module>project-b</module>
    <module>project-c</module>
  </modules>



하단에 위와 같은 내용이 추가 됐습니다. 그리고 project-a 의 pom.xml 을 열어보면 

 

<parent>
    <groupId>upper</groupId>
    <artifactId>top</artifactId>
    <version>1.0.0</version>
  </parent>


와 같은 보통때는 안보이는 내용이 추가 됐습니다. 다시 자세히 설명하자면 부모 프로젝트의 <package> 내용을 pom 으로 바꾸고 <modules> 내용을 추가하고 , 추가되는 프로젝트의 pom.xml 에 <parent> 관련된 부분을 추가 해주면 복합 모듈 프로젝트로 변경되는 것입니다. 

.
├── pom.xml
├── project-a
│   ├── pom.xml
│   └── src
│       ├── main
│       │   └── java
│       │       └── upper
│       │           └── App.java
│       └── test
│           └── java
│               └── upper
│                   └── AppTest.java
├── project-b
│   ├── pom.xml
│   └── src
│       ├── main
│       │   └── java
│       │       └── upper
│       │           └── App.java
│       └── test
│           └── java
│               └── upper
│                   └── AppTest.java
├── project-c
│   ├── pom.xml
│   └── src
│       ├── main
│       │   └── java
│       │       └── upper
│       │           └── App.java
│       └── test
│           └── java
│               └── upper
│                   └── AppTest.java
└── src
    ├── main
    │   └── java
    │       └── upper
    │           └── App.java
    └── test
        └── java
            └── upper
                └── AppTest.java

31 directories, 12 files


프로젝트의 트리 내용입니다. 


   4. 하위 프로젝트 끼리 연결 시키기 

project-b 는 project-c 를 참조하고 project-a 는 project-b 를 참조합니다. 그렇다면 각각의 project-a 와 project-b 의 pom.xml 을 열어서 

project-a 의 pom.xml 에는 
   

   <dependency>
      <groupId>upper</groupId>
      <artifactId>project-b</artifactId>
      <version>1.0.0</version>
    </dependency>


 을 추가해 주고 

 project-b 의 pom.xml 에는 
 

  <dependency>
      <groupId>upper</groupId>
      <artifactId>project-c</artifactId>
      <version>1.0.0</version>
    </dependency>


 만 추가해 주면 됩니다. 물론 이 내용들은 <dependencies> 태그 안에 위치해야 하는 것은 기본입니다. 


결론적으로 말해서 maven 은 초기의 거리낌이 느껴지는 것과는 달리 순수하게 command-line 으로 관리할 수 있는 프로젝트 관리 모듈입니다. 저 같이 이클립스보다는 이맥스(emacs) 가 손에 익은 사람들이 자바 프로젝트를 깔끔하게 관리할 수 있게 많은 기능들을 제공합니다. 
    

몽고 DB 에서 간단한 맵 리듀스 (MapReduce)를 돌려보겠습니다.

예제의 원문은

http://cookbook.mongodb.org/patterns/pivot/

에서 찾을 수 있습니다.


db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

기본이 되는 데이터를 집어 넣습니다.

map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}


map 함수입니다. mapreduce 를 돌리는 대상이 아래에도 나오지만 actors 이기 때문에 this 는 actors 컬렉션을 지칭한다고 보면 됩니다.

  for(var i in this.movies){

map 은 대체적으로 모든 데이터 한 row 당 매핑 (mapping) 된다고 보면 되기 때문에 actors 의 한 row 는 actor 한개와 movies 여러개가 들어 있습니다. 첫 줄은 movies 갯수만큼 for 문을 돌리라는 이야기 입니다.

    key = { movie: this.movies[i] };

key 값은 영화명으로 지정해 주는 것입니다. 해쉬 값으로 설정되는 것이겠지요? {movie: **** } 형태입니다.

    value = { actors: [ this.actor ] };

value 값은 배우명으로 지정해 주는 것입니다. 역시 해쉬 값으로 설정되는 것입니다. {actors: ***}

    emit(key, value);

map 함수들이 보통 하는 것이 reduce 에게 전달하기 위한 값들을 추출하는 것이 목적이기 때문에 (key, value) 쌍으로 값을 송출합니다.


reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

리듀스 (reduce) 함수는 프레임워크가 맵 (map) 함수에서 송출된 데이터를 key 값으로 정렬 시킨 값을 넘겨 받습니다. 넘겨 받는 인자가 (key, values) 로 바뀐것을 주목하시면 됩니다. 즉 map 에서 송출한 데이타들을 key 값으로 정렬한 것입니다. 즉 { movie : aaa } 라는 key 값으로 { actors : [bbb , ccc]} 라는 값으로 넘어옵니다.

  actor_list = { actors: [] };

reduce 함수는 일정한 형태로 값을 넘겨 받아야 합니다. 그 형식을 만들어 주는 것이라고 보면 됩니다. (단  key, value 가 한개씩 붙어 있는 형태는 reduce 를 거치지 않고 바로 db 에 쓰여집니다)

  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }

values 에 있는 값들을 한개씩 리턴할 값에 붙여 주는 행동입니다.


printjson(db.actors.mapReduce(map, reduce, "pivot"));


실제로 mapReduce 를 구동시키고 그 결과값을 Pivot 에 써주는 작업입니다.

db.pivot.find().forEach(printjson);


잘 쓰여졌는지 확인하는 작업입니다.

위 파일을 내려 받으시고 command line 상에서

$ mongo movie-star.js --shell


라고 입력하시면 구동하시는 모습을 확인할 수 있습니다.


이렇게 저렇게 해도 콘솔라스 폰트를 구하기 어려울 때를 대비해서 올려둡니다.




+ Recent posts