Clojure 앱에서 환경변수를 사용할 때는 어떻게 해야 할지 생각해봤다. Node.js에서는 dotenv 라이브러리를 사용해서 중요한 변수들을 코드로부터 분리하고 노출되는 것을 막을 수 있었는데 Clojure에서도 당연히 그런 라이브러리가 있을 거라는 생각에 검색을 해보니 weavejester/environ을 많이 쓰는 것 같았다. 그런데 좀 더 살펴보니 yogthos/config 라는 것도 괜찮을 것 같아 사용해봤다.
Node.js에서 dotenv 사용
https://iamcool.tistory.com/7?category=779855
weavejester/environ
https://github.com/weavejester/environ
yogthos/config
https://github.com/yogthos/config
yogthos/config 라이브러리를 사용하면 jar 외부의 config.edn 파일을 사용할 수도 있고 jar 파일 내부에 config.edn, prod.edn 파일을 포함시킬 수도 있다. 이번에는 두 번째 방법을 이용해 런타임에서 환경변수에 접근했다.
프로젝트 생성
lein을 이용해서 새로운 프로젝트를 생성한다.
lein new app env-app
라이브러리 및 프로파일 추가
아래와 같이 yogthos/config 라이브러리를 project.clj 파일에 추가한다. 그리고 dev, prod에서 각각 사용하게 될 config.edn 파일의 위치를 profiles에 추가한다.
1 2 3 4 5 6 7 8 9 10 11 12 | (defproject env-app "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.9.0"] [yogthos/config "1.1.1"]] :main ^:skip-aot env-app.core :target-path "target/%s" :profiles {:uberjar {:aot :all} :prod {:resource-paths ["config/prod"]} :dev {:resource-paths ["config/dev"]}}) | cs |
project.clj 파일의 profiles에 추가한 config.edn 파일을 생성하기 위해 아래와 같이 폴더 및 파일들을 생성한다.
config
- dev
- config.edn
- prod
- config.edn
config.edn 파일 생성
config 폴더에 생성한 config.edn 파일에 아래와 같은 내용을 입력한다. 파일이 로드되면 :runtime 키워드로 접근할 수 있으며 MySQL, Redis 서버 정보를 포함하고 있다.
1 2 3 4 5 6 7 8 9 | {:runtime {:name "dev" :db {:dbtype "mysql" :host "localhost" :port 3306 :user "user" :password "password" :dbname "dbname"} :redis {:host "localhost" :port 6379}}} | cs |
1 2 3 4 5 6 7 8 9 | {:runtime {:name "prod" :db {:dbtype "mysql" :host "prod_host" :port "prod_port" :user "prod_user" :password "prod_password" :dbname "prod_dbname"} :redis {:host "prod_host" :port "prod_port"}}} | cs |
config/prod/config.edn 파일
core.clj 파일 변경
앱의 entry point인 core.clj 파일에 로드한 환경변수를 출력할 수 있는 코드를 추가한다.
1 2 3 4 5 6 7 8 9 10 11 | (ns env-app.core (:gen-class) (:require [config.core :refer [env]])) (defn -main [& args] (let [{name :name db :db redis :redis} (:runtime env {})] (println name "runtime variables for MySQL:", db) (println name "runtime variables for Redis:", redis)) (println "App start!!")) | cs |
yogthos/config 라이브러리의 core로부터 불러온 env를 통해서 project.clj 파일의 profiles에서 정의한 파일로부터 로드한 환경변수에 접근할 수 있는데, config에서 정의한 값을 읽기 위해 :runtime을 키워드를 이용한다. 그리고 MySQL, Redis 값을 출력했다.
테스트
앱을 실행하여 dev 환경변수를 출력한다.
$ lein deps
$ lein run
dev runtime variables for MySQL: {:dbtype mysql, :host localhost, :port 3306, :user user, :password password, :dbname dbname}
dev runtime variables for Redis: {:host localhost, :port 6379}
App start!!
prod 환경변수를 출력하기 위해 아래 명령을 실행한다.
$ lein with-profile prod run
prod runtime variables for MySQL: {:dbtype mysql, :host prod_host, :port prod_port, :user prod_user, :password prod_password, :dbname prod_dbname}
prod runtime variables for Redis: {:host prod_host, :port prod_port}
App start!!
이 예제에서는 jar 파일 내부에 config.edn 파일을 추가했는데, 경우에 따라 외부로부터 입력을 받아야 할 필요가 있을 수 있다. 이 때에는 yogthos/config의 다른 예를 살펴보면 좋겠다.
이 방식으로 CircleCI 통해서 Google Cloud에 앱을 올려봤다. 로컬이나 개발서버에서는 dev/config.edn 파일을 읽도록 했고 Google Cloud에 릴리즈할 때는 CircleCI 설정 파일에서 jar 파일을 만들 때 lein with-profile prod uberjar 커맨드를 실행하여 prod 환경변수를 읽도록 했다.
소스코드
이것도 간단한 예제이지만 아래에 전체 소스코드를 올려놓았다.
https://github.com/ksleeq21/clojure-env-example