히스토리

  • [2026-06-08 Mon] Understanding 패스 — 호출 그래프/4분류 고정 (헤딩1 추가). 정리 작업 전 손대지 않고 지도부터.
  • [2026-06-08 Mon 10:35] 업데이트하자
  • [2025-12-21 Sun 12:00] 생성

관련노트

메타노트

[2025-12-21 Sun 12:00] Denote 노트를 Hugo로 내보내는 통합 시스템

Overview

Denote 노트를 Hugo로 내보내는 통합 시스템. dblock 업데이트와 export를 하나의 워크플로우로 관리.

주요 기능

  • 통합 관리: dblock 갱신 + Hugo export를 단일 스크립트로
  • Daemon 기반: 패키지 로딩 1회, 18x 성능 향상
  • Unicode 완벽 지원: NBSP(U+00A0), 한글, 특수문자 안전 처리
  • Bibliography 통합: Citar/org-cite로 인용 링크 자동 생성
  • 메모리 관리: 주기적 GC로 2000+ 파일 안정 처리

성능

작업파일 수시간속도
dblock (순차)530~11초50 files/sec
export (병렬 2 daemon)1,976~33분1.0 files/sec
export (병렬 4 daemon)1,976~18분1.8 files/sec

배포 파이프라인

┌─────────────────────────────────────────────────────────────┐
│                    Digital Garden Pipeline                   │
├─────────────────────────────────────────────────────────────┤
│  1. dblock 갱신     ~/org/meta → denote-links 업데이트      │
│         ↓                                                    │
│  2. Hugo export     ~/org/{meta,bib,notes} → markdown       │
│         ↓                                                    │
│  3. 후처리          clean-run.sh (secretlint, sed)          │
│         ↓                                                    │
│  4. 배포            Quartz build → GitHub Pages              │
└─────────────────────────────────────────────────────────────┘

Architecture

파일 구조 (v2.0 - 통합)

doomemacs-config/
├── bin/
│   ├── denote-export.sh          # 통합 wrapper (dblock + export)
│   ├── denote-export-parallel.py # Python 병렬 처리
│   ├── denote-export.el          # 통합 Elisp (daemon/dblock/export)
│   │
│   ├── [DELETED] denote-export-batch.el      # obsolete
│   ├── [DELETED] denote-export-server.el     # → denote-export.el
│   ├── [DELETED] denote-dblock-batch.el      # → denote-export.el
│   └── [DELETED] denote-dblock-update.sh     # → denote-export.sh

├── lisp/
│   └── denote-export.el          # Interactive export 설정

└── docs/
    ├── 20251221T120044--denote-export-unified-system.org  # 이 문서
    ├── [LEGACY] 20251027T092900--denote-export-system.org
    └── [LEGACY] 20251110T190854--denote-dblock-update-system.org

통합 el 파일 구조

;;; bin/denote-export.el --- Unified Denote Export System
 
;;;; 1. 초기화
;; - gc-cons-threshold (most-positive-fixnum → 16MB)
;; - UI 비활성화 (daemon용)
;; - package.el 비활성화 (ELPA 방지)
 
;;;; 2. Doom 패키지 로딩
;; - find-straight-build-dir (버전 무관)
;; - load-path + autoloads 설정
 
;;;; 3. 필수 패키지 require
;; Built-in: org, ox, cl-lib, seq, json, browse-url
;; Doom: dash, denote, ox-hugo, citar, parsebib
;; Org-cite: oc, oc-basic, oc-csl
;; Denote: denote-regexp → denote-org-extras → denote-explore
 
;;;; 4. 설정 로딩
;; - +user-info.el (config-bibfiles, user-hugo-blog-dir)
;; - lisp/denote-export.el
;; - Bibliography 초기화
;; - 매크로 확장 설정
 
;;;; 5. Helper 함수들
;; - extract-denote-id-from-filename
;; - get-org-hugo-section-from-path
 
;;;; 6. Dblock 함수들
;; - denote-dblock-update-file
;; - denote-dblock-update-directory
 
;;;; 7. Export 함수들
;; - denote-export-file
;; - denote-export-batch-files
;; - denote-export-directory
 
;;;; 8. 메모리 관리
;; - denote-export-file-counter
;; - denote-export-gc-interval (50 files)
;; - gc-cons-threshold 리셋 (16MB)
 
;;;; 9. Server 시작
;; - denote-export-server-ready = t
;; - (server-start)

처리 흐름

graph TD
    A[denote-export.sh] --> B{명령어}
 
    B -->|dblock| C[싱글 데몬 시작]
    C --> D[denote-dblock-update-directory]
    D --> E[org-update-all-dblocks]
    E --> F[데몬 종료]
 
    B -->|export| G[멀티 데몬 시작 N개]
    G --> H[Python ProcessPoolExecutor]
    H --> I[Round-robin 파일 분배]
    I --> J[denote-export-file × N]
    J --> K[ox-hugo + Denote ID rename]
    K --> L[데몬 정리]
 
    B -->|all| M[dblock → export 순차]
 
    F --> N[~/repos/gh/notes/content/]
    L --> N
    N --> O[clean-run.sh 후처리]

Usage

명령어 구조

denote-export.sh <command> [options]
 
Commands:
  dblock [dir]           # dblock 업데이트 (싱글 데몬)
  export <target> [n]    # Hugo export (멀티 데몬)
  all [n]                # dblock + export 전체
 
Targets (export):
  all                    # meta, bib, notes 순차
  meta                   # ~/org/meta
  bib                    # ~/org/bib
  notes                  # ~/org/notes
  test                   # ~/org/test (빠른 검증)
  run <dir>              # 커스텀 디렉토리
 
Options:
  n                      # 데몬 개수 (기본: 2)

기본 사용법

# 전체 배포 (권장)
./bin/denote-export.sh all
 
# dblock만 갱신 (meta)
./bin/denote-export.sh dblock
./bin/denote-export.sh dblock ~/org/meta
 
# export만 (특정 디렉토리)
./bin/denote-export.sh export meta 4
./bin/denote-export.sh export notes 2
 
# 커스텀 디렉토리
./bin/denote-export.sh export run ~/custom/path 4

후처리

cd ~/repos/gh/notes/
./clean-run.sh
 
# 또는 개별 실행
./lint.sh              # secretlint 검증
./change-text.sh       # sed 치환
npx quartz build       # 정적 사이트 생성

Technical Details

패키지 로딩 (핵심)

Doom Emacs의 straight.el 패키지를 batch/daemon 모드에서 로딩하는 방법:

;; 1. ELPA 완전 비활성화 (중요!)
(setq package-enable-at-startup nil)
(setq package-archives nil)
(fset 'package-initialize #'ignore)
(fset 'package-install (lambda (&rest _)
  (error "ELPA is disabled! Use Doom's straight packages only")))
 
;; 2. straight build 디렉토리 찾기 (버전 무관)
(defun find-straight-build-dir ()
  (let ((base (expand-file-name ".local/straight/" doom-emacs-dir)))
    (when (file-directory-p base)
      (car (sort (directory-files base t "^build-")
                 #'file-newer-than-file-p)))))
 
;; 3. 패키지 디렉토리 + autoloads 로딩
(let ((build-dir (find-straight-build-dir)))
  (dolist (pkg-dir (directory-files build-dir t "^[^.]" t))
    (add-to-list 'load-path pkg-dir)
    ;; autoloads 파일도 로딩 (중요!)
    (let ((autoload (expand-file-name
                     (concat (file-name-nondirectory pkg-dir)
                             "-autoloads.el")
                     pkg-dir)))
      (when (file-exists-p autoload)
        (load autoload nil t)))))

필수 패키지 목록

카테고리패키지용도
Built-inorg, oxOrg-mode export
Built-incl-lib, seq리스트 처리
Built-injson, browse-url유틸리티
Doomdash함수형 유틸
DoomdenoteDenote 코어
Doomox-hugoHugo 변환
Doomcitar, parsebib서지 관리
Org-citeoc, oc-basic, oc-csl인용 처리
Denotedenote-regexp정규식 (의존성)
Denotedenote-org-extrasdblock 함수
Denotedenote-explore매크로

dblock vs Export 차이

항목dblockexport
파일 의존성상호 참조 (스캔 필요)독립적
병목I/O (디렉토리 스캔)CPU (마크다운 변환)
병렬 효과오히려 느려짐4-8배 향상
권장 방식순차 (싱글 데몬)병렬 (멀티 데몬)
처리 속도50 files/sec1-2 files/sec

Unicode 안전 처리

파일명에 NBSP(U+00A0) 등 특수문자가 있을 때 쉘 quoting 문제 발생:

# ❌ 쉘 명령어로 파일 전달 시 깨짐
subprocess.run(['emacs', '--eval', f'(export "{filepath}")'])
 
# ✅ Python에서 파일 목록 생성, Elisp에서 직접 처리
org_files = sorted(directory.glob("*.org"))  # Python 유니코드 완벽 지원
for file in org_files:
    # emacsclient로 파일 경로 전달
    elisp_cmd = f'(denote-export-file "{file}")'

메모리 관리

2000+ 파일 처리 시 메모리 누수 방지:

;; 1. 초기화 시 GC 비활성화 (빠른 로딩)
(setq gc-cons-threshold most-positive-fixnum)
 
;; 2. 초기화 완료 후 GC 리셋 (16MB)
(setq gc-cons-threshold (* 16 1024 1024))
(garbage-collect)
 
;; 3. 주기적 GC (50 파일마다)
(defvar denote-export-gc-interval 50)
(defvar denote-export-file-counter 0)
 
(defun denote-export-file (file)
  (setq denote-export-file-counter (1+ denote-export-file-counter))
  (when (zerop (mod denote-export-file-counter denote-export-gc-interval))
    (garbage-collect))
 
  ;; 4. 버퍼 정리 (unwind-protect)
  (let ((buf (find-file-noselect file)))
    (unwind-protect
        (with-current-buffer buf
          ;; export 로직
          )
      ;; 항상 버퍼 정리
      (when (buffer-live-p buf)
        (kill-buffer buf)))))

데몬 라이프사이클

# 시작
subprocess.Popen(['emacs', '--quick',
                  f'--daemon={daemon_name}',
                  '--load', 'denote-export.el'])
 
# 준비 대기 (ready flag 체크)
while not ready:
    result = subprocess.run(
        ['emacsclient', '-s', daemon_name,
         '--eval', "(boundp 'denote-export-server-ready)"])
    if 't' in result.stdout:
        ready = True
 
# 종료
subprocess.run(['emacsclient', '-s', daemon_name,
                '--eval', '(kill-emacs)'])
 
# 강제 정리 (interrupt 시)
subprocess.run(['pkill', '-f', 'denote-export-daemon'])

Configuration

.dir-locals.el 설정

;; ~/org/meta/.dir-locals.el
((org-mode . ((org-hugo-section . "meta")
              (org-hugo-base-dir . "~/repos/gh/notes/"))))
 
;; ~/org/notes/.dir-locals.el
((org-mode . ((org-hugo-section . "notes")
              (org-hugo-base-dir . "~/repos/gh/notes/"))))
 
;; ~/org/bib/.dir-locals.el
((org-mode . ((org-hugo-section . "bib")
              (org-hugo-base-dir . "~/repos/gh/notes/"))))

+user-info.el 설정

;; Bibliography 파일 경로
(defvar config-bibfiles
  '("~/org/bib/references.bib"
    "~/org/bib/books.bib"))
 
;; Hugo 블로그 디렉토리
(defvar user-hugo-blog-dir
  (expand-file-name "~/repos/gh/notes/"))
 
;; Org 디렉토리
(defvar user-org-directory
  (expand-file-name "~/org/"))

데몬 개수 권장값

시스템 메모리권장 데몬 수비고
8GB2기본값
16GB4안정적
32GB+8최대 성능

Troubleshooting

데몬 좀비 프로세스

# 상태 확인
ps aux | grep denote-export-daemon
 
# 강제 종료
pkill -f "denote-export-daemon"
 
# 소켓 정리
rm -f /tmp/emacs$(id -u)/denote-export-daemon-*

패키지 로딩 실패

# 에러: "dash not found in Doom"
# 해결: Doom 패키지 재빌드
doom sync
doom build

dblock 업데이트 에러

에러: "Error during update of dynamic block"
 
원인:
- denote-org-extras 미로딩 (seq 누락 등)
- denote-directory 미설정
 
해결:
1. denote-export.el에서 (require 'seq) 확인
2. (setq denote-directory "~/org/") 확인

Export 실패 (개별 파일)

;; 테스트
(find-file "~/org/notes/test.org")
(org-hugo-export-to-md)
 
;; 로그 확인
(switch-to-buffer "*Messages*")

Ctrl+C 인터럽트 후 정리

# Python 스크립트는 signal handler로 자동 정리
# 만약 남아있다면:
pkill -f "denote-export-daemon"

Migration from Legacy

기존 파일 매핑

기존 파일새 파일비고
denote-export-server.eldenote-export.elrename + 통합
denote-dblock-batch.eldenote-export.el함수 통합
denote-export-batch.el(삭제)obsolete
denote-dblock-update.shdenote-export.shdblock 명령 통합

명령어 매핑

기존 명령새 명령
./denote-dblock-update.sh ~/org/meta./denote-export.sh dblock
./denote-export.sh meta 4./denote-export.sh export meta 4
(수동 순차)./denote-export.sh all

Performance History

v1.0 → v2.0 개선

버전방식1962 파일 시간속도
v1.0Batch 순차~10시간0.05 files/sec
v1.3Daemon 순차~2.5시간0.22 files/sec
v1.4Daemon 병렬(4)~18분1.8 files/sec
v2.0통합 + 최적화~15분 (예상)2.0+ files/sec

개선 포인트 (v2.0)

  • 패키지 로딩 통합 (dblock + export 동일 환경)
  • seq 패키지 누락 수정
  • 데몬 라이프사이클 통합 관리
  • 메모리 관리 일원화

Changelog

[2025-12-21 Sun] v2.0.0 - Unified System

주요 변경
  • denote-export.el 통합 (server + dblock)
  • denote-export.sh에 dblock 명령 추가
  • obsolete 파일 삭제 (batch.el, dblock-update.sh)
  • 패키지 로딩 버그 수정 (seq 누락)
파일 구조 개선
  • 8개 → 4개 파일로 단순화
  • 단일 el 파일로 관리 용이성 향상
  • 문서 통합

[2025-12-17 Wed] v1.5.x - Memory Management

  • gc-cons-threshold 리셋 (16MB)
  • 주기적 GC (50 파일마다)
  • 기본 데몬 4 → 2개로 감소 (안정성)

[2025-10-29 Wed] v1.4.0 - Production Ready

  • server-start 누락 해결
  • Unicode 파일명 완벽 지원
  • 1961 파일 100% 성공률

[2025-10-28 Tue] v1.3.0 - Server Mode

  • Daemon 방식 도입 (18x 성능 향상)
  • 파일명 기반 Denote ID 추출
  • 디렉토리 기반 Section 자동 결정

References

Legacy Documents

이 문서는 다음 문서들을 통합/대체합니다:

  • [Denote Export System (Legacy)]
  • [Denote dblock Update System (Legacy)]

[2026-06-08 Mon] export 파이프라인 정리 패스 — Understanding 고정

이 시스템은 가든 export의 핵심이고, “잘 동작하지만 깨지기 쉬운” 로직이다. ox-hugo / org / denote가 바뀌면 터질 수 있다. 그래서 정리(리팩터/제거)에 들어가기 전, 손대지 않고 *호출 그래프와 분류부터 고정*한다. 이 패스는 read-only로 코드를 실측해 작성했다 (Claude Understanding + GPT 교차검토).

검증된 LIVE 호출 그래프

자동 export의 실제 경로는 run.sh 중심이다 (아래 Architecture 섹션의 옛 denote-export.sh 중심 서술은 stale — 이 헤딩 마지막 참조).

run.sh export
  └─ cmd_export_all()                       # 폴더 루프: meta bib notes botlog
       └─ python3 bin/denote-export-parallel.py export <dir> <jobs> [--force]
            ├─ emacs --quick --daemon=denote-export-daemon-N --load bin/denote-export.el
            ├─ (boundp 'denote-export-server-ready) 폴링 대기
            └─ emacsclient --eval 로 export 잡 디스패치
                 └─ bin/denote-export.el (데몬 서버)
                      └─ (load "lisp/denote-export-config.el")   # 핵심 로직 1045줄
  └─ trap cleanup: pkill -f denote-export-daemon                 # cleanup은 run.sh가 담당

N개 배치 프로세스가 아니라 persistent emacs 데몬에 emacsclient로 잡을 밀어넣는 구조. cleanup 책임은 run.sh의 trap에 있다.

4분류 — 정리 대상 식별

분류대상근거 ()
자동경로 (LIVE)run.sh, bin/denote-export-parallel.py, bin/denote-export.el, lisp/denote-export-config.el 대부분위 호출 그래프
manual legacy / 자동경로 고아bin/denote-export.shself-doc “py를 쉽게 실행하는 wrapper”. run.sh는 py를 직접 호출. trap도 run.sh가 중복 보유. repo에서 유일 참조처는 tests/test_bash_cleanup.sh
stale reference (LIVE 파일 안)my/update-dblock-export-garden-all-paralleldenote-export-config.el:970 이 존재하지 않는 bin/denote-export-parallel.sh 를 찾음. interactive M-x surface지만 실행 시 sequential export로 fallback. 옛 .sh 병렬 모델 잔재
Tier C (test)tests/test-denote-export.eldead-path ../+denote-export.el 로드 시도 → 실제 함수 my/denote-link-ol-exportdenote-export-config.el:461. 그 파일은 top-level에서 (require 'ox-hugo) 하므로 emacs -Q 로 못 띄움 → 핵심 link-export 테스트 5개 SKIP

manual legacy 처리 분기 (바로 삭제 아님): GLG가 손으로 bin/denote-export.sh 를 돌리는가? → 안 쓰면 deprecated 표기 후 제거 후보 (+ test_bash_cleanup.sh). 가끔 쓰면 run.sh export 로 위임하는 얇은 wrapper로 축소.

Tier C 처리 분기: emacs -Q 에 ox-hugo를 억지로 끌어오면 “Tier A = vanilla” 불변식이 깨진다. → (1) broken-link fallback 순수 분기를 작은 helper로 추출해 Tier A 테스트, 또는 (2) ox-hugo/denote/straight를 로드하는 별도 integration runner(Tier C)에서 in-place 테스트. 목표는 SKIP 0개가 아니라 SKIP 의미의 정확한 분류.

이 문서(가이드) 자체의 stale 지점

위 Architecture 섹션(v2.0)은 현재 run.sh 중심 파이프라인 이전 시점이라 다음이 어긋난다 — 정리 시 같이 갱신할 후보:

  • bin/denote-export.sh 를 통합 진입점으로 서술 (실제 진입점은 run.sh export).
  • 파일명 lisp/denote-export.el 로 표기 — 실제는 lisp/denote-export-config.el.
  • 노트 위치를 docs/ 로 표기 — 실제는 Denote ~/org/notes 코퍼스.
  • 처리 흐름 mermaid가 denote-export.sh 진입으로 시작, run.sh 미언급.

다음 작업

작업 항목은 doomemacs-config/NEXT.md 에 나열한다. 이 헤딩은 “왜/ 무엇을”의 지도이고, NEXT는 “다음 한 걸음”의 체크리스트다. 핵심 원칙: 리팩터 전 characterization test 선행, 자동경로 로직은 동작 보존, 분류부터 문서로 고정.