�빫�� ����� ���

Clojure Web library hiccup(예전 compojure)


Programming clojure 책에는 compojure에 있는 html 함수들을 사용하는 예제들을 보여줍니다. 막상 해보니깐 안되더군요. 언제인지는 모르겠지만 hiccup이라는 라이브러리로 떨어져 나왔더군요.

Hiccup is a library for representing HTML in Clojure. It uses vectors to represent tags, and maps to represent a tag’s attributes.

hiccup의 readme 파일에 있는 내용입니다.(살짝 바뻐서, 대충 써야겠네요.) 예제소스를 보면 금방 알 수 있을 겁니다.

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
42
43
44
45
46
47
48
 
(ns hello-www.core
  (:use compojure.core
    hiccup.core
    ring.adapter.jetty)
  (:require [compojure.route :as route]))
 
(defroutes example
  (GET "/hello" [] 
    (html
			 [:head
			  [:body
			   [:h1 "Hello World Wide Web!"]]]))
  (GET "/" [] "<h1>root</h1>")
  (GET "/sample" []
    (html
      [:head
       [:title "html sample by hiccup lib, clojure"]
       ]
      [:body
       [:p [:a {:href "http://defree.co.kr"} "Samuel's blog"]]
       [:p [:b "b tag"]]
       [:p [:dd "dd tag"]]
       [:p 
        [:div "form in a div tag"
         [:form {:name "form1"}
          [:p 
           [:input {:type "text" :name "text_input" :value "this is a text input tag."}]
           [:input {:type "password" :name "password_input" :value "this is a password input tag."}]
           [:input {:type "button" :name "button_input" :value "this is a button input tag."}]
           [:input {:type "submit" :name "submit_input" :value "this is a submit input tag."}]
           ]
          [:p 
           [:input {:type "radio" :name "text_input" :value "1" :selected "true"} "radio No.1"]
           [:input {:type "radio" :name "text_input" :value "2" :selected "false"} "radio No.2"]
           [:select {:name "select"}
            [:option {:value "code1" :selected "true"} "select 1"]
            [:option {:value "code2"} "select 2"]]
           [:textarea {:value "textarea"}]]]]]
       [:h1 "h1"]
       [:h2 "h1"]
       [:h3 "h2"]
       [:h4 "h3"]
       [:h5 "h4"]
       [:h6 "h5"]]))
  (route/not-found "Page not found"))
 
(run-jetty example {:port 8080})

꽤 쉽죠? 그리고 당연히 project.clj에 hiccup을 명시해 두어야겠죠.

1
2
3
4
5
6
7
8
(defproject hello-www "0.0.1-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.2.0"]
                   [org.clojure/clojure-contrib "1.2.0"]
                   [compojure "0.4.0"]
                   [ring/ring-jetty-adapter "0.2.3"]
                   [lein-eclipse "1.0.0"]
                   [hiccup "0.2.6"]])

마음이 급해져서 설명을 어떻게 할 지 생각나질 않는군요. -_-;;

자작 Ajax library. salib v0.0.1


현재 업무에서는 오픈소스를 사용할 수도 없고 네트웍 문제 + 라이센스 문제 덕에 왠간하면 신경쓰지 않던 Ajax를 구현하게 되었습니다. 사실 그 이유 보다는 후배들에게 남겨주고 싶은 것이 생각나서 말이죠. 이거라도 줘야지 하는 마음이었답니다. 다른 SI에서는 수준 높은 코드를 구현하는 곳도 있겠지만 제가 있는 곳에서는 SQL을 제외하고는 수준높은 코드가 영 쓰이지도 나오지도 않네요. 이 라이브러리도 수준 높은 건 아니지만.. 이도 모르는 분들이 태반이라.. 저도 가끔 생각나면 하나씩 추가할 수 있도록 일단 남겨놓기는 해야겠다 하는 생각이 들었습니다.

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 
/* 
sa object 사용법
	1. AJAX 호출
		sa object가 포함된 자바스크립트를 임포트하고 sa.run(url, queryString, callback) 을 호출한다.
			url : 호출할 웹서비스(서블릿 등)
			queryString : get방식과 같이 넘겨주는 파라메터. (예 - "mode=inputLot&plant_cd=A020")
			callback : 웹서비스가 완료된 후 실행할 자바스크립트 함수. 익명함수 또는 함수 이름.
	2. 헬퍼 함수
		sa.createInputTag(type, name)
			input 태그 생성시 ie6의 버그를 우회하여 태그를 리턴함
*/
var sa = {
	url : ""
	, desc : "ajax object"
	, getXmlHtmlRequest : function() {
		try { 
			if(window.XMLHttpRequest) {
				return new XMLHttpRequest();
			}
			else {
				return new ActiveXObject("Microsoft.XMLHTTP");
			}
		}
		catch(e) {
			return null;
		}
	}
	, xmlObject : null
	, init : function() {
		sa.xmlObject = sa.getXmlHtmlRequest();
	}
	, createInputTag : function(type, name) {
		var inputTag;
		try {
			inputTag = document.createElement("<input type='" + type + "' name='" + name + "'/>"); // ie6 버그때문에 input은 이와같이 생성하여야 함.
		}
		catch(e) {
			inputTag = document.createElement("input");
			inputTag.setAttribute("type", type);
			inputTag.setAttribute("name", name);
		}
		return inputTag;
	}
	, showNowLoading : function() {
		var i; var obj; // ie6 select 태그의 layer 버그 우회
		for(i = 0; i < document.all.tags("select").length; i++) {
			obj = document.all.tags("select")[i];
			if(!obj || !obj.offsetParent) {
				continue;
			}
			obj.style.visibility = "hidden";
		}		
		document.getElementById("loading_div").style.display = "";
	}
	, eraseNowLoading : function() {
		var i; var obj; // ie6 select 태그의 layer 버그 우회
		for(i = 0; i < document.all.tags("select").length; i++) {
			obj = document.all.tags("select")[i];
			if(!obj || !obj.offsetParent) {
				continue;
			}
			obj.style.visibility = "visible";
		}		
		document.getElementById("loading_div").style.display = "none";
	}
	, uninitializedHandler : function() {}
	, loadingHandler : function() {}
	, loadedHandler : function() {}
	, interactiveHandler : function() {}
	, completeHandler : function() {}
	, run : function(url, queryString, callback) {
		var desc = "";
		if(sa.xmlObject === null) {
			sa.init();
		}
		sa.completeHandler = callback;
		sa.xmlObject.open("POST", url, true);
		sa.xmlObject.onreadystatechange = function() {
			if(sa.xmlObject.readyState === 0) {
				desc = "open매소드 호출되지 않음";
				sa.uninitializedHandler();
			}
			else if(sa.xmlObject.readyState === 1) {
				desc = "status 와 헤더는 도착하지 않은 상태";
				sa.showNowLoading();
				sa.loadingHandler();
			}
			else if(sa.xmlObject.readyState === 2) {
				desc = "status 와 헤더는 도착하지 않은 상태";
				sa.loadedHandler();
			}
			else if(sa.xmlObject.readyState === 3) {
				desc = "데이터 일부를 받은  상태";
				sa.interactiveHandler();
			}
			else if(sa.xmlObject.readyState === 4) {
				desc = "데이터전송 완료";
				sa.completeHandler();
				sa.eraseNowLoading();
			}
		};
		sa.xmlObject.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");   
		sa.xmlObject.send(queryString);
	}
};

Windows 에서 Clojure Web 개발 환경설정(leiningen, Compojure, Eclipse REPL)


일하다가 지루해져서(화장실에 책 들고 볼일보다가..) Clojure 책을 구경하고 있었습니다.(열심히 읽은게 아니라 말 그대로 구경) 생각해보니깐, 프로그래밍 언어를 익히는 데 가장 좋은 방법은 프로그램을 계속 만드는 것인데, 요새 제 일도 그렇고 해서 웹으로 만들어야겠다 하는 생각이 들더군요. Programming Clojure 책 후반부에 Compojure 라이브러리가 딱 1챕터 나옵니다. 그런데 버전이 올라가면서 조금씩 바뀌었나보군요. 환경설정하는데 한참 걸렸습니다. ~_~

준비물은 eclipse 3.4 이상, J2SDK 1.6 이상입니다.

1. counterclockwise 설치하기
쉬워요. counterclockwise 홈페이지에서 플러그인을 이클립스에 복사해 넣으면 땡입니다. 설치하고 나면 이클립스에 클로져 지원이 생기죠.

이클립스에서 클로져 지원


이렇게 말이죠

2. leiningen 설치
leiningen은 클로져 프로젝트 도구입니다. 프로젝트를 생성하고, 의존성을 관리하고, 빌드 등을 지원하는 툴이죠.(아겔님 블로그 참조)

    설치방법

  • github의 leiningen 저장소에 보면 릴리즈된 leiningen 패키지를 찾을 수 있습니다.(요즘은 1.3.0이군요.) 다운로드 받아서 적당한 위치에 옮겨넣습니다.(전 d:\src\leiningen 에 넣었습니다.)
  • 그리고 실행 스크립트인 lein.bat 파일을 leiningen의 readme 페이지에서 찾을 수 있습니다. installation 부분에 있어요. 그것도 leiningen.jar 디렉토리에 같이 넣어둡니다. 필요하시면 path를 걸어두시는 것도 좋죠.
  • 나중에 보니깐 lein.bat나 lein.sh 만 있으면 self-install 명령으로 설치가 가능한거였더군요. -_-;; leiningen jar를 다운받지 않아도 그냥 “lein self-install” 명령으로 알아서 다운받네요. 흠흠
  • 전 처음에 lein.sh만 있는 줄 알고 그거 보고 스크립트를 윈도우용으로 작성해야겠구나 싶어서 스크립트 코드를 한참동안 읽었더랬습니다. readme 파일도 읽어봐야겠다 생각이 들어서 읽다가 발견한 lein.bat.. 하.. 참. ~_~

3. leiningen만 있으면 사실 거의 다 된겁니다. 프로젝트를 생성하고 이클립스 연동을 해 보죠.

lein new hello-www

이렇게 하면 leiningen 디렉토리 하위에 hello-www 디렉토리와 프로젝트를 위한 스켈레톤이 생성됩니다. 이클립스에서 편하게 Compojure 개발을 하려면 몇가지 더 추가하여야 합니다. hello-www 디렉토리를 보면 project.clj 파일이 있습니다. 에디터 등으로 열어서 다음과 같이 바꿔줍니다.

1
2
3
4
5
6
7
(defproject hello-www "0.0.1-SNAPSHOT"
  :description "FIXME: write"
  :dependencies [[org.clojure/clojure "1.2.0"]
                   [org.clojure/clojure-contrib "1.2.0"]
                   [compojure "0.4.0"]
                   [ring/ring-jetty-adapter "0.2.3"]
                   [lein-eclipse "1.0.0"]])

compojure 지원, jetty 지원, eclipse 지원을 추가한거죠.(아겔님 블로그 참조, lein-eclipse 홈페이지 참조) 그 다음에 해당 프로젝트 디렉토리에서 “lein deps” 명령을 사용합니다.

cd hello-www
..\lein.bat deps

그러면 “Copying 15 files to D:\src\lein\hello-www\lib” 와 같이 파일 몇 개를 복사했다고 알려줍니다. 필요한 jar 등을 가져온거죠.(다운로드 받을 수도 있습니다. 복사할 파일이 없으면 말이죠. 그리고 빌드 한번 해봐야죠.

..\lein.bat install

빌드는 maven을 이용하는 것 같습니다. pom.xml을 생성하거든요.(아직 정확히는 모르겠다는.. -_-;;)

Created D:\src\lein\hello-www/hello-www-0.0.1-SNAPSHOT.jar
Wrote pom.xml
[INFO] Installing D:\src\lein\hello-www\hello-www-0.0.1-SNAPSHOT.jar to C:\Docum
ents and Settings\LG\.m2\repository\hello-www\hello-www\0.0.1-SNAPSHOT\hello-www
-0.0.1-SNAPSHOT.jar

이렇게 말이죠.
이번엔 이클립스 지원을 추가해야죠.

..\lein eclipse

.classpath 파일과 .project 파일이 생성됩니다. 그럼 이클립스에서 import 하면 되죠. import->Existing Projects into Workspace 선택한 후 생성된 프로젝트(d:\src\leiningen\hello-www)를 선택하면 됩니다.

4. 생성된 프로젝트에 클로져 지원을 추가해줍니다.(프로젝트 오른쪽 클릭 -> “enable/disable clojure language support”) 그러면 Run As 메뉴에 “New Clojure project’s JVM, with a REPL”이 추가되어 있어요. 이제 이클립스에서 REPL을 사용할 수 있게 됬습니다.

5. 한번 코드를 동작시켜봐야죠? src/hello-www/core.clj 파일을 다음과 같이 수정하시거나 REPL에 바로 작성하셔도 됩니다.

1
2
3
4
5
6
7
8
9
10
(ns hello-www.core
  (:use compojure.core
        ring.adapter.jetty)
  (:require [compojure.route :as route]))
 
(defroutes example
  (GET "/" [] "<h1>Hello World Wide Web!</h1>")
  (route/not-found "Page not found"))
 
(run-jetty example {:port 8080})

요러면 http://localhost:8080 에서 “Hello World Wide Web!” 라는 글자를 볼 수 있을 겁니다.

6. 클로져 코딩하며 놀기~~

저 스스로도 잊어버리지 않으려고 적어놨습니다만… 썩 간단하지가 않군요. -_-