Category Archives: 프로그래밍

Unit Test의 조건

몇일전에 들었던 podcast를 다시 들으며 언급했던 내용들을 찾아봤습니다. agile toolkit podcast에서 최근에 올라온 Psychology of Build Times에서 언급된 내용입니다.

현재 Unit Test 제대로 적용한 프로젝트를 못했었는데 이미 프로젝트 시작한지도 오래된것도 있고, 모두 네트워크와 디비 연결 같은것이 있어서 어떻게 유닛 테스트를 작성해야 할지 막막하더군요. 라이브러리화 된 몇개의 소스 파일들만 유닛 테스트 코드가 조금 들어있을뿐입니다. 특별한 유닛테스트 라이브러리를 쓴것도 아니고 그냥 ifdef 입니다. –;

podcast를 듣고, Michael Feathers의 A Set of Unit Testing Rules를 읽어보니 유닛테스트로 어떤 정도를 테스트하는지 좀더 감이 오는거 같습니다. 유닛테스팅하는데 걸리는 시간이 길어지면 개발자들이 잘 하지 않고, 하더라도 기다리다 딴일하게 된다는군요. 그래서 10초 이내로 모든 유닛테스트가 되게하고, 오래걸리는 테스트는 따로 분류해서 유닛테스팅에서 구별해 내라고 하네요. 다음은 블로글 글에서 나온 유닛 테스트의 조건입니다.

1. 디비와 연결하지 말것
2. 네트워크를 통한 통신이 없을것
3. 파일시스템에 손데지 말것
4. 다른 유닛테스트와 의존성이 없을것 (독립적으로 테스트 가능)
5. 설정파일을 만드는 등의 별도의 환경설정이 필요하지 않을것

다시 cppunit을 적용해보도록 노력해봐야겠네요.

proxy 뒤의 tattertools

tattertools가 아파치의 mod_rewrite로 url을 처리하기 때문에, 메인 웹서버인 lighttpd에서 돌리기 힘듭니다. 그래서 현재 80포트의 lighttpd에서 처리를 받아서 82번 포트의 아파치로 연결을 포워딩하도록 lighttpd의 mod_proxy를 이용하여 서비스 중입니다.

하지만 tattertools의 로그를 보면 모든 접속자가 127.0.0,1로 표시되고, 트랙백 주소가 http://mix1009.net:82/trackback/46로 나오는 등 문제가 있습니다. 원래 트래백 주소 문제를 해결할려고 모든 php 문서를 편집해서 고쳤었는데, 접속주소 문제를 해결하면서 좀더 깔끔한 해결책이 나왔습니다.

일단, 아래 php파일을 이용해서 헤더정보를 확인했습니다.

[CODE type=php]<pre>
<?
  print_r($_SERVER);
?>
</pre>[/HTML][/CODE]

REMOTE_ADDR은 127.0.0.1, HTTP_X_FORWARDED_FOR 라는 곳에 원래 아이피 주소가 나왔습니다. 두 단어로 구글을 통해서 검색을 해서 얻은 결론은, REMOTE_ADDR가 127.0.0.1로 세팅되고 HTTP_X_FORWARDED_FOR가 세팅되어 있을때 REMOTE_ADDR를 바꿔주면 해결이 된다는 거였습니다.

tattertools의 모든 php에서(?) include하는 config.php에 마지막에 다음을 추가해줬더니 아주 잘 동작합니다:

[CODE type=php]if ($_SERVER[‘REMOTE_ADDR’] == ‘127.0.0.1’ && $_SERVER[‘HTTP_X_FORWARDED_FOR’]) {
  $_SERVER[‘REMOTE_ADDR’] = $_SERVER[‘HTTP_X_FORWARDED_FOR’];
  $_SERVER[‘SERVER_PORT’] = ’80’;
  unset($service[‘port’]);
}[/HTML][/CODE]

$service[‘port’]는 config.php가 인클루드되기 전에 $_SERVER[‘SERVER_PORT’]가 80번이나 443번이 아니면 세팅되어, 트래백 주소에 포트가 붙게하는 역할을 합니다. 따라서 127.0.0.1에서 proxy 요청이 왔을때는 $server[‘port’] 변수를 날려줍니다. 그러면 트랙백 주소에 포트가 안붙고 깔끔하게 나옵니다.

SLIME 사용방법

M은 메타(Alt)키, C는 Ctrl키.

Emacs든 vi 든 caps lock은 Ctrl로 바꿔 쓰는게 편합니다.

Emacs의 기본적인 사용법은 알고 있다고 가정하고 설명들어갑니다. 그리고 윈도우즈에서 Emacs, CLISP, Slime 인스톨 방법은 이전 글을 참고하세요.

Emacs를 시작하고 “M-x slime”으로 slime을 시작합니다. 잠깐 기달리면 REPL (Read-Evaluate-Print Loop)가 뜹니다. Slime은 크게 두가지 프로그램으로 구성되어 있는데, Common Lisp로 돌아가는 서버 부분(Swank)과, Emacs Lisp로 돌아가는 Front-end 부분이 있습니다. 설정에 따라서 Swank를 서버쪽에 띄우고 로컬에서 Emacs와 Slime Client를 띄우고 작업할수도 있습니다.

REPL에서 Lisp expression을 쳐서 간단한 작업을 할수 있지만, 본격적인 프로그래밍을 위해서는 파일로 작업해야 편합니다. 먼저 C-x C-f로 기존 파일이나 새파일을 열고 Lisp 코드를 짜면 됩니다. 원하는 S-exp을 REPL에서 실행하도록 보내는 단축키 몇개는 지속적으로 사용하게 되고요, Lisp 코드를 편집할때 쉽게 해주는 단축키들이 많은데, 알아두면 도움이 됩니다. 모든 단축키를 설명한 것은 아니니 자세한건 slime 메뉴얼을 구해서 보시기 바랍니다.

  • C-c C-c: 현재 top level form을 컴파일합니다. 주로 defun, defmacro등을 등록할때 사용.
  • C-x C-e: 현재 커서 바로 이전 expression을 실행(Evaluate)합니다. 디버깅할때 유용합니다.
  • C-M-x: 현재 top level form을 실행(Evaluate)합니다. 리턴값이 밑에 표시되고 한번에 입력할수 있기 때문에 많이 씁니다.
  • C-c C-z: REPL로 이동합니다. 이때 화면이 나눠지는데, C-x o로 양 창을 이동할수 있습니다.
  • TAB: 현재줄을 indent합니다. 여러줄을 입력할때 다음줄로 넘어갈때 항상 TAB를 눌러 주는게 좋습니다. 안그러면 자동 괄호 닫기 기능등이 비정상적으로 작동합니다.
  • C-j: 다음줄로 가서 indent합니다. Enter 누르고 TAB 누르는것과 같습니다. 익숙해지면 편하죠.
  • C-c C-q: 열린 괄호를 모두 닫습니다.
  • C-c M-q: 현재 form을 자동 indent합니다.
  • C-c C-k: 현재 파일을 컴파일&로드 합니다.
  • M-. : 해당 definition으로 이동
  • M-, : 이전으로 이동
  • C-c C-d h: 현재 단어를 hyperspec에서 찾아봅니다.
  • C-c TAB 또는 C-M-i : autocompletion. 누르면 창 나눠지면서 포커스 다른 창으로 이동하는데 여기서 C-n,C-p 등으로 원하는 단어 선택해서 Enter치면 완성되고, q를 누르면 취소됩니다.
  • C-c M-i : fuzzy autocompletion. with-output-file을 빨리 칠때 wof하고 C-c M-i치면 w*-o*-f*로 되어있는 거도 포함되어 선택할수 있습니다. 이게 원래 autocompletion보다 편해서 .emacs에서 디폴트로 변경해서 사용하고 있습니다.
  • C-M-n, C-M-p : expression 하나씩 앞뒤로 이동합니다. C-M-u은 상위로 이동합니다.
  • C-c Enter : Macro Expand-1. 매크로 짤때 필수죠.

여기까지가 에디터에서 사용하는 명령들이고, REPL에서도 에디터에서 사용할수 있는 단축키들이 대부분 그대로 통합니다. 추가적으로

  • Enter: Evaluate
  • C-Enter: 괄호 닫고 Evaluate
  • Shift-Enter: Eval하지 않고 다음줄로 내려갑니다.
  • C-↑, C-↓, M-p, M-n : 히스토리 기능
  • ,q : SLIME 종료 (Swank도 종료)

다음은 디버깅 관련 단축키입니다.

  • C-Shift-i: inspect. 원하는 변수등에서 누르면 자세한 정보가 표시됩니다.
  • Ctrl-c Ctrl-t: trace. 원하는 함수이름에서 trace기능을 켜고 끌수 있습니다. trace 기능이 켜 있으면 해당 함수가 호출될때 인자와 리턴값이 표시됩니다. trace된 함수들을 모두 크기 위해서는 REPL에서 (untrace) 하면 됩니다.

실행(Evaluation)중 오류가 나면, 디버거가 뜹니다. 맨위에 오류가 설명되고, 그 밑에 재시작, 끝내기등 실행 재개에 대한 메뉴가 나옵니다. 왼쪽에 숫자를 입력하면 메뉴가 선택되지만, 숫자에 대한 메뉴가 일정하지 않기 때문에 다른 단축키를 알아두는게 좋습니다.

  • q: 종료 (quit)
  • 0: 스택 한단계 위로 이동

밑에는 스택(Backtrace)이 표시되는데, 해당 라인으로 이동하여 Enter를 치면 자세한 내용이 표시됩니다.

  • M-n, M-p: 위/아래로 이동. 새로 이동한 줄의 자세한 내용이 표시되고 그전보던 줄의 자세한 내용은 감춰집니다.
  • v : 해당 스택 frame의 소스로 이동.
  • e : 해당 스택 frame에서 expression을 evaluate
  • i : 해당 스택 frame에서 expression을 inspect

참고 자료:
http://common-lisp.net/project/slime/doc/slime.pdf
http://common-lisp.net/movies/slime.mov

Common Lisp 공부하기

예전에 Paul Graham의 On Lisp로 공부를 시작했었는데, 처음 보기에는 좀 어려운 책인거 같네요.

처음 Lisp 배우기에는 Pratical Common Lisp가 쉬운거 같습니다. 이해하기 쉽게 잘 썼고, 예제 중심으로 잘 설명이 되어 있네요. 이 책을 보고 On Lisp와 Common Lisp The Language(2nd Ed.)을 참고하면서 프로그래밍에 익숙해지면 좋을 듯 합니다. 세 책 모두 웹에서 구할수 있습니다 ^^.

기타 웹사이트 & 자료

CVS to Subversion migration

오늘 1년 가까이 미루어왔던 일을 저질렀습니다. 회사의 CVS 저장소 전체를 Subversion으로 마이그레이션 했습니다. 5년간 작업한 CVS를 옮기면서 몇가지 문제가 발생하긴 했지만 그래도 성공적으로 옮겼습니다.

subversion은 CVS와 가능하면 비슷한 기능과 인터페이스를 제공하면서 부족한 점들을 보완한 버전 관리 도구로, 요즘 오픈 소스 진영에서 많이 사용하고 있습니다. CVS와의 차이점을 살펴보면

1. commit 단위로 버전이 관리됩니다. CVS에서는 파일별로 관리되기 때문에 한번 commit할때 같이 바뀐 파일을 조회하기가 번거롭습니다.

2. repository가 디비로 관리됩니다. 장점도 되고 단점도 됩니다.

3. CVS에 없는 rename, symbolic link등이 지원됩니다.

4. 웹과의 연동이 잘 되어 있습니다. (trac)

5. Client에 공간을 많이 차지합니다. 대신 네트워크는 덜 사용합니다.

자세한것은 subversion 홈페이지 참조하세요.

그리고 trac은 웹기반 통합 개발 관리툴 정도 되는데, 개발할때 부하를 줄 정도로 복잡하지 않고 간단한 기능만 편한 인터페이스로 모아둔 느낌입니다. wiki로 문서를 쉽게 작성할수 있고, 버그 리포팅과 svn 버전 관리와 연동하여 관리가 가능합니다. 역시 자세한 건 trac 홈페이지 참조하시면 되겠습니다.

몇달전에도 cvs2svn 을 이용하여 컨버전하다가 문제가 생겨서 그만둔적인 적이 있었습니다.

사용하는 서버는 FreeBSD고 subversion과 trac을 ports를 이용하여 설치했습니다.

# cd /usr/ports/devel/subversion
# make install
# cd /usr/ports/www/trac
# make install

컨버전 툴인 cvs2svn도 ports를 이용하여 설치하였었지만, 버전이 낮아서 http://cvs2svn.tigris.org에서 받아서 다시 설치했습니다.

시스템에 svn 그룹을 만들고, 사용자들을 svn 그룹에 추가해줬습니다.
그리고 다음처럼 svn repository와 trac 환경을 만들어줬습니다. (편의상 SVNROOT와 TRACROOT 디렉토리로 설명합니다.)

# svnadmin create /SVNROOT
# chgrp -R svn /SVNROOT  (svn 그룹은 만들었다고 가정)
# chmod -R g+w /SVNROOT/db

# trac-admin /TRACROOT initenv
# chown -R www /TRACROOT

이렇게 하고 웹서버에 fast-cgi모듈로 연결해주고, https와 인증을 추가해줬습니다. trac쪽 fastcgi 스크립트는 /usr/local/share/trac/cgi-bin/trac.fcgi 에 있습니다.

여기까지는 큰 어려움 없이 됐고, 웹에서도 잘 접속이됐습니다.

cvs2svn 마이그레이션 하면서 몇가지 문제점이 발생했었는데, cvs2svn을 최신 버전으로 업그레이드하고 cvs2svn 실행할때 몇가지 옵션을 줘서 해결했습니다. 정확한 이유는 모르겠지만 branch 인식을 제대로 못해서 문제가 발생했던거 같습니다. (프로젝트에는 전혀 없는 branch에 대해서 오류가 납니다 –)

# cvs2svn –encoding=euc-kr –existing-svnrepos –trunk-only –trunk=Library -s /SVNROOT /CVSROOT/Library
# cvs2svn –encoding=euc-kr –existing-svnrepos –trunk-only –trunk=Project1 -s /SVNROOT /CVSROOT/Project1
# cvs2svn –encoding=euc-kr –existing-svnrepos –trunk-only –symbol-default=branch –trunk=Project2 -s /SVNROOT /CVSROOT/Project2

CVS의 기존 디렉토리 구조를 유지하고, 다른 프로젝트간에 디렉토리 복사등을 쉽게 하려고 SVN repository를 하나로 잡았습니다. Project2에서 branch 오류가 계속 나서 –symbol-default=branch 옵션을 주어서 해결했습니다. 어차피 branch 했던 코드가 많지 않아서 –trunk-only 옵션으로 메인 branch만 가져왔습니다.

이렇게해서 대충 설정은 끝났고, 네트워크 svn 접속은 ssh를 통하여 접속하도록 하여 별도로 서버(svnserve)를 돌리지는 않았습니다.

따라서 SVN 주소는:

svn+ssh://userid@svn.company.com/SVNROOT

다른 FreeBSD에서 문제없이 checkout 되는거 확인했는데, LANG 환경변수에 따라서 경고가 뜨는데 이건 아직 왜그런지 모르겠습니다.

윈도우즈 클라이언트로는 TortoiseCVS 썼었는데 TortoiseSVN으로 바꿨습니다. http://tortoisesvn.tigris.org 에서 받을수 있습니다. 윈도우즈에서도 위 svn 주소로 접속이 이상없이 됐고, tortoiseCVS와 거의 동일하게 사용할수 있기 때문에 개발자들이 큰 어려움 없이 사용할수 있을것 같네요.

윈도우즈에서 Common Lisp 개발 환경 갖추기

사용 소프트웨어: Emacs, CLISP, SLIME, HyperSpec

1. Emacs: 강력한 에디터.
Nqmacs 버전으로 Emacs 22.1이 될 개발버전을 컴파일한 Win32 배포본을 설치합니다.
http://sourceforge.net/project/showfiles.php?group_id=92168 에서
emacs-22.0.50.1-20050626-w32.zip를 다운로드 하여 적당한 디렉토리에 압축풀면 됩니다.
저는 C:\Program Files\emacs에 설치했습니다.
실행은 bin 디렉토리 밑에 runemacs를 실행하면 되고, 필요할 경우 바탕화면이나 시작 메뉴에 바로가기를 만들어주면 됩니다.

2. Common LISP: Emacs는 LISP로 짜여졌지만, Common LISP(이하 CL)를 사용하기 위해서는 따로 CL을 설치해야합니다. 공개 CL 중에 CLISP, SBCL을 많이 쓰는거 같은데, 여기서는 CLISP를 설치합니다.
https://sourceforge.net/project/showfiles.php?group_id=1355
에서
clisp-2.41-win32-mingw-without-readline.zip를 다운로드 하여 C:\Program Files에 압축을 풀었으며, 설치는 따로 필요없고 Emacs 설정에서 LISP 프로그램만 설정해주면 됩니다. 아래 .emacs에 대해서 설명 참조하세요.

3. SLIME: Emacs 내에서 CL 개발 환경을 만들어주는 플러그인. CLISP, SBCL등 대부분의 CL을 지원합니다.
자주 업데이트 되기 때문에 CVS 버전을 사용할 것을 권장하고 있습니다. 사용중에도 안되는게 있으면 CVS업데이트치라고 하더라고요.
http://common-lisp.net/project/slime/ 에서 설치 관련 정보를 찾을수 있습니다.

CVSROOT는  :pserver:anonymous@common-lisp.net:/project/slime/cvsroot
암호는 anonymous
모듈은 slime

emacs/site-lisp에서 체크아웃하면 됩니다. 저는 TortoiseCVS를 사용하여 체크아웃했습니다.

4. HyperSpec: Common Lisp HyperSpec (CLHS)는 CL 프로그램을 작성하면서 API를 조회할수 있는 HTML 문서입니다. clisp가 설치된 doc 디렉토리 밑에 설치하면 됩니다.
http://www.lispworks.com/documentation/HyperSpec/ 에서 밑에 보시면 download가 있습니다. tar.gz 파일인데 받아서 적당히 풀어주면 됩니다. 디렉토리 구조는 아래처럼 되면 됩니다.

clisp-2.41
  doc
   HyperSpec
       Body
       Data
       Front
       Graphics
       Issues

일단 이렇게 하면 설치는 끝났고 설정만 남았습니다. 설정은 c:\.emacs 파일을 만들어주면 됩니다.

제 .emacs 파일을 공개합니다 🙂 C:\.emacs 에 저장해주면 됩니다.

먼저 한글 관련 설정 (전 세벌식 사용자!):

;; 한글환경 설정
(set-language-environment “UTF-8”)
(setq default-input-method “korean-hangul3”)
(set-input-method “korean-hangul3”)
(prefer-coding-system ‘utf-8-dos)
(utf-translate-cjk-load-tables)

;; 글로벌 폰트락 설정
(global-font-lock-mode 1 t)

;; 한영전환 shift-space로 하기
(global-set-key [?\S- ] ‘toggle-input-method)

shift-space로 한영 전환하면 한글조합하는게 이맥스창 내에서 보이고, 윈도우즈 한/영키를 이용하면 조합할때 다른곳에 보이기 때문에 불편합니다.

리스프 설정:

;; inferior lisp
(setq inferior-lisp-program “c:/Progra~1/clisp-2.41/base/lisp.exe -M c:/Progra~1/clisp-2.41/base/lispinit.mem”
     lisp-indent-function ‘common-lisp-indent-function
     slime-complete-symbol-function ‘slime-fuzzy-complete-symbol
     slime-startup-animation nil)

;; slime
(add-to-list ‘load-path “c:/Progra~1/emacs/site-lisp/slime”)
(require ‘slime)
(slime-setup)

(setq common-lisp-hyperspec-root “c:/Progra~1/clisp-2.41/doc/HyperSpec/”)
;(setq common-lisp-hyperspec-root “http://www.lispworks.com/documentation/HyperSpec/“)

inferior-lisp라고 되어있는건 emacs lisp가 더 좋다는 의미에서 나온게 아닐까 생각이 드네요. 지금 찾아보니 emacs 밑에 붙는다고 해서 inferior인거 같네요… hyperspec은 LISP 온라인 문서입니다. LISP 배우실려면 로컬에 설치하는것이 좋죠.

기타 설정:

;(setq visible-bell t)
;(setq sound-alist ‘(t 1))
;(setq bell-volume 10)

(global-set-key “%” ‘match-paren)
        
(defun match-paren (arg)
  “Go to the matching paren if on a paren; otherwise insert %.”
  (interactive “p”)
  (cond ((looking-at “\\s\(“) (forward-list 1) (backward-char 1))
((looking-at “\\s\)”) (forward-char 1) (backward-list 1))
(t (self-insert-command (or arg 1)))))

(setq make-backup-files t)

앞에 세줄은 오류날때 소리나는거 조절할수있는데, 지금은 주석처리되어 있네요. vi에서 처럼 %로 괄호간 이동할수 기능이 구현되어 있습니다. 커서가 괄호위에 있을때만 동작합니다.

.emacs 파일에 더 내용이 있긴 한데, 나머지는 사용하면서 자동으로 Emacs에서 추가해준 설정 내용이라 공개 안합니다.

그리고 Slime 시작할때 약간 오류가 나는데 slime.el 파일을 약간 바꿔줘야합니다. CVS에 따라서 라인은 약간 다를수 있지만 현재는 1890번줄에서

‘(iso-latin-1-unix iso-8859-1-unix binary))

       –>
‘(utf-8-unix emacs-mule-unix binary))

위 처럼 바꿔주면 정상적으로 slime이 시작됩니다. 고친 방법이 정상적인 방법인지는 저도 모르지만, 아직까진 문제없이 잘 돌리고 있습니다.

그럼 모든 설치가 완료되었으니 runemacs로 Emacs를 실행하고 “M-x slime” 하면 REPL이 뜨고 LISP를 바로 사용할수 있습니다. lisp 파일 편집중 C-c C-c로 현재 위치의 S-exp을 LISP로 보낼수 있으며, C-x C-e로 커서 바로 이전 S-exp를 부분적으로 실행해 볼수 있습니다. 편집중 C-c C-z로 REPL로 다시 갈수 있습니다. SLIME 기능이 많기 때문에 좀더 공부해서 다음에 다시 설명하겠습니다.^^