클로저스크립트는 Node.js 프로그래밍을 지원한다. 이를 활용하면, 클로저스크립트로 서버 사이드 프로그램이나 데스크탑 애플리케이션(예: Light Table)을 개발할 수 있다.

참고로, 다음의 예제들은 Linux Ubuntu 15.04 버전에서 설치하고 실행했다.

1. Node.js의 설치 및 설정

1.1. Node.js의 설치

다음은 Node.js를 설치하는 한 예이다.

$ wget https://nodejs.org/dist/v5.10.1/node-v5.10.1-linux-x64.tar.xz

$ tar -xvf node-v5.10.1-linux-x64.tar.xz

$ sudo mv node-v5.10.1-linux-x64 /usr/local/node

Node.js를 설치한 경로를 다음과 같이 export한다.

$ export NODE_HOME=/usr/local/node

다음과 같이 실행해서 버전 넘버가 나오면, Node.js가 제대로 설치된 것이다.

$ node -v
v5.10.1

1.2. Global Modules 설치 경로 지정

자신의 홈 디렉토리(~/)의 .npmrc 파일에 다음과 같이 global modules의 설치 경로를 지정해 줄 수 있다.

~/.npmrc
prefix=~/npm-global-modules

다음을 실행하면, global modules 설치 경로를 확인할 수 있다.

$ npm root -g
/home/philos/npm-global-modules/lib/node_modules

2. 클로저스크립트 Node 프로그래밍

2.1. Hello World 출력하기

다음과 같이 node-demo 프로젝트를 새로 만든다.

$ lein new node-demo

$ cd node-demo

이때 새로 만들어진 project.clj 파일의 내용을 다음과 같이 수정해 준다.

project.clj
(defproject node-demo "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.8.40"]]
  :plugins [[lein-cljsbuild "1.1.1"]]
  :cljsbuild {:builds [{:id "dev"
                        :source-paths ["src"]
                        :compiler {:target :nodejs   ; (1)
                                   :output-dir "out"
                                   :output-to "out/app.js"
                                   :main node-demo.hello
                                   :optimizations :none
                                   :source-map true
                                   :pretty-print true} }]})
1 :target :nodejs 설정이 있으면, 클로저스트립트 컴파일러는 Node.js에 맞게 소스 코드를 컴파일한다.

hello.cljs 파일을 다음과 같이 작성한다.

src/node_demo/hello.cljs
(ns node-demo.hello
  (:require [cljs.nodejs :as node]))

(node/enable-util-print!)

(defn -main []
  (println "Hello World!"))

(set! *main-cli-fn* -main)

다음과 같이 컴파일한다.

$ lein cljsbuild once
Compiling ClojureScript...
Compiling "out/app.js" from ["src"]...
Successfully compiled "out/app.js" in 1.717 seconds.

다음과 같이 실행하면, Hello World! 문자열이 출력된다.

$ node out/app.js
Hello World!

2.2. Figwheel 사용하기

이번에는 Figwheel을 사용해 보자!

다음의 두 라인을 project.clj 파일에 추가해 준다.

project.clj
(defproject node-demo "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.8.40"]]
  :plugins [[lein-cljsbuild "1.1.1"]
            [lein-figwheel "0.5.1"]]     ; (1)
  :cljsbuild {:builds [{:id "dev"
                        :source-paths ["src"]
                        :figwheel true    ; (2)
                        :compiler {:output-dir "out"
                                   :output-to "out/app.js"
                                   :main node-demo.hello
                                   :optimizations :none
                                   :source-map true
                                   :pretty-print true
                                   :target :nodejs} }]})

Figwheel을 실행하기에 앞서 ws 모듈과 source-map-support (optional) 모듈을 미리 설치해 준다.

$ npm install ws
$ npm install source-map-support

다음과 같이 Figwheel을 실행해 준다.

$ lein figwheel
Figwheel: Validating the configuration found in project.clj

Figwheel: Configuration Valid. Starting Figwheel ...
Figwheel: Starting server at http://localhost:3449
Figwheel: Watching build - dev
Compiling "out/app.js" from ["src"]...
Successfully compiled "out/app.js" in 7.675 seconds.
Launching ClojureScript REPL for build: dev
Figwheel Controls:
          (stop-autobuild)                ;; stops Figwheel autobuilder
          (start-autobuild [id ...])      ;; starts autobuilder focused on optional ids
          (switch-to-build id ...)        ;; switches autobuilder to different build
          (reset-autobuild)               ;; stops, cleans, and starts autobuilder
          (reload-config)                 ;; reloads build config and resets autobuild
          (build-once [id ...])           ;; builds source one time
          (clean-builds [id ..])          ;; deletes compiled cljs target files
          (print-config [id ...])         ;; prints out build configurations
          (fig-status)                    ;; displays current state of system
  Switch REPL build focus:
          :cljs/quit                      ;; allows you to switch REPL to another build
    Docs: (doc function-name-here)
    Exit: Control+C or :cljs/quit
 Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application

이 상태에서 다음과 같이 실행해 주면, Figwheel 접속이 이루어졌다는 메시지가 추가로 출력되는 것을 볼 수 있다.

$ node out/app.js
Hello World!
Figwheel: trying to open cljs reload socket
Figwheel: socket connection established

그리고 위의 lein figwheel 실행 화면을 다시 보면, 다음과 같은 메시지와 프람프트가 함께 추가로 보일 것이다.

To quit, type: :cljs/quit
cljs.user=>

이 상태에서 src/node_demo/hello.cljs 파일의 맨 마지막 부분에 다음과 같은 내용을 추가한 후, 파일을 저장해 보자.

src/node_demo/hello.cljs
(ns node-demo.hello
  (:require [cljs.nodejs :as node]))

(node/enable-util-print!)

(defn -main []
  (println "Hello World!"))

(set! *main-cli-fn* -main)

(println "source code modified!")   ;  (1)

그러면 npm out/app.js을 실행한 화면에 다음과 같은 내용이 출력될 것이다.

Figwheel: notified of file changes
source code modified!   ; (1)
Figwheel: loaded these dependencies
("../B7805F4.js")
Figwheel: loaded these files
("../node_demo/hello.js")

즉, 수정한 소스를 저장하면 코드가 자동으로 컴파일 된 후, reloading되서 실행까지 되는 것을 확인할 수 있다.

2.3. Node.js 모듈 호출하기

이번에는 Node.js 모듈을 호출하는 법을 알아 보자. 이 예에서는 express 모듈을 설치하고 호출해 볼 것이다.

위의 project.clj 파일을 다음과 같이 약간 수정해 준다.

project.clj
(defproject node-demo "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [org.clojure/clojurescript "1.8.40"]]
  :plugins [[lein-cljsbuild "1.1.1"]]
  :cljsbuild {:builds [{:id "dev"
                        :source-paths ["src"]
                        :compiler {:target :nodejs
                                   :output-dir "out"
                                   :output-to "out/app.js"
                                   :main node-demo.express   ; (1)
                                   :optimizations :none
                                   :source-map true
                                   :pretty-print true} }]})

다음은 express 모듈을 호출해 웹 서버를 시작하는 간단한 코드이다.

src/node_demo/express.cljs
(ns node-demo.express
  (:require [cljs.nodejs :as node]))

(def express (node/require "express"))

(def app (new express))

(defn -main
  []
  (.listen app
           3000
           (fn []
             (js/console.log "App Started at http://localhost:3000"))))

(set! *main-cli-fn* -main)

그리고 다음과 같이 실행해서 package.json 파일을 먼저 만들어 준다.

$ npm init

이때 여러가지를 물어오는데 적당하게 입력하도록 한다. 그러면 package.json 파일이 만들어지는데, 이때 입력한 내용은 나중에 이 파일을 수정해서 고칠 수 있다.

이 상태에서 express 모듈을 다음과 같이 설치한다.

$ npm install --save express

이제 컴파일을 해보자.

$ lein cljsbuild once
Compiling ClojureScript...
Compiling "out/app.js" from ["src"]...
Successfully compiled "out/app.js" in 1.538 seconds.

다음과 같이 실행하면, localhost:3000에서 웹서버가 실행된다.

$ node out/app.js
App Started at http://localhost:3000