히스토리

  • [2025-10-24 Fri 09:02] 프로젝트 관리가 연결 하나로
  • [2025-10-21 Tue 10:53] 초안 작성, 기존 문서 변환

관련노트

프로젝트 Denote 문서 Org-mode 전환 및 관리 전략

개요

프로젝트별 문서 관리를 위해 Denote 체계를 활용하면서, Markdown 대신 Org-mode로 전환하는 전략을 수립했다. 이는 다음과 같은 이점을 제공한다:

  1. Emacs 워크플로우 통합: Org-mode의 강력한 기능 활용
  2. Literate Programming: 스크립트와 문서의 통합 관리
  3. 재현성 확보: 문서 내 코드 블록 직접 실행 및 결과 기록
  4. 협업 지원: 필요시 특정 헤딩만 Markdown으로 export
  5. AI 이해 향상: 명확한 구조로 에이전트의 문서 파악 용이

핵심 전략

Denote Silo 구조

  • ~/org/ : 사용자 지식베이스 (전역 silo)
  • ~/claude-memory/ : AI 메모리 시스템 (AI silo)
  • 프로젝트/docs/ : 프로젝트별 로컬 silo (예: sks-gateway-forked/docs/)

파일명 규칙

timestamp--한글-제목__영어_태그들.org
예: 20251021T105353--프로젝트-denote-문서-org-mode-전환__llmlog_denote_orgmode.org

Org-mode Frontmatter

#+title:      제목
#+date:       [2025-10-21 Mon 10:53]
#+filetags:   :tag1:tag2:tag3:
#+identifier: 20251021T105353

변환 스크립트 (Literate Programming)

최종 안전 변환 스크립트

이 스크립트는 다음 문제들을 해결한다:

  • YAML frontmatter 파싱 오류 (문서 중간의 --- 구분자)
  • YAML alias/token 파싱 문제
  • Denote 파일명에서 메타데이터 추출
  • 날짜 형식 자동 변환

핵심: --from markdown-yaml_metadata_block 옵션으로 YAML 파싱 비활성화

# 안전한 Markdown → Org-mode 변환 스크립트
# claude-memory의 다양한 형식 지원
 
MD_FILE="$1"
ORG_FILE="${MD_FILE%.md}.org"
 
if [ ! -f "$MD_FILE" ]; then
    echo "❌ 파일 없음: $MD_FILE"
    exit 1
fi
 
# 파일명에서 denote 정보 추출
BASENAME=$(basename "$MD_FILE" .md)
TIMESTAMP=$(echo "$BASENAME" | grep -oP '^\d{8}T\d{6}')
TITLE=$(echo "$BASENAME" | sed -E 's/^[0-9]{8}T[0-9]{6}--//; s/__.*//; s/-/ /g')
TAGS=$(echo "$BASENAME" | grep -oP '__\K.*' | tr '_' ':')
 
echo -n "🔄 $BASENAME ... "
 
# YAML frontmatter 확인 및 추출
HAS_YAML=false
if head -n 1 "$MD_FILE" | grep -q '^---$'; then
    HAS_YAML=true
    YAML_TITLE=$(sed -n '/^---$/,/^---$/p' "$MD_FILE" | grep '^title:' | sed 's/^title: *//;s/^"//;s/"$//')
    YAML_DATE=$(sed -n '/^---$/,/^---$/p' "$MD_FILE" | grep '^date:' | sed 's/^date: *//')
    YAML_TAGS=$(sed -n '/^---$/,/^---$/p' "$MD_FILE" | grep '^tags:' | sed 's/^tags: *//;s/\[//;s/\]//;s/"//g;s/, */:/g')
    YAML_ID=$(sed -n '/^---$/,/^---$/p' "$MD_FILE" | grep '^identifier:' | sed 's/^identifier: *//')
fi
 
# pandoc 변환 (YAML metadata 비활성화)
if pandoc --from markdown-yaml_metadata_block -t org --wrap=none "$MD_FILE" -o "$ORG_FILE" 2>/dev/null; then
    # 날짜 생성
    if [ -n "$YAML_DATE" ]; then
        DATE_ORG=$(date -d "$YAML_DATE" '+[%Y-%m-%d %a %H:%M]' 2>/dev/null || echo "$YAML_DATE")
    elif [ -n "$TIMESTAMP" ]; then
        YEAR=${TIMESTAMP:0:4}
        MONTH=${TIMESTAMP:4:2}
        DAY=${TIMESTAMP:6:2}
        HOUR=${TIMESTAMP:9:2}
        MIN=${TIMESTAMP:11:2}
        DATE_ORG=$(date -d "${YEAR}-${MONTH}-${DAY} ${HOUR}:${MIN}" '+[%Y-%m-%d %a %H:%M]' 2>/dev/null || echo "[${YEAR}-${MONTH}-${DAY} Mon ${HOUR}:${MIN}]")
    else
        DATE_ORG=$(date -r "$MD_FILE" '+[%Y-%m-%d %a %H:%M]')
    fi
 
    # 제목 결정
    if [ -n "$YAML_TITLE" ]; then
        FINAL_TITLE="$YAML_TITLE"
    elif [ -n "$TITLE" ]; then
        FINAL_TITLE="$TITLE"
    else
        FINAL_TITLE=$(grep -m1 '^# ' "$MD_FILE" | sed 's/^# *//' || echo "Untitled")
    fi
 
    FINAL_TAGS="${YAML_TAGS:-$TAGS}"
    FINAL_ID="${YAML_ID:-$TIMESTAMP}"
 
    # frontmatter 추가
    {
        echo "#+title:      $FINAL_TITLE"
        echo "#+date:       $DATE_ORG"
        [ -n "$FINAL_TAGS" ] && echo "#+filetags:   :$FINAL_TAGS:"
        [ -n "$FINAL_ID" ] && echo "#+identifier: $FINAL_ID"
        echo ""
        cat "$ORG_FILE"
    } > "${ORG_FILE}.tmp"
    mv "${ORG_FILE}.tmp" "$ORG_FILE"
 
    if [ "$HAS_YAML" = true ]; then
        echo "✓ (YAML)"
    else
        echo "✓"
    fi
    exit 0
else
    echo "❌ 변환 실패"
    exit 1
fi

배치 변환 스크립트

프로젝트 디렉토리의 모든 Denote Markdown 파일을 변환한다.

# 프로젝트 docs/ 디렉토리의 Denote Markdown 파일 일괄 변환
 
DOCS_DIR="${1:-.}"
SCRIPT="/tmp/convert_denote_md_to_org.sh"
 
if [ ! -f "$SCRIPT" ]; then
    echo "❌ 변환 스크립트를 찾을 수 없습니다: $SCRIPT"
    exit 1
fi
 
cd "$DOCS_DIR" || exit 1
 
echo "📂 디렉토리: $DOCS_DIR"
echo "🔍 Denote 형식 .md 파일 검색 중..."
 
COUNT=0
while IFS= read -r md_file; do
    "$SCRIPT" "$md_file" && ((COUNT++))
done < <(ls -1 *.md 2>/dev/null | grep -E '^[0-9]{8}T[0-9]{6}--')
 
echo ""
echo "✅ 변환 완료: $COUNT 개 파일"
echo ""
echo "🗑️  원본 .md 파일 삭제하시겠습니까? (y/N)"
read -r response
if [[ "$response" =~ ^[Yy]$ ]]; then
    ls -1 *.md 2>/dev/null | grep -E '^[0-9]{8}T[0-9]{6}--' | xargs rm -v
    echo "✅ 원본 파일 삭제 완료"
fi

.dir-locals.el 설정

프로젝트 루트에 .dir-locals.el 파일을 생성하여 Denote를 로컬 silo로 설정한다.

((org-mode . (
              ;; Denote 디렉토리를 프로젝트 docs/로 설정
              (denote-directory . "/절대/경로/프로젝트/docs/")
 
              ;; 프로젝트 특화 키워드 (자동완성용)
              (denote-known-keywords . ("solution" "test" "analysis"
                                       "implementation" "debugging"))
 
              ;; 기타 Org-mode 설정
              (eval . (eldoc-mode -1))
              (eval . (org-indent-mode 1))
              )))

사용 예시

SKS Gateway 프로젝트 적용 사례

  1. 프로젝트 구조:

    sks-gateway-forked/
    ├── .dir-locals.el           # Denote silo 설정
    ├── CLAUDE.md                # AI 가이드 (Markdown 유지)
    └── docs/
        ├── 00_INDEX.md          # 인덱스 (Markdown 유지)
        └── 20251001T*.org       # Denote 문서 (Org-mode)
  2. 변환 실행:

    cd ~/repos/work/sks-gateway-forked/docs
    /tmp/batch_convert_docs.sh .
  3. 태그 분석 및 키워드 설정:

    ls *.md | grep -oP '__\K[^.]+' | tr '_' '\n' | \
      sort | uniq -c | sort -rn | head -10

Claude Memory 변환 전략

~/claude-memory/ 디렉토리의 파일 변환 시 YAML frontmatter 파싱 오류가 발생할 수 있다.

오류 유형

  1. YAML alias 오류: & 또는 * 문자 사용
  2. Token 파싱 오류: 특수 문자나 들여쓰기 문제
  3. 다중 라인 값: | 또는 > 사용

안전한 변환 절차

  1. 전체 파일 목록 확인
  2. 문제 파일 개별 분석
  3. Frontmatter 수정 또는 제거 후 변환
  4. 변환 검증

협업 전략

Org-mode 비사용자와의 협업

특정 섹션만 Markdown으로 export:

;; 현재 서브트리를 Markdown으로 export
(org-md-export-to-markdown nil t)
 
;; 또는 특정 태그가 있는 헤딩만
(org-map-entries
 (lambda () (org-md-export-to-markdown nil t))
 "+EXPORT")

Memex Knowledge Base 통합

이 전략은 /home/goqual/sync/emacs/memex-kb/ 프로젝트의 일부로서:

  • 재현성 확보: Literate programming으로 스크립트와 문서 통합
  • 지식 그래프: Denote 링크로 문서 간 관계 형성
  • 다중 silo 관리: 프로젝트/개인/AI 메모리 분리

실행 결과

SKS Gateway 프로젝트 (2025-10-21)

📂 디렉토리: ~/repos/work/sks-gateway-forked/docs/
✅ 변환 완료: 40개 파일
  - Denote 형식 .md → .org
  - YAML frontmatter → Org properties
  - 원본 .md 파일 삭제

Claude Memory 시스템 (2025-10-21)

📂 디렉토리: ~/claude-memory/
🔍 총 99개 .md 파일 발견
 
변환 결과:
  - ✓ (YAML): 26개 (YAML frontmatter 있음)
  - ✓: 73개 (파일명에서 메타데이터 추출)
  - 실패: 0개
 
📊 최종 결과:
  - .org 파일: 100개
  - .md 파일: 0개
  - 변환 시간: ~2분

핵심 해결책: --from markdown-yaml_metadata_block 옵션으로 문서 중간의 --- 구분자로 인한 YAML 파싱 오류 해결

다음 단계

  1. claude-memory/ 전체 파일 org 변환 (완료)
  2. SKS Gateway 프로젝트 문서 org 변환 (완료)
  3. Memex KB README 업데이트
  4. 프로젝트별 denote 워크플로우 표준화 문서 작성
  5. AI 에이전트의 literate programming 활용 능력 향상
  6. 다른 프로젝트들에 .dir-locals.el 적용

참고 자료

변경 이력

  • [2025-10-21 Tue 10:53] 초안 작성
  • [2025-10-21 Tue 11:10] 최종 스크립트 업데이트 및 실행 결과 추가
    • --from markdown-yaml_metadata_block 옵션으로 YAML 파싱 오류 해결
    • SKS Gateway 40개, claude-memory 99개 파일 변환 완료