사람이 거의 열지 않는 옵시디언 — AI가 주 독자인 vault에서 치운 것과 일부러 남긴 것

정기창·

옵시디언(Obsidian) vault를 정리하던 중에 제가 이런 말을 했습니다. "옵시디언은 요즘 AI가 많이 쓰지, 나는 거의 안 쓴다." 그 한마디가 정리의 전제를 바꿨습니다. 이 vault를 가장 많이 읽고 쓰는 쪽은 제가 아니라 AI였습니다.

저는 Claude Code로 여러 레포를 오가며 일하고, 레포를 넘나드는 AI 에이전트의 장기 메모리로 옵시디언 vault를 씁니다. PARA 구조로 나뉜 마크다운 노트가 약 2,000개 있고, vault 자체가 하나의 git 레포입니다. AI는 작업을 시작할 때 인덱스를 거쳐 필요한 노트 한두 개만 읽고, 작업이 끝나면 결과를 노트에 적은 뒤 인덱스와 현황 보드를 고쳐 커밋합니다. vault 경로에 직접 쓰는 스킬과 에이전트도 20개쯤 됩니다.

1편에서는 한 번 줄였던 vault가 왜 다시 불어났는지와 층마다 건 크기 상한을, 2편에서는 정리하는 동안 재는 도구가 여러 번 틀린 이야기를 적었습니다. 마지막 편인 이 글은 정리하다가 바뀐 전제에 관한 기록입니다. 결론부터 적자면, 사람이 보지 않는 사람용 장치는 비용만 남겼고 AI에게 그 비용은 잘못된 곳으로 가는 링크로 돌아왔습니다. 무엇을 치웠는지와 함께, 무엇을 일부러 남겼고 왜 남겼는지를 중심으로 적어 보겠습니다.

AI는 옵시디언 앱이 그린 화면을 보지 못합니다

옵시디언은 마크다운 파일을 그대로 보여 주기만 하는 앱이 아닙니다. Dataview나 Tasks 같은 플러그인은 파일 속 쿼리 블록을 실행해 그 자리에 표와 할 일 목록을 그려 줍니다. 그런데 AI는 앱을 거치지 않고 파일을 직접 읽습니다. 예를 들어 노트에 이런 블록이 있다고 해 보겠습니다.

```dataview
TABLE status
FROM "1-Projects"
WHERE status = "active"
```

앱에서는 이 자리에 진행 중인 프로젝트 표가 그려집니다. 하지만 파일을 직접 읽는 AI가 받는 것은 이 쿼리 문자열이 전부입니다. 결과는 파일 어디에도 적혀 있지 않습니다.

Waypoint처럼 폴더 목차를 파일 안에 직접 적어 주는 플러그인은 사정이 조금 낫습니다. 목록이 실제로 파일에 쓰이기 때문입니다. 다만 그 목록은 앱이 켜져 있을 때만 갱신됩니다. 앱을 거의 열지 않는 vault에서는 파일에 적힌 목록이 조금씩 실제와 멀어집니다.

그래서 1편에서 만든 공통 쓰기 규약 _AI-WRITING-GUIDE.md의 맨 앞에 이 문장을 적었습니다.

주 독자는 AI다. 앱에서만 보이는 결과에 정보를 맡기지 않는다.

UX 설계에는 주 페르소나(primary persona)라는 개념이 있습니다. 앨런 쿠퍼가 정리한 것으로, 제품을 쓰는 사람이 여럿이면 반드시 만족시켜야 할 한 명을 주 페르소나로 정하고, 다른 사람들의 요구는 주 페르소나를 해치지 않는 선에서만 들어준다는 원칙입니다.

돌이켜 생각해보면 저는 이 vault의 주 페르소나를 저로 잡고 꾸며 왔습니다. 5월에 이 인덱스 구조를 처음 설계한 글에서는 기존 노트를 옮기지 않고 인덱스만 위에 얹은 이유를, 그 지식베이스가 사람이 매일 쓰는 공간이기 때문이라고 적었습니다. 대시보드를 만들고, 폴더마다 폴더 노트가 생기게 두고, 평가 스크린샷이 쌓이게 둔 것도 같은 전제 위에서였습니다. 실제 주 페르소나는 AI였고, 사람을 위한 장치가 AI에게는 보이지 않거나 오히려 방해가 되는 자리가 여럿 있었습니다.

이 전제를 받아들이고 여섯 군데를 손봤습니다. 절마다 무엇을 왜 바꿨는지와 함께, 일부러 그대로 둔 것과 그 이유를 적었습니다.

Home 대시보드는 두 번째 정본이었습니다

vault에는 제가 손으로 관리하던 Home 대시보드가 있었습니다. 들여다보니 7개월째 그대로였고, 이미 코드를 지운 프로젝트를 여전히 "개발 중"으로 적고 있었습니다.

현황의 정본은 따로 있습니다. AI가 기록할 때마다 갱신하는 현황 보드 _STATUS.md이고, 세션을 시작할 때마다 훅이 이 보드를 요약해 대화 맨 앞에 넣어 줍니다. Home은 같은 현황을 한 번 더 적고 있던 두 번째 정본이었습니다.

같은 사실이 두 곳에 있으면 한쪽이 먼저 낡는데, 어느 쪽이 먼저 낡을지는 정해져 있습니다. 기록할 때마다 함께 고쳐지는 쪽은 살아남고, 누군가 기억해서 고쳐야 하는 쪽이 먼저 낡습니다. Home의 그 누군가는 저였고, 저는 앱을 거의 열지 않았습니다.

사실 하나에 정본 하나를 두는 단일 정본(single source of truth) 원칙은 라우팅 매트릭스를 정리한 글에서도 다뤘습니다. 그때 낡은 매트릭스가 안 적힌 매트릭스보다 위험하다는 결론을 적었는데, Home이 정확히 그 모양이었습니다.

그래서 Home에서 상태 목록을 없애고, 무엇을 알고 싶으면 어느 파일을 보라는 안내 페이지로 바꿨습니다. 현황이 궁금하면 _STATUS.md를 보라는 식입니다.

페이지 자체는 지우지 않고 이 안내만 남겼습니다. 안내는 상태를 담지 않으니 앞으로 고칠 일이 없습니다. 현황이 아무리 바뀌어도 "현황은 저 파일에 있다"는 문장은 틀려지지 않습니다. 같은 내용을 옮겨 적은 사본은 낡지만, 가리키기만 하는 문장은 낡지 않습니다.

frontmatter는 본문보다 먼저 읽힙니다 — summary는 넣고 날짜는 채우지 않았습니다

진입 노트 122개에 summary 한 줄

summary를 넣은 이유는 하나입니다. AI가 본문을 열지 않고도 이 노트가 지금 작업과 관련 있는지 판단하게 하려는 것입니다.

피터 피롤리와 스튜어트 카드는 사람이 정보를 찾는 모습을 먹이를 찾는 동물에 빗대 정보 채집 이론(information foraging theory)을 세웠습니다. 그 이론의 핵심이 정보 냄새(information scent)입니다. 사람은 링크 문구나 요약 같은 단서를 보고 그 길 끝에 원하는 정보가 있을지 가늠한다는 것입니다.

단서가 약하면 일단 들어가 보고 되돌아 나오는 헛걸음이 늘어납니다. AI의 헛걸음은 관련 없는 본문을 통째로 읽어 컨텍스트를 채우는 일입니다.

그래서 인덱스가 가리키는 진입 노트 122개의 frontmatter에 summary를 넣었습니다. 150자 이하의 한 문장이고, 가장 긴 것이 145자입니다. 요약은 서브에이전트 셋이 나눠 썼고, 20KB가 넘는 노트는 전부 읽지 않고 목차와 앞부분, 마지막 절만 읽고 썼습니다(이 글의 KB는 1,000바이트 기준입니다). 넣을 때는 요약 한 줄 말고는 파일이 바뀌지 않았는지를 스크립트로 대조했습니다.

날짜는 채우지 않고, status에 쌓인 기록은 내렸습니다

같은 frontmatter라도 날짜 필드(created·updated)는 일괄로 채우지 않았습니다. 사람이 앱에서 노트를 고쳐도 이 값은 자동으로 갱신되지 않아서, 채운 뒤에는 곧 git 이력보다 틀린 정보가 됩니다. Home과 같은 병입니다. 날짜를 정확히 아는 곳은 git 이력이고, frontmatter의 날짜는 누군가 기억해서 고쳐야 하는 사본입니다.

틀린 메타데이터는 없는 것보다 나쁩니다. 없는 값은 다른 곳을 찾아보게 하지만, 틀린 값은 그대로 믿게 합니다.

frontmatter를 로그로 쓰던 노트도 있었습니다. status: 칸에 상태 변화 기록이 쌓여 한 칸이 최대 8.1KB까지 자란 노트가 23개였습니다. 1편에서 본 병과 같은 모양입니다. 상한 없이 덧붙이는 칸은 결국 로그가 됩니다.

기록은 지우지 않고 원문 그대로 노트 끝이나 이력 파일로 옮겼고, status에는 active나 dormant 같은 짧은 값만 남겼습니다. 값은 정리 커밋을 뺀 최근 30일 활동을 기준으로 정했습니다.

빈 폴더 노트가 진짜 노트를 가렸습니다

folder-notes 플러그인에는 새 폴더가 생기면 폴더와 같은 이름의 노트를 자동으로 만들어 주는 기능이 있습니다. 폴더마다 대표 노트를 두려는 사람용 편의 기능입니다. 문제는 이 기능이 스크린샷 폴더에서도 똑같이 동작했다는 것입니다.

그래서 스크린샷 폴더마다 평가 노트와 이름이 같은 빈 노트가 하나씩 생겼습니다. 예를 들어 UI 평가 노트 평가-1차.md가 있으면, 그 평가의 스크린샷 폴더 안에도 같은 이름의 빈 노트가 있는 식입니다. 이름만으로 링크를 걸면 AI도 옵시디언도 빈 노트 쪽으로 갈 수 있었습니다. 프로그래밍에서 안쪽 범위에 선언한 같은 이름이 바깥 변수를 가리는 섀도잉(shadowing)과 닮은 상황인데, 여기서는 가리는 쪽이 내용 없는 빈 노트였습니다.

진짜 노트를 가리던 빈 폴더 노트 76개를 지우고 플러그인도 껐습니다. 사람이 앱에서 폴더 노트를 거의 쓰지 않으니 켜 둘 이득이 없었습니다. 같은 날짜와 같은 이름을 가진 서로 다른 실제 노트도 한 쌍 있었습니다. 성격이 다른 두 기록이었는데, 한쪽 파일명에 접미사를 붙이고 그 노트를 가리키던 링크를 함께 고쳤습니다.

반대로 _INDEX.md나 README처럼 이름이 겹치는 파일은 그대로 뒀습니다. 이쪽의 겹침은 사고가 아니라 규약입니다. AI의 조회 절차가 폴더마다 그 이름으로 파일을 찾기 때문에, 이름이 같아야 절차가 성립합니다.

같은 이름이 문제가 되는 것은 이름만으로 찾을 때이고, 위치와 함께 찾을 때는 약속이 됩니다. 겹치는 이름을 한꺼번에 없애지 않고 문제인 경우와 규약인 경우를 가른 이유입니다.

그 결과 이름이 같은 파일 묶음은 102개에서 25개로, 다른 폴더에서 경로 없이 이름만으로 건 모호한 링크는 54회에서 12회로 줄었습니다.

보관은 날짜가 아니라 근거로 했습니다

비활성 프로젝트 폴더를 보관 폴더(4-Archives/)로 옮기는 일은 규칙 한 줄로 끝낼 수도 있었습니다. 마지막 수정이 60일 넘은 폴더를 옮긴다는 규칙입니다. 하지만 날짜는 아무도 쓰지 않는다는 사실을 대신 재는 값일 뿐입니다. 폴더가 조용한 것과 폴더가 필요 없는 것은 다릅니다.

이 상황에 꼭 맞는 이야기로 체스터턴의 울타리(Chesterton's fence)가 있습니다. G. K. 체스터턴이 남긴 비유인데, 길을 가로막은 울타리의 쓸모를 모르겠다고 바로 치우지 말고 왜 세웠는지 알아낸 다음에 치우라는 것입니다. 오래돼 보이는 폴더가 바로 그 울타리였습니다. 그래서 신호 네 가지를 함께 봤습니다.

  • 정리 커밋을 뺀 마지막 내용 변경일
  • 그 폴더에 쓰는 스킬이나 자동화가 있는가
  • 최근 30일 안의 작업 계획(플랜)이 그 폴더를 참조하는가
  • 현황 보드에 진행 중으로 올라 있는가

코드가 있는 프로젝트는 코드 레포의 마지막 커밋도 함께 봤습니다. 첫 번째 신호에서 정리 커밋을 빼는 이유는 2편에 적었습니다. 제 정리 작업 자체가 활동으로 잡혀, 멈춘 폴더가 살아 있는 것처럼 보였기 때문입니다.

이렇게 해서 프로젝트 폴더 86개 중 54개를 보관 폴더로 옮겼고 32개가 남았습니다. 54개는 네 가지 신호로 고른 첫 묶음 53개에, 보드에 "종료"로 적혀 있었지만 마지막 변경이 정확히 기준일인 60일째여서 첫 목록에서 빠졌던 프로젝트 1개를 따로 더한 수입니다. 옮긴 폴더를 가리키던 경로 참조도 함께 고쳤습니다.

날짜로는 오래됐지만 남긴 폴더가 6개 있었고, 모두 울타리를 세운 이유가 있었습니다.

  • 진행 중인 플랜이 참조하는 폴더 2개
  • 스킬이 산출물을 쓰는 위치인 폴더 2개
  • 폴더의 날짜와 달리 그날도 갱신된 프로젝트 1개
  • 진행 중인 기획의 근거 자료인 폴더 1개

날짜만 보고 옮겼다면 플랜과 스킬이 가리키는 경로가 한꺼번에 어긋났을 것입니다.

반대 방향의 어긋남도 있었습니다. 현황 보드에는 "진행 중"으로 올라 있는데, vault와 코드 레포 모두 3개월 넘게 멈춘 프로젝트였습니다. 이번에는 보드가 틀린 쪽이어서, 폴더를 옮기고 보드의 행을 "보류"로 내렸습니다.

앞에서 현황의 정본은 보드라고 적었지만, 정본을 하나로 모았다고 그 정본이 늘 맞는 것은 아니었습니다. 다만 틀렸을 때 고칠 곳이 한 군데라는 점은 분명했습니다.

로그형 노트는 나눴습니다, 서로 인용하는 문서만 빼고

작업할 때마다 날짜별 기록이 쌓이는 노트들이 있습니다. AI가 이런 노트를 진입점으로 열면 지금 상태를 알기 위해 지난 기록까지 함께 읽게 됩니다.

로그형 노트 11개의 지난 절을 <노트>-이력.md로 옮기고, 노트 끝에는 지난 기록은 이력 파일에 있다는 포인터를 달았습니다. 11개 노트의 합계가 1,668KB에서 576KB로 줄었습니다. 1편에서 인덱스와 보드에 적용한 상태와 이력의 분리를 노트 단위로 한 셈입니다.

12개는 나누지 않았습니다. 절끼리 "§15-9 참조"처럼 번호로 서로 인용하는 문서라, 지난 절을 다른 파일로 옮기면 그 참조가 끊깁니다. 겉보기에는 날짜순으로 쌓인 기록 같아도, 절이 서로를 인용하는 문서는 기록의 묶음이 아니라 하나의 글입니다. 한 덩어리로 된 문서와 리서치 원시 자료도 그대로 뒀습니다.

아무도 보지 않던 스크린샷 729장 — 남길 것은 두 가지였습니다

UI 평가를 할 때마다 스크린샷이 vault로 복사됐고, 어느새 729장, 99MB가 쌓여 있었습니다. 저는 그 스크린샷을 보지 않았습니다.

점수와 결함 수의 추세는 이미 DB 테이블에 197건이 쌓여 있었고, vault의 평가 노트 120개보다 오히려 완전했습니다. 에이전트 메모리에 DB를 처음 붙일 때 저는 파일을 정답으로 두고 DB는 미러로 붙인다고 적었는데, 평가 기록만큼은 DB 쪽이 더 완전해져 있었습니다. 추세를 볼 자리는 이미 따로 있었던 셈입니다.

남길 만한 것은 두 가지뿐이었습니다. 하나는 여러 평가에서 반복된 결함 패턴의 요약입니다. 키보드 접근 11건, aria-label 10건, 포커스 표시 8건으로 결함이 접근성에 몰려 있었습니다. 평가 한 건만 봐서는 보이지 않고 모아야 보이는 정보입니다. 다른 하나는 평가마다 한 줄씩 적는 요약 표입니다.

그런데 그 한 줄 요약 표도 한 줄이 아니었습니다. 칸 하나에 평가 전문이 들어가 한 칸이 13KB까지 커져 있었습니다. 앞의 status: 칸과 같은 병입니다. 칸마다 첫 문장만 남기니 표가 312KB에서 35KB로 줄었습니다.

스크린샷은 지웠습니다. git 이력에는 남아 있으니 필요하면 되살릴 수 있습니다. 개별 평가 노트는 보관 폴더로 옮겼고, 평가 에이전트가 더 이상 vault에 스크린샷을 복사하지 않게 바꿨습니다. 쌓인 것만 치우고 복사를 그대로 두면 같은 더미가 다시 쌓이기 때문입니다.

옵시디언 vault 정리 전체 결과

대상 전 후
프로젝트 폴더 86개 32개
노트가 아닌 파일(스크린샷 등) 825개, 122MB 93개, 22MB
vault의 .git 325MB 131MB
어디서도 링크되지 않은 노트 271개 (13.1%) 206개 (10.2%)

어디서도 링크되지 않은 노트, 흔히 고아 노트라고 부르는 것의 수는 2편에서 바로잡은 방법으로 센 값입니다. .git이 줄어든 것은 git gc로 저장소를 정리한 결과이고, 지운 스크린샷은 이력에 그대로 남아 있습니다.

정리 — vault는 실제 독자를 기준으로 설계합니다

세 편에 걸친 정리에서 남은 교훈은 결국 하나입니다. 실제 독자를 기준으로 설계해야 한다는 것입니다. 옵시디언처럼 사람을 위해 만든 지식 관리 도구를 AI 에이전트의 메모리로 쓴다면, 주 독자가 AI인 vault에 필요한 것은 이렇습니다.

  • 통째로 읽혀도 부담이 없는 작은 인덱스
  • 본문을 열기 전에 판단하게 하는 요약
  • 겹치지 않는 이름
  • 앱의 렌더에 기대지 않는 정보
  • 사실 하나에 정본 하나
  • 근거가 있는 과감한 보관

대시보드, 자동 폴더 노트, 스크린샷 같은 사람용 편의 장치는 사람이 보지 않으면 비용만 남습니다. 그리고 AI에게 그 비용은 잘못된 곳으로 가는 링크로 돌아옵니다.

output style을 정리한 글의 끝에, 도구를 오래 쓸수록 무엇을 적을까보다 어디에 적을까를 더 고민하게 된다고 적었습니다. 이번 정리도 대부분 그 질문이었습니다. 현황은 보드에, 지난 기록은 노트 끝과 이력 파일에, 관련 여부를 가늠할 단서는 본문 앞에 두었습니다. 평가 추세는 이미 DB에 있었으니, vault에는 반복 패턴과 한 줄 요약 표를 두었습니다.

남긴 것들은 같은 질문을 통과했습니다

체스터턴의 울타리는 보관 절에서 꺼냈지만, 돌아보면 다른 절에서 그대로 둔 것들도 모두 같은 질문을 통과했습니다. 이게 왜 여기 있는가. 답이 아직 살아 있는 것은 남겼고, 답이 사라진 것은 치웠습니다. 사람이 볼 것이라는 이유로 세운 장치는 사람이 보지 않게 되면서 그 이유를 잃었습니다.

절 일부러 그대로 둔 것 그 이유
Home 대시보드 파일을 가리키는 안내 상태를 담지 않아 낡지 않습니다
frontmatter 일괄로 채우지 않은 날짜 필드 자동으로 갱신되지 않아 곧 틀립니다
이름 충돌 _INDEX.md·README의 같은 이름 조회 절차가 그 이름으로 찾습니다
보관 오래됐어도 쓰이는 폴더 6개 플랜·스킬·진행 중인 작업과 이어져 있습니다
로그형 노트 번호로 서로 인용하는 문서 12개 나누면 참조가 끊깁니다
스크린샷 반복 결함 패턴, 한 줄 요약 표 모아야 보이고, 한 번에 훑을 수 있습니다

치운 자리에 같은 것이 다시 쌓이지 않도록 원인 쪽도 막았습니다. 플러그인을 끄고, 평가 에이전트의 스크린샷 복사를 멈추고, Home에서 상태를 뺐습니다.

치우는 동안에도 1편과 2편에서 세운 원칙은 그대로 지켰습니다. 줄인 내용은 원문 그대로 옮겨 보관했고, 바꾸기 전에는 미리보기로 바뀔 수를 확인했고, 확인은 찾을 때와 다른 방법으로 했습니다. 과감하게 치운 만큼 되돌릴 길은 남겨 두었습니다.

세 편을 마치며

몇 달 전 에이전트 메모리와 하네스를 점검하는 법을 쓸 때, 에이전트의 메모리에 필요한 내용만 남아 다른 세션에서 잘 쓰이는지가 고민이라고 적었습니다. 세 편을 쓰고 나니 그 고민의 답이 조금 보입니다. 무엇이 필요한지 가리려면 누가 읽는지부터 알아야 했습니다.

저는 그 독자를 저라고 생각하고 있었습니다. 실제 독자는 앱 바깥에서, 세션이 열릴 때마다 이 vault를 읽고 있었습니다. 정리를 시작할 때 먼저 물었어야 할 것은 무엇을 지울지가 아니라 누가 읽는지였다는 생각이 들었습니다.

옵시디언에이전트 메모리Claude CodeLLM 지식 관리frontmatter단일 source of truth컨텍스트 엔지니어링