Emacs Lisp으로 Org 문서에 항목 추가하기 (삽질기)

Emacs, Emacs Lisp // 2025년 09월 25일 작성

Emacs로 글을 쓰는 체제로 다시 돌아온 후 여러 불편한 사항들을 고쳐가고 있다. 그 중 하나가 임시 글 목록을 관리하는 것인데 수동으로 관리하다 너무 귀찮음을 느낀 나머지 이를 자동화하고 싶어졌다.

이 글은 이와 관련된 팁...이라기 보다는 삽질기에 가까운 영양가가 별로 없는 글이지만 어쨌든 정리해서 포스팅한다.

요구사항

개인적으로 글을 쓸 때 임시 파일을 만들어 두고 여기에 마크다운 방식으로 글을 작성한다. 그런데 간혹 순서대로 글을 올려야 할 때가 있는데 이럴 때 파일명만 보고 순서를 구분하는 건 굉장히 곤혹스러울 때가 많았다.

그래서 이후 홈 디렉터리의 todo.org라는 파일에 TODO 항목으로 해당 임시 파일 경로를 기록해두는 식으로 순서를 정리하기로 했다. 정확히는 ~/todo.org 파일 내용 중 Writes라는 헤드라인 내용의 끝에 리스트 형식으로 TODO 태그를 달고 파일 경로를 계속 덧붙이는 방식이었다. 이런 식으로 말이다.

* Writes
- TODO [[file:/foo/bar/doc1.md]]
- TODO [[file:/foo/bar/doc2.md]]

* Another Items
...

이렇게 하면 글을 올릴 순서도 정리할 수 있고 여기서 바로 편집할 파일을 여는 것도 가능했기 때문에 여러 면에서 편리했다.

하지만 이 파일을 수동으로 관리하는 방식 자체는 너무 귀찮았다. 글을 추가할 때마다 이 파일을 직접 고쳐야 했으니 말이다.

그래서 이를 자동화하는 방법이 필요했다. 즉 이 ~/todo.org 파일을 열고 TODO와 파일 경로를 자동으로 덧붙이는 기능을 구현하는 것이 목적이다.

다만 이 ~/todo.org 파일에는 글쓰기 뿐만 아니라 다른 용도의 항목들도 기재되어 있어서 특정 헤드라인을 찾아서 삽입되는 방식으로 동작해야만 한다.

자 그렇다면 이런 요구조건을 모두 만족하는 방식으로 구현이 가능할까? 물론 어떻게든 가능은 하겠지만 좀 더 유연한 방식으로 구견하고 싶었다는 점이 덧붙어야 할 것 같다.

그래서 벌였던 삽질들

이 기능을 가급적 유연하게 동작했으면 했기에 우선은 Org Mode에서 제공하는 기능을 최대한 활용해 보고 싶었다.

그리하여 탐색을 해보다 처음으로 알아냈던 방법은 org-goto-heading을 이용해 특정 헤드라인으로 컨텍스트 포인트를 이동할 수 있고 이후 org-end-of-subtree를 이용해 헤드라인 항목의 끝으로 이동한 후 insert 함수로 필요한 내용을 추가하면 된다는 식이었다.

다만 이 방법에는 큰 문제가 있었으니 아래와 같은 오류가 난다는 점이다.

Symbol’s function definition is void: org-goto-heading

이 핵심적인 함수가 없다고 한다. 하하. 뭐야....

물론 (require 'org) 등으로 모듈을 불러와도 의미는 없었다. 거기다 Org Mode 패키지가 거의 최신 버전에 근접한 버전이기도 했기에 딱히 다른 문제가 있을 가능성은 적었다.

사실 이 함수가 내부(internal) 전용이라 안 보이는 건지 아니면 원래부터 없었던 건지 혹은 있었는데 사라진 건지 아직도 모르겠다. 관련된 정보를 찾을 수 없어서 말이다.

이후 삽질을 하다 위 방법은 집어 치우고 다른 방법을 찾아보다 또다른 방법이 있다는 것을 알아냈었다. 바로 org-goto-heading 대신 org-refile을 사용할 수 있다는 내용이었다. 정확히는 org-with-point-at 함수와 org-find-refile-target 함수를 이용해 특정 헤드라인으로 이동할 수 있다는 내용이었다.

하지만 이 힌트도 별 의미는 없었다.

Symbol’s function definition is void: org-find-refile-target

이 함수도 없다고 한다. 하하. 하하하.... 왜....

역시 이번에도 내부 함수인지 원래 없던 건지 아니면 사라진 건지 알 수는 없었다.

이후 약간의 삽질을 더 하긴 했지만 결국 Org Mode의 기능을 쓰는 건 포기하기로 했다. 좀 지치기도 했고 생각한 대로 일반 버퍼 편집 방법으로도 대충 해도 동작할 것 같았기 때문이다.

결국 어거지로 맞추긴 했다

그리하여 Org Mode에서 제공되는 함수들은 다 집어치우고 일반적으로 사용되는 Emacs의 함수들을 이용해 완성된 코드는 이런 식이다.

(defun insert-current-path-to-todo-org-file ()
  (interactive)
  (let ((current-file (buffer-file-name)))
    (when current-file
      (with-current-buffer (find-file-noselect "~/todo.org")
        (goto-char (point-min))
        (re-search-forward "^\\* Writes$")
        (re-search-forward "^[[:blank:]]*$")
        (beginning-of-line)
        (insert (format "** TODO [[file:%s]]\n" current-file))
        (save-buffer)))))

보다시피 굉장히 직관적인 코드다. 우선은 ~/todo.org 파일을 열고 내용의 시작점으로 이동한 뒤 정규표현식으로 헤드라인 부분을 찾는다. 이후 해당 헤드라인 내용의 끝을 찾기 위해 빈(blank) 줄을 찾는다. 그리고 여기에다 현재 버퍼의 경로를 이용해 TODO 내용을 추가하고 저장하는 것으로 동작은 완료된다.

오류 처리가 좀 부실해서 너무나 제한적인 환경에서만 사용 가능한 코드일 것 같지만 어쨌든 동작한다는 것 자체가 중요한 것 같다.

실제 환경의 Emacs에서 Org Agenda를 통해 TODO를 살펴본 모습 실제 환경의 Emacs에서 Org Agenda를 통해 TODO를 살펴본 모습

실제로 사용 중인 코드는 위의 코드에서 경로나 검색하는 헤드라인 이름 정도의 차이가 있을 뿐 거의 그대로 사용 중인데 대충 만든 코드임에도 아직도 잘 돌아가고 있다. 다만 임시 파일을 생성한 뒤 위 기능을 호출하는 함수를 별도로 하나 만들고 단축키를 배정해 뒀다는 점은 이 글에서 생략되어 있긴 하지만 말이다.

기회가 된다면 좀 더 유연하게 동작하게 만들고 싶지만 당장은 귀찮아서 더는 못 하겠다. 지속된 삽질은 정신적으로 많이 피곤하니 어쩔 수가 없다.

Seorenn (Konrad Seo)
개발자 주제에 경제나 먹거리 관련 글을 주로 쓰는 사람