[개발기] 검색한 대화를 마크다운으로 꺼내야 읽지 — Obsidian Vault 위키화 편

27.255.***.***
9

주의 위키화는 돈이 많이 듭니다. 토큰을 쪽쪽 빨아먹습니다. 만약 본인이 부유하지 않다면 대화만 복기하시오. 위키화를 시도하지 마시오.

이 글은 seCall — AI 에이전트 대화 세션을 수집·검색하는 CLI 도구의 바이브코딩 테스트 개발기입니다. 세부적인 코드는 모르고 알고 싶지도 않습니다. 알아서 좋을거 하나도 없습니다. 코드가 궁금하면 만든 분(?)에게 물어보세요. (저보다 빠르고, 잘 만듭니다) 기술 선택의 이유와 흐름만 다룹니다.

시리즈:

  • #1: SQLite FTS5 + 한국어 형태소 분석

  • #2: 로컬 벡터 검색

  • #3: 하이브리드 검색 + RRF

  • #4: Obsidian Vault 위키화 ← 지금 이 글 (마지막)


검색은 되는데, 그래서 뭐

3편까지 해서 검색은 꽤 잘 됩니다. 한국어도 되고, 영어도 되고, "비엠25"라고 쳐도 "BM25" 대화가 나옵니다.

근데 검색 결과가 터미널에 텍스트로 쭉 나오면... 솔직히 안 읽게 됩니다. 대화 하나가 수백 턴인 경우도 있는데, 터미널에서 스크롤 올려가며 읽을 사람이 있을까요(뜨끔).

결국 대화를 파일로 꺼내서 읽을 수 있는 형태로 만들어야 합니다. 그것도 그냥 텍스트 파일이 아니라, 대화끼리 연결되고, 메타데이터로 필터링도 되는 형태로요.

바로 wiki.js에 연결할까 해보다가 설치하고 새해만 되면 왜인지 3페이지 겨우 쓰고 쳐박아 둔 Obsidian이 떠올랐습니다.(엘라스틴인가 국카스텐인가하는 그 방법은 저처럼 게으른 사람에겐 맞지 않습니다. first brain이 있어야 secend brain도 있을 수 있습니다)


왜 Obsidian인가

Obsidian을 선택한 이유는 간단합니다:

  • 마크다운 기반 — 별도 포맷 변환 없이 .md 파일만 떨구면 됨

  • 위키(백)링크 [[]] — 대화끼리, 프로젝트끼리 연결 가능

  • Dataview 플러그인 — frontmatter 메타데이터로 SQL처럼 쿼리 가능

  • 로컬 파일 — 클라우드 종속 없음, git으로 버전 관리 가능

  • 접을 수 있는 callout — 도구 출력이나 사고 과정을 깔끔하게 숨길 수 있음

사실 제가 이미 Obsidian을 쓰고(설치만 해두고) 있었던 게 가장 큰 이유입니다. 새로운 도구를 배울 필요가 없었거든요(진짜로)


구조: Raw와 Wiki 두 층

처음엔 대화를 그냥 마크다운으로 변환해서 폴더에 넣으면 끝이라고 생각했습니다.

근데 대화 원문은 길고, 반복적이고, 구조도 제각각입니다. Claude Code 세션 하나가 200턴 넘는 경우도 있는데, 이걸 그대로 위키처럼 쓸 수는 없습니다.

그래서 두 층으로 나눴습니다:

vault/
├── raw/sessions/          ← 대화 원문 (자동 생성, 수정 금지)
│   └── 2026-04-05/
│       ├── claude-code_seCall_a1b2c3d.md
│       └── claude-code_myProject_b2c3d4e.md
├── wiki/                  ← 정리된 지식 (AI가 큐레이션)
│   ├── projects/
│   ├── topics/
│   └── decisions/
├── index.md               ← 전체 세션 목록
├── log.md                 ← 수집 로그
└── SCHEMA.md              ← 규칙 문서

raw/sessions/ 는 불변입니다. secall ingest 하면 대화가 마크다운으로 변환돼서 여기 들어가고, 이후 절대 수정하지 않습니다. 원본 보존이 목적이니까요.

wiki/ 는 AI가 여러 대화를 읽고 정리한 결과물입니다. "seCall 프로젝트에서 검색 관련해서 어떤 결정들을 내렸는지" 같은 걸 프로젝트별, 토픽별로 정리해줍니다.

이 구분이 꽤 중요합니다. raw는 기록, wiki는 지식입니다.


Frontmatter: 대화의 이력서

각 세션 마크다운에는 frontmatter가 붙습니다:

---
type: session
agent: claude-code
model: claude-opus-4-6
project: seCall
session_id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
date: 2026-04-05
start_time: "2026-04-05T14:30:00+09:00"
end_time: "2026-04-05T15:45:00+09:00"
turns: 15
tokens_in: 45000
tokens_out: 12000
tools_used: [Bash, Read, Write]
summary: "seCall 검색 기능에 RRF 알고리즘 적용..."
status: raw
---

이게 있으면 Obsidian Dataview로 이런 게 됩니다:

TABLE agent, turns, date
FROM "raw/sessions"
WHERE project = "seCall" AND turns > 10
SORT date DESC

"seCall 프로젝트에서 10턴 넘는 세션만 최신순으로 보여줘" — SQL 아닙니다. 마크다운 파일의 frontmatter를 읽어서 테이블로 보여주는 겁니다. 이게 Obsidian의 킬러 기능이에요.


Obsidian 맞춤 마크다운

그냥 마크다운이면 아무 에디터에서나 되지만, Obsidian 전용 문법을 쓰면 훨씬 읽기 편해집니다.

Callout으로 도구 출력 접기

Claude Code 세션에는 도구 호출이 엄청 많습니다. Bash, Read, Write 같은 도구를 한 세션에 수십 번 쓰는데, 이걸 다 펼쳐놓으면 대화 흐름이 안 보입니다.

Obsidian callout을 쓰면 접을 수 있습니다:

> [!tool]- Bash
> ```
> git status
> ```
> 출력: M src/main.rs, M Cargo.toml...

- 하나로 기본 접힌 상태가 됩니다. 보고 싶을 때만 펼치면 되니까 대화 흐름을 방해하지 않습니다.

사고 과정(thinking block)도 마찬가지입니다:

> [!thinking]- Thinking
> 검색 결과를 합치려면 점수 정규화가 필요한데...

Dataview 충돌 방지

이건 좀 짜증나는 문제였는데, Obsidian의 Dataview 플러그인은 키:: 값 형식을 인라인 필드로 인식합니다. 근데 대화 안에 :: 가 들어가는 경우가 꽤 있습니다. Python 타입 힌트, URL, 각종 코드에서요.

해결법은 의외로 단순합니다 — :: 사이에 제로 너비 공백(zero-width space)을 끼워넣으면 Dataview가 인식을 못 합니다. 눈에는 안 보이고, 복사해도 거의 영향 없고, Dataview만 속일 수 있습니다. 코드 블록 안에서는 이스케이프하지 않고 그대로 둡니다.(수만줄에 달하는 피드백과 함께 이슈 티켓 많이 만들어주신 github:batmania52님 이 자리를 빌어 감사드립니다)

눈에 안 보이는 문자로 문제를 해결한다는 게 좀 하남자스럽긴 한데, 잘 됩니다. 😇


index.mdlog.md

대화가 수백 개 쌓이면 폴더를 뒤지는 것도 일입니다. 그래서 자동 생성되는 메타 파일 두 개가 있습니다.

index.md — 전체 세션 목록. 새 세션이 인제스트될 때마다 맨 위에 추가됩니다. 위키링크로 바로 이동 가능.

## Sessions
- [[raw/sessions/2026-04-05/claude-code_seCall_a1b2c3d4|RRF 알고리즘 적용]] — 15턴, claude-code, 14:30
- [[raw/sessions/2026-04-04/claude-code_seCall_b2c3d4e5|벡터 검색 디버깅]] — 8턴, claude-code, 09:15

log.md — 수집 이력. 언제, 어떤 세션이, 몇 턴, 몇 토큰이었는지 기록합니다. append-only라 쌓이기만 합니다.

이것도 Obsidian에서 열면 위키링크가 다 살아있어서, 로그에서 바로 해당 세션으로 점프할 수 있습니다.


Git Sync — 버전 관리는 덤

Obsidian Vault가 결국 마크다운 파일 모음이니까, git으로 관리하면 무료 백업 + 버전 히스토리가 됩니다.(무려 옵시디언에는 이미 git plug-in도 있습니다!) seCall에는 secall sync 명령이 있는데, 하는 일은:

  1. git pull --rebase (원격 변경 가져오기)

  2. 새 세션 인제스트

  3. git push (원격에 올리기)

한 줄이면 "새 대화 수집 + vault 업데이트 + 원격 백업"이 끝납니다. .gitignore에 DB 파일이나 인덱스 파일은 자동으로 제외됩니다.

솔직히 이 기능은 Obsidian Git 플러그인이랑 겹치긴 합니다. 근데 CLI 한 줄로 인제스트와 sync를 동시에 할 수 있다는 게 편합니다. 그리고 여러 곳에 흩어져있는 대화들을 서로 모아주는 기능도 합니다.


그래서 실제로 어떻게 쓰고 있나

secall ingest 한 번이면 이런 일이 벌어집니다:

  1. ~/.claude/, ~/.codex/ 등에서 새 대화를 자동 감지

  2. 형태소 분석 → FTS5 인덱싱 + 임베딩 → 벡터 인덱싱 (#1, #2에서 다룬 부분)

  3. 마크다운 변환 → vault에 .md 파일 생성 (이번 편)

  4. index.md, log.md 업데이트

이후에는 두 가지 방식으로 대화를 돌아봅니다:

  • 검색이 필요할 때: secall recall "검색어" — 터미널에서 빠르게 찾기

  • 복기하고 싶을 때: Obsidian 열고 vault 브라우징 — Dataview로 필터링하면서 천천히 읽기

검색은 "뭘 찾을지 아는" 상황에서 좋고, vault 브라우징은 "뭘 찾을지 모르지만 둘러보고 싶은" 상황에서 좋습니다. 의외로 후자에서 더 많은 발견이 있습니다.(뭐!? 내가 이런 생각을 했다고!? 같은 발견입니다)


정리

기술

역할

선택 이유

Obsidian Vault

대화 위키화

마크다운 기반, Dataview, 이미 쓰고 있음

Frontmatter

세션 메타데이터

Dataview 쿼리로 필터링/정렬 가능

Callout 접기

도구 출력/thinking 숨김

대화 흐름 가독성 확보

Raw/Wiki 분리

원본 보존 + 지식 정리

기록과 지식을 구분

Git Sync

버전 관리 + 백업

vault가 마크다운이니까 자연스럽게


시리즈를 마치며

4편에 걸쳐서 seCall의 주요 기능을 풀어봤습니다.

  • #1에서 한국어 전문검색을 만들고

  • #2에서 의미 기반 벡터 검색을 붙이고

  • #3에서 둘을 합쳐서 하이브리드 검색을 만들고

  • #4에서 그 결과를 읽을 수 있는 형태로 꺼내는 것까지

돌이켜보면 핵심은 하나였습니다 — AI 대화는 쓰고 버리는 게 아니라 쌓이는 지식이다. 검색 가능하고, 브라우징 가능하고, 정리 가능한 형태로 만들어두면 이전 대화가 다음 작업의 컨텍스트가 됩니다. 그래서 노션이고 옵시디언이고 유명해 진 것이겠죠?

바이브코딩으로 만든 프로젝트치고는로 꽤 많은 걸 배웠습니다. 코드는 여전히 봐도 잘 모르지만(이제 에이전트들이 짜는 코드와 구조는 제가 알 수 있는 한계를 까마득히 넘어 섰습니다😇), SQLite FTS5, BM25, 벡터 임베딩, RRF, HNSW... 이 개념들은 이제 설명할 수 있게 됐습니다. 그게 바이브코딩의 좋은 점이 아닐까 싶습니다 — 만들면서 배우는 거.

본 포스트는 안(no)바이브 작성입니다.(바이브할 이유가 no입니다) 안심하고 읽으셔도 됩니다 😁 맨땅에 헤딩하는게 에이전틱 개발이 아닙니다. 다양한 경험을 통해 쌓아올린 인간지능의 도메인지식과 견고한 마구를 씌운 인공지능의 지치지 않는 정확한 작업의 이인삼각, 대환장콜라보입니다.

로그인한 회원만 댓글 등록이 가능합니다.

개발한당

KR | ID | EN
  • IDR
  • KOR
8.36 0.01

2026.07.10 KEB 하나은행 고시회차 1025회

다가오는 한인 행사일정

  • 등록 된 일정이 없어요!