User Tools

Site Tools


study:web_programming:3_장

루미너스 아키텍쳐

프로젝트 관리

프로젝트의 관리는 project.clj와 profiles.clj 2 개의 파일을 사용한다.

sample project.clj

프로파일

자세한 설명 : 라이닝언 프로파일

개관

정의: 프로파일이란 하나의 프로젝트를 빌드, 릴리즈, 테스트, 개발 등, 프로젝트를 여러가지 단계 및 측면에서 필요한 설정을 선언적 방식으로 할 수 있도록 하는 프로젝트 관리 방법.

프로파일에는 defproject(project.clj 파일에서 정의되는)에서 사용하는 모든 키-값을 사용할 수 있다. 프로파일에 정의된 키-값은 project 맵에 병합(merge)된다.

프로파일 만들기

다음 4가지 방식으로 프로파일을 만들 수 있다.

  1. 키로 정의하기
    • defproject에서 :profiles 키로 정의한다.
  2. profiles.clj 파일
    • git같은 버전관리툴에서는 무시되면서, 해당 프로젝트에만 특화된 설정을 하고 싶을 때 사용.
    • project.clj와 같은 디렉토리에 있어야 한다.
  3. ~/.lein/profiles.clj
    • 해당 유저에게만 특화된 프로젝트 설정을 할 때
  4. /etc/leiningen/profiles.clj
    • 시스템 전역에 걸쳐 적용

키로 정의하기

defproject에 :profiles 키에 맵으로 값을 할당한다.

(defproject myproject "0.5.0-SNAPSHOT"
  :description "A project for doing things."
  :dependencies [[org.clojure/clojure "1.4.0"]]
  :profiles {:dev {:resource-paths ["dummy-data"]
                   :dependencies [[expectations "1.4.41"]]}})

기본 프로파일

프로젝트가 만들어지지 않은 아무 디렉토리에서 lein show-profiles 명령을 실행하면 기본적으로 존재하는 프로파일을 볼 수 있다.

> lein show-profiles
base
debug
default
leiningen/default
leiningen/test
offline
uberjar
update
user

서브 프로파일

기 정의된 프로파일로 다른 프로파일을 만들 수 있다. 이때는 프로파일 값으로 맵이 아니라 백터를 쓰고 구성하고 싶은 프로파일 명을 원소로 넣는다.

{:shared {:port 9229, :protocol "https"}
 :qa [:shared {:servers ["qa.mycorp.com"]}]
 :stage [:shared {:servers ["stage.mycorp.com"]}]
 :production [:shared {:servers ["prod1.mycorp.com", "prod1.mycorp.com"]}]}

루미너스의 프로파일 구성 방식

(defproject guestbook "0.1.0-SNAPSHOT"
 
...
 
:profiles
  {:uberjar {:omit-source true
             :env {:production true}
             :aot :all
             :source-paths ["env/prod/clj"]
             :resource-paths ["env/prod/resources"]}
   :dev           [:project/dev :profiles/dev]       ; (1)
   :test          [:project/test :profiles/test]     ; (2)
   :project/dev  {:dependencies [[prone "1.0.1"]
                                 [ring/ring-mock "0.3.0"]
                                 [ring/ring-devel "1.4.0"]
                                 [pjstadig/humane-test-output "0.7.1"]
                                 [mvxcvi/puget "1.0.0"]]
 
 
                  :source-paths ["env/dev/clj" "test/clj"]
                  :resource-paths ["env/dev/resources"]
                  :repl-options {:init-ns user}
                  :injections [(require 'pjstadig.humane-test-output)
                               (pjstadig.humane-test-output/activate!)]
                  ;;when :nrepl-port is set the application starts the nREPL server on load
                  :env {:dev        true
                        :port       3000
                        :nrepl-port 7000}}
   :project/test {:env {:test       true
                        :port       3001
                        :nrepl-port 7001}}
   :profiles/dev {}
   :profiles/test {}})
  • (1) 에서 :dev 프로파일은 :project/dev과 :profiles/dev으로 구성된다.
  • (2) 에서 :test 프로파일은 :project/test와 :profiles/test로 구성된다.
  • project/dev와 project/test는 project.clj파일에서 정의한다.
  • profiles/dev와 profiles/test는 profiles.clj에서 정의한다.

project/test 프로파일 보기

lein show-profiles로 project/test 프로파일이 어떻게 설정되는지 볼 수 있다.

> lein show-profiles project/test
{:env {:test true, :port 3001, :nrepl-port 7001},
 :jvm-opts nil,
 :eval-in nil}

실행 소스 파일 선택하기

개발시에는 에러가 발생하면 에러 메시지가 자세하게 나오게 하는 것이 좋지만, 배포시에는 제한된 메시지만 뿌리게 하고 싶다. 하지만 개발시와 배포시의 실행을 코드상으로 구별하는 것은 또 다른 버그를 만들 수 있다. 이것도 프로파일을 사용하면 원하는 소스만을 해당 개발과 배포시에 구분해서 사용할 수 있다.

루미너스에서 실행 파일 선택하는 방법

:source-paths는 defproject의 키로서 프로젝트에서 실제 사용할 소스의 위치를 정의한다. 이 키가 루미너스에서는 root와 :uberjar, 그리고 :project/dev 에서 다음과 같이 설정되어 있다.

  • root - :source-paths [“src/clj”]
  • :uberjar - :source-paths [“env/prod/clj”]
  • :project/dev - :source-paths [“env/dev/clj” “test/clj”]

개발시에는 env/dev에 있는, 배포시에는 env/prod 폴더에 있는, guestbook.config 이름공간을 사용한다.

env/dev/guestbook/config.clj

(ns guestbook.config
  (:require [selmer.parser :as parser]
            [clojure.tools.logging :as log]
            [guestbook.dev-middleware :refer [wrap-dev]]))
 
(def defaults
  {:init
   (fn []
     (parser/cache-off!)
     (log/info "\n-=[guestbook started successfully using the development profile]=-"))
   :middleware wrap-dev})

env/prod/guestbook/config.clj

(ns guestbook.config
  (:require [clojure.tools.logging :as log]))
 
(def defaults
  {:init
   (fn []
     (log/info "\n-=[guestbook started successfully]=-"))
   :middleware identity})

어플리케이션 컴포턴트

보통 웹 어플리케이션에서는 MVC 패턴을 사용하지만 루미너스에서는 뷰와 컨트롤러 사이의 구분이 엄격하지 않다. 오히려 관련된 코드는 같은 이름공간에 존재한다.

하지만 클로저 웹 스택은 아주 유연하기 때문에 MVC 패턴 방식으로 구성할 수도 있다.

루미너스에서의 컴포넌트

  • core: HTTP 서버를 실행하고 멈춘다.
  • handler: 클라이언트로부터 요청을 받아 응답을 하는 루트 핸들러.
  • routes: 여러 종류의 요청을 처리하기 위한.
  • db: 어플리케이션 데이타 모델과 프리젠테이션 레이어를 위한 데이타 모델.
  • layout: 어플리케이션의 레이아웃을 만든다.
  • middleware: 커스텀 미들웨어.

core

최초의 어플리케이션 구동과 서버 구동하는 코드들이 정의되어 있다.

handler

루미너스는 핸들러의 routing을 다음과 같이 분리되어 있다.

  • home-routes: 주요 레이아웃 라이팅(GET 메소드로 실제 클라이언트 요청 처리)
  • app-routes: csrf 사후 처리, not-found
  • app: wrap-base

일반적으로는 다음과 같이 시작한다.

(defroutes app-routes
  (GET  "/" [] (resource-response "index.html" {:root "public"}))
  (GET  "/widgets" [] (response [{:name "Widget 1"} {:name "Widget 2"}]))
  (route/resources "/")
  (route/not-found "Page not found"))

init과 destroy 함수는 서버 시작과 종료시에 실행할 코드 정의한다.(이 2 함수는 handler가 아닌 core 이름공간으로 갈 필요가 있어 보인다)

  • init: 로거 시작, 컴포넌트 시작을 알림
  • destroy: 컴포넌트 종료를 알림

컴포넌트 라이브러리로는 스튜 지에라의 Component가 아닌 mount 사용하고 있다.

middleware

커스텀 미들웨어를 정의한다.

routes

db

환경 변수로 DB 설정한다.

layout

HTTP 응답을 위한 HTML 만들기.

*app-context*는 요청의 :servlet-context나 혹은 환경변수의 :app-context로 동적으로 설정되면서 wrap-context 미들웨어에 적용된다.

study/web_programming/3_장.txt · Last modified: 2019/02/04 14:26 (external edit)