Org-mode를 노트북으로 사용하기
주피터 노트북(Jupyter Notebook)이 세상에 나온지 꽤 됐지만 그동안 눈길 한 번 주지 않았다. 주피터 노트북에서는 파이썬을 사용하는 경우가 많은데, 내가 파이썬을 별로 좋아하지 않아서 그랬던 것 같다. 어찌어찌 하여 쓸 일이 생겼는데, 막상 써 보니 상당히 괜찮은 것 같았다. 문서에 코드와 그 실행 결과, 설명을 함께 쓸 수 있다는 점이 마음에 들었다.
프로그램을 작성할 때, REPL에서 간단한 코드를 작성해 테스트 해보곤 한다. REPL에서 테스트한 코드와 배운 내용을 어딘가에 정리해 놓고 싶었지만, 정리에는 시간도 필요하고 원래 하던 자업의 흐름도 끊기기 때문에 간단한 일은 아니었다. REPL에서 테스트한 코드는 그렇게 그냥 사라졌고, 배웠던 내용도 시간이 지나면서 까먹었다.
주피터 노트북과 같은 형태로 테스트 코드를 저장하고 실행 결과와 간단한 설명을 곁들여 놓으면 좋겠다는 생각이 들었지만, 그걸 위해 주피터 노트북을 쓰는 것은 너무 거하다는 생각이 들었다. 별도 웹 서버를 띄우고 브라우저로 접속해야 하는 점이 마음에 들지 않았다. 그러다 Babel을 이용해 Emacs Org-mode에서 비슷한 흉내를 낼 수 있다는 것을 알게 되었다.
org-babel
설치 및 설정
패키지 매니저(M-x list-packages
)를 통해 org-babel
을
설치한다. 그리고 Org mode에서 사용할 언어 패키지를 설치해야 한다. 예를
들어, Rust를 사용하려면 ob-rust
패키지를, Python을 사용하려면
ob-ipython
패키지를 설치해야 한다. 패키지 매니저에서 ob-
로
시작하는 패키지를 살펴보면 어떤 패키지가 있는지 확인할 수
있다. ob-restclient
도 보여 설치했다.
그리고 org-babel
을 활성화할 언어를 org-babel-load-languages
에
추가해야 한다. 몇몇 언어는 별도 패키지를 설치하지 않고도 사용할 수
있는 것 같다. 예를 들어 C, Perl, Sqlite 등은 관련 패키지를 설치하지
않았지만 사용할 수 있다. Restclient는 여기에 추가하지 않아도 실행하는
데 문제가 없는 것 같다.
;; init.el
(custom-set-variables
;;...
'(org-babel-load-languages
'((emacs-lisp . t)
(perl . t)
(python . t)
(rust . t)
(js . t)
(C . t)
(clojure . t)
(sqlite . t)))
;;...
테스트
다음과 같이 org-test.org
파일을 만들고 다음과 같이 Emacs List, JavaScript, C, Perl, Python, Rust, Restclient, Sqlite를 테스트해 보았다.
#+STARTUP: showall indent
#+TITLE: Org-Notebook
* Org-notebook
Press `C-c C-c` at the beginning of the code block. The return value
of the code block will be added to =#+RESULTS:= section.
Emacs Lisp
** Emacs Lisp
#+BEGIN_SRC emacs-lisp
(mapcar #'1+ `(1 2 3 4 5))
#+END_SRC
#+RESULTS:
| 2 | 3 | 4 | 5 | 6 |
JavaScript
** JavaScript
#+BEGIN_SRC js
function square(x) {
return x * x;
}
return [1, 2, 3, 4, 5].map(x => square(x))
#+END_SRC
#+RESULTS:
| 1 | 4 | 9 | 16 | 25 |
C
** C
#+BEGIN_SRC C :includes <stdio.h>
int a = 1;
int b = 2;
printf("%d + %d = %d\n", a, b, a + b);
#+END_SRC
#+RESULTS:
: 1 + 2 = 3
Perl
** Perl
#+BEGIN_SRC perl
my $x = 1;
my $y = $x ? '1' : '0';
return "$x -> $y\n";
#+END_SRC
#+RESULTS:
: 1 -> 1
#+BEGIN_SRC perl
use DateTime;
return DateTime->now;
#+END_SRC
#+RESULTS:
: 2025-05-13T21:04:35
Python
** Python
#+BEGIN_SRC python
ls = range(50)
def partition(ls, n):
for i in range(0, len(ls), n):
yield list(ls[i: i + n])
return list(partition(ls, 5))
#+END_SRC
#+RESULTS:
| 0 | 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 |
Rust
** Rust
#+name: Rust test
#+BEGIN_SRC rust
for i in 1..10 {
println!("{i} * {i} = {:2}", i * i);
}
#+END_SRC
#+RESULTS: Rust test
: 1 * 1 = 1
: 2 * 2 = 4
: 3 * 3 = 9
: 4 * 4 = 16
: 5 * 5 = 25
: 6 * 6 = 36
: 7 * 7 = 49
: 8 * 8 = 64
: 9 * 9 = 81
Restclient
** Restclient
#+BEGIN_SRC restclient
GET https://httpbin.org/get?a=100&b=200
Accept-Charset: utf-8
#+END_SRC
#+RESULTS:
#+BEGIN_SRC js
{
"args": {
"a": "100",
"b": "200"
},
"headers": {
"Accept": "*/*",
"Accept-Charset": "utf-8",
"Content-Length": "0",
"Host": "httpbin.org",
"Mime-Version": "1.0",
"X-Amzn-Trace-Id": "Root=1-68055014-4a42df2777408571271f1a1d"
},
"origin": "149.86.63.230",
"url": "https://httpbin.org/get?a=100&b=200"
}
// GET https://httpbin.org/get?a=100&b=200
// HTTP/1.1 200 OK
// Date: Sun, 20 Apr 2025 19:50:44 GMT
// Content-Type: application/json
// Content-Length: 356
// Connection: keep-alive
// Server: gunicorn/19.9.0
// Access-Control-Allow-Origin: *
// Access-Control-Allow-Credentials: true
// Request duration: 0.110329s
#+END_SRC
Sqlite
#+header: :dir ~/temp/
#+header: :db test.db
#+header: :columns yes
#+BEGIN_SRC sqlite
select value*10
from generate_series(1, 10);
#+END_SRC
#+RESULTS:
| 10 |
| 20 |
| 30 |
| 40 |
| 50 |
| 60 |
| 70 |
| 80 |
| 90 |
| 100 |
결론
Org-mode 문서에서 몇몇 언어를 테스트해보았다. C나 Rust와 같이 REPL이 없는 언어에서 코드가 어떤 식으로 동작하는 지 확인해보려면, 보통 간단한 테스트 프로그램을 작성해 컴파일하고 실행하는 번거로운 절차를 거쳐야 한다. Emacs에서는 org-babel을 사용해 org-mode 문서에서 코드 조각을 좀더 쉽게 테스트해볼 수 있고, 테스트 목적과 결과를 쉽게 문서화할 수 있다.
REPL이나 인터렉티브 셸을 지원하는 언어라면 REPL에서 코드 조각을 쉽게 테스트할 수 있지만, 테스트 목적과 결과 등을 함께 정리하는 것은 또 다른 얘기다. org-mode에서는 org 문서 안에 코드를 실행할 수 있으므로 이런 작업이 쉽다. 실행 결과와 설명을 함께 정리하고 싶은 경우 유용하다. 새로운 언어를 배울 때 org-mode로 정리하면 좋을 것 같다.