2009년 4월 29일 수요일

Emacs에서 한글 맞춤법 검사기 사용하기

(2013.4.30에 업데이트된 포스트 가 있습니다.)

소개

작년 이맘때까지만 하더라도 소스가 공개된 쓸만한 한글 맞춤법 검사기가 없었습니다. 며칠 전에 hunspell에서 사용 가능한 한글 단어 사전이 개발 되고 있다는 것을 알게 되었습니다.
OpenOffice와 Firefox, 그리고 기타 hunspell이나 enchant를 사용하는 소프트웨어들에서 잘 동작합니다. Emacs에서도 ispell과 enchant를 이용하면 한글 맞춤법 검사를 할 수 있습니다.

Emacs 설정

우선 hunspell 1.2.8 이상의 버전과 한글 사전 데이터(.aff와 .dic 파일), enchant 가 깔려 있어야 합니다.
(우분투 9.04의 경우 기본 hunspell 버전이 1.2.6인데 launchpad ubuntu-ko 저장소에 hunspell 1.2.8 버전과 hunspell-ko-kr이 패키징 되어 있습니다. 아래 저장소를 추가하면 쉽게 설치가 가능합니다.)
 deb http://ppa.launchpad.net/ubuntu-ko/ubuntu jaunty main
 deb-src http://ppa.launchpad.net/ubuntu-ko/ubuntu jaunty main
(enchant는 libenchant1c2a 패키지에 있습니다.)
이제 .emacs 파일에 아래의 내용을 추가하면 ispell.el로 한글 맞춤법 검사가 가능하게 됩니다. (flyspell-mode도 잘 동작합니다.)
;; enchant를 ispell 프로그램으로 사용
(setq ispell-program-name "enchant")

;; 사전 목록에 한국어("korean") 추가
(if (>= emacs-major-version 23)
    (setq ispell-local-dictionary-alist
      '(("korean"
         "[가-힣]"
         "[^가-힣]"
         "" nil
         ("-d" "ko_KR")
         nil utf-8)))
    (setq ispell-local-dictionary-alist
      '(("korean"
         "[가-힝]"
         "[^가-힝]"
         "" nil
         ("-d" "ko_KR")
         nil utf-8))))

;; 한국어를 기본 사전으로 지정
(setq ispell-dictionary "korean")
Emacs 버전 22와 23에서 테스트 되었습니다. 버전 22에서는 유니코드로 표현 가능한 모든 글자를 지원하지 않기 때문에 정규식에 '힣'이 들어가면 제대로 보이지도 않고 동작하지도 않습니다. 버전에 따라서 ispell-local-dictionary-alist 설정을 다르게 한 것은 이 때문입니다.

이야기

그동안 공개된 한글 맞춤법 검사기가 없어서 얼마나 아쉬웠는지 모릅니다. 저 뿐만 아니라 많은 분들이 비슷하게 한글 맞춤법 검사기를 기다려왔을 것입니다. 류창우님이 해결책의 실마리를 hunspell 에서 발견했습니다. 이후로 관심 있는 사람들이 모여서 구글 그룹을 만들고 쓸만한 사전을 만들기 시작했습니다.
이런 일이 진행되고 있다는 것을 알게 된 것은 며칠 전 KLDP에서 먼지분투에 대한 글을 보면서였습니다. 글이 KDLP 메인 화면에 자주 나오 길래 뭔가 하고 한 번 들어가 본 것인데 "* hunspell 기반 맞춤법 검사 사전 내장" 이라는 항목에 정신이 번쩍 들었습니다. '어? 한글 맞춤법 검사 사전이 있다고? 왜 이걸 여태 모르고 있었지?' 오버라고 생각할지도 모르겠지만, 저는 전율을 느꼈습니다. (조금 지난 후에 알았는데 KLDP 에 류창우님이 직접 올린 소개의 글이 있더군요.)
우선 아직 양이 얼마 되지 않기 때문에 관련 페이지들과 구글 그룹의 글을 대부분 살펴 볼 수 있었습니다. OpenOffice에서 잘 동작하는 것은 확인을 하였고, Firefox 에서는 빨간 줄이 나타나지 않아서 한참을 헤맸었습니다. 알고 보니 언어 선택을 바꾸지 않아서였더군요. ㅜㅜ
이제 주로 사용하는 프로그램 중에 emacs에서만 한글 맞춤법 검사가 되면 사용에 문제가 없을 것 같았습니다. 그룹 글들을 검색해 보고 nomos님의 시도를 발견했습니다. 일단 창우님의 수정된 hunspell을 설치하고 my-emacs-hunspell을 만들었습니다. 무슨 문제인지 맞춤법 검사도 안 되고 오류도 발생하지 않아서 답답했습니다. hunspell -a -d .... 을 직접 실행시키고 맞춤법이 틀린 문장을 넣으면 잘 동작했기 때문에 emacs와 hunspell의 통신 상에 문제가 있는 것이라는 의심을 했습니다. 둘 사이에 끼여서 관찰했으면 좋겠다는 생각을 했지만 간단한 방법이 떠오르지가 않았습니다. (아시는 분은 댓글 달아 주세요.^^)
차선책으로 떠오른 것이 tee 였습니다. tee 는 표준 입력을 파일과 표준 출력에 동시에 써주는 명령입니다. 개선 과정을 거쳐서 나온 최종 my-emacs-hunspell은 아래와 같습니다.
#!/bin/bash
# /usr/bin/hunspell -a -d /usr/share/myspell/dicts/ko_KR
tee ~/tmp/ei_$$.txt | /usr/bin/hunspell -a -d /usr/share/myspell/dicts/ko_KR | tee ~/tmp/eo_$$.txt
($$는 자신의 process id로 바뀝니다. 한 번 ispell-region을 실행하는 경우에도 my-emacs-hunspell을 여러 번 부르는 경우가 있었기 때문에 pid 별로 다른 파일이 생기도록 했습니다. emacs에서 ispell 프로세스가 생길 때마다 ~/tmp/ 디렉터리에 ei_와 eo_로 시작하는 두 개의 파일이 만들어지게 됩니다.)
여러 번의 실패한 시도와 문서 찾아보기의 결과로 위의 ispell-program-name 부분을 제외한 emacs lisp 코드에 정착하게 되었고 nomos님의 결과와 같은 결과를 만들어 낼 수 있었습니다. (검사가 잘 되기도 하고 어떤 때는 Ispell misalignment:...에러가 나는 결과)
에러의 원인을 알아내는 데는 그리 오래 걸리지 않았습니다. 첫 번째 한글 단어가 맞춤법이 잘못되었다고 판단하면 에러가 발생하지 않지만, 첫 번째 한글 단어가 아닌 경우에는 오류가 어김 없이 발생했습니다. 이유는 hunspell의 출력과 emacs의 ispell.el에서 기대하는 입력이 일치 하지 않기 때문입니다. 둘 간의 통신은 emacs가 한 줄을 보내면 hunspell이 맞춤법 검사 결과를 돌려주는 방식입니다. 맞춤법이 잘못된 단어를 발견하면 잘못된 단어의 위치를 알려 주게 되어있습니다. 이 위치가 hunspell은 몇 번째 바이트인가를 알려주는데, ispell.el은 몇 번째 글자인가를 알려주기를 기대하고 그 위치에서 단어를 찾으려니 에러가 발생하는 것입니다.
ispell.el을 고치는 것이 맞는지, hunspell을 고치는 것이 맞는지, (좀 더 정확히는 어떤 것을 고치는 것이 더 간단한지) 고민하던 중에 제3의 프로젝트에서 해결책을 발견하게 되었습니다. 그것이 enchant입니다.
Enchant는 말하자면 여러 맞춤법 라이브러리들을 enchant 라이브러리를 통해서 사용할 수 있게 해주자는 그런 프로젝트입니다. (홈페이지 참조) 명령행 실행기(command line program)에 대해서 그저 "technology demo"라고 하고 있기는 하지만, 일단 검사와 대체 단어 제시 기능을 사용하는 데는 문제가 없었습니다. 이렇게 해서 ispell-program-name"enchant"로 바꾸는 것으로 emacs에서 한글 맞춤법 검사 기능 사용하기 "완료".

남은 일들

좀 있습니다.

끝으로

hunspell로 한글 맞춤법 검사가 가능하도록 구체화하는 데 첫 삽을 떼시고 (아직까지는) 작업 대부분을 감당하고 계신 류창우님, 감사합니다. 아울러 옆에서 류창우님의 작업을 도와주시고 격려해 주셨던 다른 여러분께도 감사의 말을 드립니다. 지금의 hunspell이 있기까지 애쓴 여러 연구자들과 개발자들께도 감사의 말을 드립니다.
갈퀴를 이용해서 단어 추가/삭제/변경 등의 제안을 하실 수 있습니다. 작은 참여들이 모여서 훌륭한 맞춤법 검사 환경이 만들어질 수 있습니다. 주저하지 마시고 참여를...
(한두 시간 안에 끝날 줄 알았던 글을 쓰는 데 꽤나 오랜 시간이 걸렸습니다. 너무 오랫동안 글을 안 써서 그런가 봅니다. 잠시, 아직 자리잡히지도 않은 갈퀴에 사람이 너무 몰려서 오히려 방해가 되지는 않을까 어이없는 걱정을 했었습니다. 그리고 깨달은 현실은 '여기 보는 사람이 얼마나 된다고...')

웹페이지들

2008년 9월 25일 목요일

Windows 에서 git 사용

Cygwin? msysGit?

Cygwin 을 쓰던 사람이라면 Cygwin 으로 배포되는 git 을, git 이외에 Cygwin 류의 것을 쓸 일이 없다는 사람은 msysGit 을 추천한다.

(Cygwin 홈 페이지 자신을 "a Linux-like environment for Windows" 라고 소개하고 있다.)

Cygwin 팁

Windows 폴더 팝업 메뉴에서 Cygwin shell 을 바로 띄우는 방법이다. fvue.nl/wiki/Bash_and_Windows 에 자세한 내용이 있다. (msysGit 은 설치하면 "Git Gui Here"와 "Git Bash Here" 메뉴 아이템을 추가한다.)

C:\cygwin\cygwin_here.bat (Window2000 이상)

 @echo off
 REM --- cygwin_here.bat ------------------------------------------------------
 REM function: Start Cygwin in current directory
 REM args:     - 1..9

 REM Setting `CHERE_INVOKING' prevents /etc/profile from issuing `cd $HOME'
 set CHERE_INVOKING=1
 C:\cygwin\bin\bash --login -i %1 %2 %3 %4 %5 %6 %7 %8 %9

registry 수정

 HKEY_CLASSES_ROOT
 |-Folder
   |-shell
     |-Shell to Cygwin   "Shell to Cyg&win"
       |-command         "C:\cygwin\cygwin_here.bat"

혹시 권한을 탓하며 실행을 거부하는 경우에는 Cygwin 을 실행하고 권한을 주면 된다.

 cd /
 chmod +x cygwin_here.bat

(Cygwin 에서 cat 으로 cygwin_here.bat 을 만들었는데 실행권한이 없었다. 그냥 다른 Windows 에디터를 사용했었다면 이런 일이 안 생겼을지도 모르겠다.)

원활한 원격 작업을 위한 설정

원격 저장소와 통신에 사용하는 디폴트 프로토콜은 ssh 이다. 보통 아무 작업도 해주지 않으면 push 나 pull 할 때 암호를 물어오게 되어 있다. 암호를 입력해 주면 되는데, 문제는 그렇게 두면 git gui 에서는 원격 작업을 할 수 없다는 것이다.

작업 머신에서 서버에 암호 입력 없이 접근할 수 있게 해주는 방법이 있다.

 ssh-keygen.exe -C "you@your.email.com" -t dsa

 scp ~/.ssh/id_dsa.pub  you@yourgitserver:~/.ssh/authorized_keys

(이 때 한 번 로그인 암호를 입력하게 된다.)

ssh-keygen 시에 passphrase 를 물어 오는데 입력을 하지 않는 것이 편하다. 입력을 하고 나중에 ssh-agent, ssh-add 를 이용하면 많이 불편하지는 않지만, 입력을 하지 않는 것 보다는 불편하다.

ssh-agent, ssh-add 사용하기

만일 주인이 없는 사이에 누군가 개발 머신을 켜고 원격 저장소를 엑세스하는 것을 막고자 한다면, 아니면 누군가 id_dsa 를 복사해 가서 원격 저장소를 엑세스 할 가능성을 없애려면, 매번 암호 입력을 하거나 passphrase 를 사용해서 ssh-keygen 을 해야만 한다.

아래는 ssh-agent 와 ssh-add 를 사용하는 방법이다. 먼저 Cygwin 이나 Git-Bash 를 실행하고

 eval `ssh-agent -s`
 ssh-add

passphrase 를 입력해준다.

이제 마음껏 작업한다.

 git pull
 git push
 git gui
 ...

다른 사람이 자리에 앉아서 서버에 접속할 가능성을 차단하기 위해 ssh-agent 를 죽인다.

 ssh-agent -k

물론 컴퓨터를 끄고 집에 가려 한다면 마지막 명령은 필요없다.

2008년 9월 24일 수요일

Git - CVS 에서 전환

첫번째 문서 중 중요 내용 요약, 약간의 내용

단어들

저장소
repository
중앙 저장소
central repository
개별 저장소, 작업본 저장소
local repository
작업본
working copy
bare 저장소
작업본이 없는 저장소
SHA1-number
git 은 모든 object 에 SHA1 hash number 를 생성한다. 이것은 어떤 파일이나 특정 commit 등을 지정하는데 사용할 수 있다.

처음

이름과 이메일 설정

 git config --global user.name "Your Name"
 git config --global user.email "my@email.com"

원할 경우, conflicts 해결을 위해 머지(merge) 툴을 설정할 수 있다.

 git config --global merge.tool meld

CVS 에서 하던 것처럼

괜히 더 복잡하다고 생각할 수도 있다.

중앙 저장소를 두는 셋팅의 경우.

가져오기
 cvs checkout

 git clone <server>:/path/to/repos/project.git
중앙 저장소에 바뀐 부분 가져오기
 cvs update

 git pull origin master
수정한 것을 중앙 저장소로
 cvs update
 cvs checkin file1 file2 ...

 git pull origin
 git commit file1 file2 ...
 git push origin master

여기에서 git 과 CVS 의 차이가 확연히 나타난다. git 의 commit 은 개별 저장소에만 commit 을 하는 것이다. (git 의 경우 작업본마다 개별 저장소를 가지고 있다.) 중앙 저장소에 push 하는 것은 별개의 작업이다.

좀 더 유용한 작업 방식은 아래 처럼.

 ... 작업
 git commit file1 file2 ...
 ... 작업
 git commit fileN dir2 ...
 ... 작업
 git commit -a  # (수정 사항 모두 commit)
 ... 새로운 기능 완성!
 git pull origin master
 git push origin master
기타 작업
 cvs add file1 ...
 git add file1 ...

 cvs remove file1
 git rm file1

파일 이동이나 이름 바꾸기

 [cvs remove, add 각각]
 git mv file1 file

지운 파일 살리기

 cvs update
 git checkout file1

commit 을 자주

git 과 CVS 의 가장 큰 차이 두가지:

  • 작업본이 완전한 저장소 - commit 과 commit 들을 보내는 차이가 존재함
  • 각각의 파일 상태를 추적하는 것이 아니라 전체 상태를 추적함

git 의 commit 은 CVS 의 그것과 같다. 다만 push 하기 전까지 그것은 개발자의 작업본 저장소에만 적용된다. 중앙 저장소로 push 하면 개발자의 저장소와 중앙 저장소는 같은 상태가 된다. 마지막 push 이후 12번의 commit 을 했다면 12번의 commit 이 중앙 저장소로 push 된다. CVS 에서라면 서버에 직접 12번 commit 을 했어야 한다. git 에서는 원하는 만큼 commit 하고 됐다고 생각될 때 서버로 작업한 것을 올릴 수 있다.

결론 : git 은 자주 commit 하는데 부담이 없다.

Branch

git 에서 branch 와 merge 는 (CVS에 비해서) 아주 간단하다. 사실 각각의 개별 저장소는 각각의 branch 라고 할 수 있다.

branch 만들기

 git branch <branch>

특정 지점 으로 branch 만들기

 git branch <branch> <지점>

<지점> - 다른 branch, tag, SHA1-number 등이 올 수 있다.

branch 목록 보기

 git branch

작업 branch 바꾸기

 git checkout <branch>

git checkout 은 branch, tag 등 지점을 가져오는데도 사용되고 파일이나 디렉토리를 가져오는데도 사용된다.

branch 지우기 - commit 들이 현재 branch 에서 추적 가능한 경우에만

 git branch -d <branch>

branch 지우기 - commit 들이 추적 불가능한 경우에도 지워버림

 git branch -D <branch>
작은 오류 수정 push

작은 오류 수정을 해야할 필요가 있는데, 많은 commit 을 했고 push 할만한 상태가 아닐 경우, branch 가 유용할 수 있다.

  • 아직 수정하지 않은 경우 - 임시 branch (fix 라고 이름 지었다고 가정)를 만들고 checkout 한다. 문제를 해결하고 commit, 서버에 push 한다. 작업 branch 를 chekout 하고 수정사항을 서버에서 pull 한다. fix branch 를 지운다.
  • 문제를 이미 해결하고 commit 해둔 경우 - 임시 branch (fix)를 만든다. 오류 수정한 commit 의 SHA1-number 를 찾는다. (gitk 나 git log 사용) git cherry-pick SHA1-number 로 해당 commit 만을 fix branch 에 반영한다. push 하고 작업 branch 를 checkout 한다. fix branch 를 삭제한다.
기타 유용한 경우
  • 실험 branch
  • 둘 이상의 release 관리
  • 특화 기능 추가 요청
  • 등등 …

submodule

git 은 파일 하나 하나의 변화를 추적하는 대신 전체의 변화를 한 commit 으로 관리한다. 부분별 관리가 따로 필요할 경우를 위해서 만들어진 기능이 submodule 기능이다.

Manual 참조.

기타

  • push 는 bare 저장소에 쓰기 위한 것이다. 작업본이 있는 저장소에도 push 를 할 수 있기는 하지만 작업본까지 바꾸지는 않기 때문에 개발자를 혼란에 빠뜨릴 것이다.
  • GUI 가 있다. "git gui". pull은 없고 fetch 와 merge 를 해야한다.
  • git bisect - 버그가 들어온 경우, 어떤 commit 때문인지를 2진 검색(binary search) 으로 찾을 때 사용.