Canvas 1 Layer 1

의존성 관리 도구 캐스크를 사용한 이맥스 환경설정

들어가며: 이맥스(Emacs)의 패키지 관리

이맥스Emacs 24 버전부터는 기본적으로 패키지 관리자가 포함되어있지만, 이를 통해서 설치되는 패키지는 전역적으로 설치됩니다. 이러한 방식은 편리하지만, 각각의 프로젝트에게는 섬세하지도, 적절하지도 않습니다. 이맥스 24의 기본 패키지 관리자는 루비와 비교하면 젬Gem에 해당합니다. 루비에서 캐스크]의 역할을 담당하는 것은 젬이 아니라 바로 번들러Bundler입니다. 캐스크는 각 패키지의 의존성을 패키지 단위로 관리해주고, 패키지나 이맥스를 해당하는 의존성을 바탕으로 실행할 수 있도록 도와주는 도구입니다.

캐스크는 이러한 의존성 관리를 크게 두 방향에서 사용할 수 있도록 해줍니다. 먼저 하나는 번들러와 같이 프로젝트 단위로 자신의 의존성을 정의하고 프로젝트를 개발할 수 있는 환경을 만들어주는 역할입니다. 두번째는 이맥스를 에디터로 사용하는 사람들의 입장에서 패키지들을 체계적으로 관리할 수 있게 도와줍니다. 이 글에서는 캐스크를 통해서 이맥스 환경 설정을 관리하는 방법에 대해서 소개합니다.

캐스크(Cask) 설치

컈스크는 아래 명령어로 설치할 수 있다. 시스템에 파이썬이 설치되어있어야 합니다.*

* 캐스크는 이맥스 24 버전 이상에서만 사용가능합니다.

$ curl -fsSkL https://raw.github.com/cask/cask/master/go | python

맥OSmacOS에서 홈브류Homebrew를 사용하고 있다면 brew 명령어를 사용해서 설치하는 방법도 있습니다.

$ brew install cask

정상적으로 설치되었는지 확인해봅니다.

$ cask --version
0.7.0

캐스크Cask 사용하기

본적적인 .emacs.d 환경설정에 앞서 cask 명령어를 간단히 사용해보겠습니다. 임의의 위치에 캐스크를 테스트해볼 디렉터리를 만듭니다. 여기서는 ~/tmp/cask를 사용합니다.

$ mkdir ~/tmp/cask
$ cd ~/tmp/cask

init 명령어를 통해서 기본 설정을 포함한 Cask 파일을 생성할 수 있습니다.

$ cask init
$ head Cask
(source gnu)
(source melpa)

(depends-on "bind-key")
(depends-on "cask")
(depends-on "dash")
(depends-on "drag-stuff")
(depends-on "exec-path-from-shell")
(depends-on "expand-region")
(depends-on "f")
...

head로 초기화된 Cask 파일을 출력해보면 위와 같은 내용이 출력됩니다. Cask 파일에서 사용하는 명령은 기본적으로 sourcedepends-on 함수입니다. 일단 source는 패키지를 가져오는 저장소를 의미하고 depends-on은 사용하는 패키지를 정의한다는 정도만 이해하고 넘어갑니다.

이제 cask 명령을 통해서 의존성을 설치합니다.

$ cask
Wrote /home/nacyot/Dropbox/programmings/sandbox/cask/.cask/24.3.1/elpa/archives/gnu/archive-contents
Wrote /home/nacyot/Dropbox/programmings/sandbox/cask/.cask/24.3.1/elpa/archives/melpa/archive-contents

의존성이 정상적으로 설치되었는지 살펴봅니다. 의존성은 기본적으로 cask 명령어를 실행한 위치의 Cask 파일을 사용해서 설치되며, 설치 위치는 명령어를 실행한 디렉터리 아래의 .cask가 됩니다.

$ ls .cask/24.3.1/elpa/
archives                           flycheck-cask-20140118.923        popwin-20140426.659
bind-key-20140414.1744             git-commit-mode-20140313.1504     prodigy-20140421.2359
cask-20140324.15                   git-rebase-mode-20140313.1504     projectile-20140427.251
dash-20140407.253                  htmlize-20130207.1202             s-20131223.944
diminish-20091203.1012             idle-highlight-mode-20120920.948  shut-up-20140211.521
drag-stuff-20140121.723            magit-20140416.1539               smartparens-20140414.606
epl-20140405.51                    multiple-cursors-20140418.815     smex-20140425.1314
exec-path-from-shell-20140219.104  nyan-mode-20120710.2200           use-package-20140317.1213
expand-region-20140406.324         package-build-20140422.803        web-mode-20140425.1520
f-20140220.21                      pallet-20140413.1345              yasnippet-20140314.255
flycheck-20140422.657              pkg-info-20140405.50

정상적으로 설치된 것을 알 수 있습니다. 이제 이러한 패키지들을 바탕으로 emacs를 실행시켜보겠습니다.

$ cask exec emacs

이맥스를 실행하면 기존의 설정에 추가적으로 현재 Cask 파일의 의존성이 로드됩니다.

캐스크를 활용한 이맥스 환경설정

여기까지 이야기한 내용은 특정 프로젝트에서 의존성을 관리하는 방법에 가깝습니다. 지금부터는 전역적인 이맥스 사용자 설정을 다루겠습니다.

이맥스 23 이후 버전의 환경설정은 기본적으로 ~/.emacs.d/init.el 파일을 거쳐서 실행됩니다. 따라서 먼저 캐스크 초기화 코드를 이 파일에 추가해줍니다.

(require 'cask "~/.cask/cask.el")
(cask-initialize)

첫번째 줄에서는 앞서 설치한 cask.el을 로드합니다. 로드 경로에서도 알 수 있듯이 캐스크는 기본적으로 홈 디렉터리 바로 아래의 .cask에 설치됩니다. 다른 위치에 설치했다면 물론 해당하는 경로를 지정해야합니다. 다음 줄에서는 cask-initialize 함수를 호출해 cask를 초기화합니다. 이 때 캐스크를 환경설정에 대해서 초기화하기 위한 Cask 파일이 필요합니다. 이 파일은 ~/.emacs.d/Cask에 위치해야합니다.

Cask 파일 살펴보기

다음과 같이 Cask 파일을 정의합니다.

(source gnu)
(source melpa)
(source marmalade)

(depends-on "ruby-mode")
(depends-on "rspec-mode")
(depends-on "robe")
(depends-on "rinari")
(depends-on "magit" "0.8.1")
(depends-on "ox-reveal" :git "git@github.com:yjwen/org-reveal.git")

앞서도 이야기했지만 Cask 파일에서 사용할 수 있는 기본적인 함수는 sourcedepends-on입니다. 다른 함수들도 있지만 일반적으로 환경설정을 위한 용도에서는 사용하지 않습니다.

source: 이맥스 패키지 저장소 지정

먼저, source 함수는 패키지를 가져올 저장소를 지정하는 명령어로 아래와 같이 사용할 수 있습니다.

(source ALIAS)
(source NAME URL)

실제로는 아래와 같이 사용합니다.

(source melpa)
(source "melpa" "http://melpa.milkbox.net/packages/")

패키지가 인식할 수 있는 저장소의 별칭을 사용하면 URL을 지정하지 않아도 됩니다. 사용할 수 있는 별칭은 아래와 같습니다.

gnu (http://elpa.gnu.org/packages/)
melpa (http://melpa.milkbox.net/packages/)
marmalade (http://marmalade-repo.org/packages/)
SC (http://joseito.republika.pl/sunrise-commander/)
org (http://orgmode.org/elpa/)

gnu, melpa, marmalade는 주로 사용하므로 지정해두는 것을 추천합니다.

depends-on: 이맥스의 의존성 정의

depends-on은 사용하고자 하는 패키지를 지정하는 함수입니다. 예를 들어 ruby-mode 패키지를 사용하고자 하면 아래와 같이 사용합니다.

(depends-on "ruby-mode")

위와 같이 ruby-mode를 지정하면 위에서 source에서 지정한 저장소에서 해당하는 패키지를 찾아 설치한다. 이 때 필요한 경우 아래와 같이 특정 버전을 강제할 수 있습니다.

(depends-on "magit" "0.8.1")

기본 패키지 관리자에 비해서 가장 큰 장점 중 하나는 깃Git으로 관리되는 패키지를 직접 지정할 수 있다는 점입니다. 아래는 공식 문서에서 제공하는 예시입니다. 깃 저장소를 지정했을 때 특정한 커밋이나 브랜치, 혹은 특정 파일들만 로드할 수 있도록 하는 법을 알 수 있습니다.

(depends-on "magit" :git "https://github.com/magit/magit.git")
(depends-on "magit" :git "https://github.com/magit/magit.git" :ref "7j3bj4d")
(depends-on "magit" :git "https://github.com/magit/magit.git" :branch "next")
(depends-on "magit" :git "https://github.com/magit/magit.git" :files ("*.el" (:exclude "magit-svn.el")))

cask 명령어

필요한 패키지 설정이 모두 끝났으면 작업 디렉토리를 ~/.emacs.d로 옮겨서 cask 명령을 실행합니다. 이를 통해서 지정한 패키지들을 모두 설치할 수 있습니다. 하나 알아둬야할 점은 명시적으로 cask 명령을 emacs 외부에서 실행하지 않으면 이맥스 실행 시 해당하는 패키지가 적용되지 않는다는 점입니다.

추가적으로 모든 패키지를 업데이트하고자 할 때는 cask update 명령어를 사용합니다.

팁: 파렛트(pallet) 패키지(이맥스 안에서 패키지 관리)

파렛트pallet 패키지와 함께 사용하면 좀 더 편리하게 사용할 수 있습니다.* 예를 들어 M-x pallet init 명령어를 실행하면 현재 package.el로 설치된 패키지들을 Cask 파일에 입력해줍니다. 앞서 Cask 파일은 cask install 명령을 실행하지 않으면 바로 반영이 되지 않는다고 하였는데, 파레트 패키지를 사용하면 Cask 파일을 수정하고, M-x pallet-install 명령어를 실행하면 바로 패키지가 설치됩니다. 또한 M-x package-install이나 M-x list-packages 명령어로 설치한 패키지들을 자동을 Cask 파일로 옮겨줍니다.

* 주의 : pallet 명령어 사용시 Cask 파일이 변경되는 경우가 있습니다. 이 때 깃 저장소를 지정해둔 정보가 삭제될 가능성이 있습니다. Cask.diff 참조.

팁: init-loader를 활용한 이맥스 초기화

앞서 이야기한 바와 같이 이맥스 초기화는 기본적으로 init.el을 거쳐가며 대부분의 설정파일은 여기에서 이루어집니다. 캐스크 사용 시에도 캐스크를 초기화하고, 캐스크를 통해 읽어들인 각 패키지에 대한 설정은 init.el파일에서 할 수 있습니다. 하지만 이 역시 설정할 내용이 많을 때는 init.el 파일이 매우 복잡해지기 때문에 init-loader를 사용해 좀 더 편리하게 관리할 수 있습니다.

init-loader 패키지를 사용하려면 먼저 Cask 파일에 추가해줍니다.

(depends-on "init-loader")

init.el에는 아래 내용을 추가합니다.

(init-loader-load "~/.emacs.d/init-loader/")

init-loader-load는 초기화 파일들을 위치시킬 디렉터리를 지정합니다. 여기서는 ~/.emacs.d/init-loader를 지정했으면 원하는 디렉터리를 지정하면 됩니다. 위와 같이 설정하면 init-loader은 해당하는 폴더 아래의 숫자 2개로 시작하는 모든 파일을 로드합니다.

00_util.el
01_ik-cmd.el
21_javascript.el
99_global-keys.el

위와 같이 적절히 초기화 설정을 분리하면 됩니다. 일반적으로는 패키지 단위로 설정을 관리하는 게 좋습니다.

설정 파일 컴파일하기

일반적으로 패키지는 설치과정에서 컴파일됩니다. 하지만 다른 설정 파일이 많아질 수록 이맥스 시작 시간도 길어집니다. 필요하다면 init-loader 폴더나 설정 파일을 보관하는 다른 폴더는 아래 명령어로 컴파일 해두는 게 좋습니다.

C-u 0 M-x byte-recompile-directory

마치며

이맥스를 사용하기 어렵게 만드는 것 중 하나는 언제나 환경설정이었습니다. 이맥스는 정말 방대한 툴이고, 그만큼 사람에 따라서 수십에서 심지어는 수백개 패키지를 사용하는 것도 이상하지 않습니다. 일반적으로 이맥스를 잘 사용한다는 데는 단순히 단축키를 수십개 더 외우고 있느냐 보다 이런 다양한 패키지들을 얼마나 잘 정리하고 활용하는 지가 중요합니다.

캐스크 공식 문서를 보면 아마 대부분의 이맥스 사용자가 사용해온 기존의 패키지 관련 툴들에 관한 이야기가 나옵니다. 오래 전에는 [[El-get]]이 있었고, 이맥스 24에서 공식으로 포함된 [[package.el]] 같은 툴도 있었습니다. 그리고 저장소의 서브 모듈을 사용하는 방법이나 의존 패키지를 통째로 패키지에 포함해서 의존성을 정의하는 방법도 사용해왔습니다. 필자도 대부분의 방법을 경험했지만 결국엔 두손두발 다 놓게 만드는 게 이맥스 설정입니다. 이맥스 리습Emacs Lisp도 잘 모르고, 한 번 아무리 공을 들여서 설정해봤자, 시간이 지나면 역시 관리는 잘 되지 않습니다. 이런 척박한 토양 위에서, 캐스크는 이맥스 사용자에게 단비와 같은 패키지입니다.

함께 읽으면 좋은 자료들