주로 쓰는 개발 프레임워크는 Node.js 이다. 그래서 백앤드를 구성하는 20개 남짓의 microservice들은 모두 노드로 구현돼 있다. 서비스 간 통신을 위해 만든 라이브러리가 HTTP와 Redis로 구현돼 있기 때문에 프로토콜만 맞출 수 있다면 서비스를 구현할 때 어떤 언어든지 고려해볼 수 있다. 그래서 내가 만든 몇 개의 서비스들 중에서 가장 중요한 서비스를 테스트 삼아 Clojure로 구현해 보았다. 처음부터 끝까지 혼자 다 만든 서비스라서 Node.js로 구현된 로직을 Clojure로 다시 쓰는 데에는 오랜 시간이 걸리지 않았고 성능도 비슷한 수준으로 나왔다. 오히려 다시 쓰기 시작하면서 구조가 더 명확해지고 눈엣가시 같던 구현들도 제거할 수 있었으니 상당히 만족스러웠다. 그 상태에서 두 개 서비스를 더 Clojure로 다시 썼고 충분히 좋은 성능을 보여주었기 때문에 프로덕션 릴리즈를 해보고 싶었다. 하지만 백앤드 팀의 유일한 Clojure 개발자라서 혼자 모든 것을 다 해야 하는 상황이 팀에게도 나에게도 이상적이지 않았다. 제법 여럿이 흥미를 보였지만 더 발전되지 못했고 지금은 아무도 찾지 않는 저장소가 되고 말았다.


최근에 Spark을 공부하면서 Scala를 써보려고 했는데, Clojure 만큼은 아니란 생각이 들었다. Functional에 왜 OOP를 가져다 붙였는지 잘 이해할 수가 없다. 언어들이 서로 좋은 것들을 배껴와 서로 닮아 가는 건 알겠는데 그럴 거면 Java를 더 functional 하게 만들지 하는 생각이 들었다. 그래서 관심이 식어버렸다. 다시 Clojure를 꺼내 들었는데 하나도 기억이 안나서 처음 했던 것처럼 간단한 headless 서버를 만들어 보기로 했다. 이번에 쓸 프레임워크는 Pedestal이다. 


이곳의 코드를 참고했다. 

https://github.com/pedestal/pedestal/tree/master/samples/hello-world


프로젝트 생성


다음 명령을 실행하여 프로젝트를 생성할 수 있다. 

lein new pedestal-service echo-service


다음과 같이 폴더와 파일들을 생성한다. 

echo-service

- src

  - services

    - echo.clj

    - ping.clj

  - server.clj


코드 수정


각 파일에 들어갈 코드는 다음과 같다. 


1
2
3
4
5
;; echo.clj
(ns echo-server.services.echo)
 
(defn handler [request]
  (let [message (get-in request [:params :message] "Echo")]
    {:status 200 :body (str message "\n")}))
cs


1
2
3
4
;; ping.clj
(ns echo-server.services.ping)
 
(defn handler [request]
  {:status 200 :body (str "pong\n")})
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
;; project.clj
(ns echo-server.server
  (:gen-class)
  (:require [io.pedestal.http :as http]
            [io.pedestal.http.route :as route]
            [io.pedestal.http.body-params :as body-params]
            [io.pedestal.http.route.definition :refer [defroutes]]
            [echo-server.services.ping :as ping]
            [echo-server.services.echo :as echo]))
 
(defroutes routes
  [[["/"
      ["/ping" {:get ping/handler}]
      ["/echo" {:get echo/handler}]]]])
 
(def service {:env :prod
              ::http/routes routes
              ::http/resource-path "/public"
              ::http/type :jetty
              ::http/port 8080})
 
(defonce runnable-service (http/create-server service))
 
(defn run-dev
  "The entry-point for 'lein run-dev'"
  [& args]
  (println "\nCreating dev server")
  (-> service
      (merge {:env :dev
              ::http/join? false
              ::http/routes #(deref #'routes)
              ::http/allowed-origins {:creds true :allowed-origins (constantly true)}})
      http/default-interceptors
      http/dev-interceptors
      http/create-server
      http/start))
 
(defn -main
  "The entry-point for 'lein run'"
  [& args]
  (println "\nCreating prod server")
  (http/start runnable-service))
cs

 

자동으로 생성되는 파일명을 변경했기 때문에 project.clj 파일을 다음과 같이 일부 수정해야 한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;; project.clj
(defproject echo-service "0.5.1"
  :description "Simple hello-world service in Pedestal"
  :url "http://pedestal.io/samples/index"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [io.pedestal/pedestal.service "0.5.1"]
                 [io.pedestal/pedestal.jetty "0.5.1"]
                 [ch.qos.logback/logback-classic "1.1.7" :exclusions [org.slf4j/slf4j-api]]
                 [org.slf4j/jul-to-slf4j "1.7.21"]
                 [org.slf4j/jcl-over-slf4j "1.7.21"]
                 [org.slf4j/log4j-over-slf4j "1.7.21"]]
  :min-lein-version "2.0.0"
  :resource-paths ["config", "resources"]
  :profiles {:dev {:aliases {"run-dev" ["trampoline" "run" "-m" "hello-world.server/run-dev"]}
                   :dependencies [[io.pedestal/pedestal.service-tools "0.5.1"]]}}
  :main echo-server.server)
cs


실행 


이후 다음 명령으로 서버를 실행시킨다. 

lein run


HTTP 서버를 개발 모드에서 실행시키는 방법은 다음과 같다.

lein repl

echo-server.server=> (def serv (run-dev))


동작 확인


서버가 정상적으로 동작하는지 보기 위해 cURL을 이용한다. 

curl -i "localhost:8080/ping" 

curl -i "localhost:8080/echo?message=hello"


소스코드 

Pedestal을 이용한 간단한 예제이지만 API endpoint를 이해하기에 부족함이 없다. 구현 코드는 이 곳에서 볼 수 있다. 


참고


http://pedestal.io/index

https://github.com/pedestal/pedestal

https://nordicapis.com/10-frameworks-for-building-web-applications-in-clojure/


Posted by 코딩새싹
,