나는 대학생 때 Notion을 사용했다. 약 3~4년 전부터 사용한 것 같은데, 그 당시 Notion은 대학생한테 매우 좋은 서비스였다. education 요금제로 유료 기능을 무료로 사용할 수 있었고, 동시 편집과 비교적 간단한 사용 방식은 매우 매력적이었다.
이번 연도에는 AI를 사용하기 시작하면서 MCP를 통해 Notion을 조작하는 방식으로 활용하였다. 그런데 여기서 큰 불편함이 존재했는데, 바로 Notion MCP의 성능이 좋지 못하다는 것이었다. Figma나 다른 서비스에서 제공하는 MCP를 사용해 보면 성능이 그렇게 좋지 못하다는 것을 느낄 것이다. 특히 결과물에 비해 토큰을 너무 많이 사용한다는 점이 아쉬웠다. 그리고 한 번 수정할 부분이 생기면 정확히 타겟을 잡고 프롬프트를 입력하는 것도 힘들었다.
그러다가 Obsidian으로 눈길을 돌렸는데, Obsidian은 작년부터 사용하기 시작했었다. Obsidian은 로컬 파일 시스템 기반이라 Notion과 같은 편의성이나 동시 작업 같은 기능이 기본으로 존재하지는 않는다. 물론 익스텐션으로 추가할 수도 있다. 그런데 지금 Obsidian의 가장 큰 장점은 바로 앞서 이야기한 로컬 파일 시스템 기반이라는 점이다. Codex처럼 CLI 또는 로컬 환경에서 AI를 사용하는 사람 입장에서는 이것만큼 강력한 장점이 없을 것이다.
이번 글에는 내가 Obsidian으로 지식 관리를 변경하면서 커스터마이징한 설정들과 장점들에 대해서 이야기해 보려고 한다.
사실 Notion MCP가 성능이 좋지 못하다고 느끼는 이유 중 하나는 결과물에 비해 공들여야 하는 것이 많다는 것이다. 덤으로 결과물에 비해 너무 많은 토큰 사용량도 한몫한다. 사실 이거는 사용자가 정확히 지시를 내리지 않은 것이 가장 큰 원인이다. 하지만 정확한 지시를 못하는 이유 중 하나는 바로 사용자가 Notion의 구조를 제대로 이해하기 어렵다는 것이다.
Notion은 컴포넌트 형태로 UI를 구성하고 해당 컴포넌트에는 여러 가지 타입이 존재한다. 물론 사용자가 많이 사용한 컴포넌트거나 간단한 형태, 예를 들어 텍스트나 링크 같은 것들이면 해당 컴포넌트를 수정해 달라고 요청하는 게 어렵지는 않을 것이다. 하지만 데이터베이스를 포함한 캘린더, 관계형 속성, 수식, 여러 뷰가 섞이기 시작하면 이야기가 달라진다.
사람이 Notion 화면을 볼 때는 이 모든 것이 하나의 페이지처럼 보인다. 하지만 MCP를 통해 작업하는 에이전트는 먼저 어떤 페이지인지 찾아야 하고, 그 안에 어떤 블록이 있는지 읽어야 한다. 데이터베이스가 있다면 속성 구조와 각 항목의 식별자도 확인해야 한다. 그다음에야 어느 블록이나 속성을 수정해야 하는지 결정할 수 있다.
예를 들어 “이번 주 일정에서 끝난 작업을 완료로 바꾸고, 관련 공부 노트 링크도 추가해 줘”라고 요청했다고 해 보자. 사람에게는 자연스러운 말이지만 에이전트 입장에서는 먼저 이번 주 일정이 어떤 페이지인지 찾아야 한다. 일정이 일반 페이지인지, 데이터베이스인지, 연결된 다른 데이터베이스가 있는지 확인해야 한다. 완료 상태가 체크박스인지 선택 속성인지도 알아야 한다. 마지막으로 어느 공부 노트를 어떤 블록에 링크해야 하는지도 정해야 한다.
결국 하나의 요청을 처리하려면 검색, 페이지 조회, 블록 조회, 속성 확인, 수정, 수정 결과 확인처럼 여러 단계가 필요하다. 각 단계의 결과는 다시 모델이 읽을 수 있는 형태로 넘어온다. 페이지 구조와 속성 정보, 블록 목록이 길어질수록 그만큼 토큰도 같이 사용된다. 중간에 잘못된 페이지를 찾거나 원하는 타겟을 놓치면 다시 조회하는 과정도 생긴다.
여기서 MCP가 어떤 방식으로 동작하는지 보면 왜 이런 일이 생기는지 조금 더 이해하기 쉽다. MCP는 AI가 외부 서비스와 통신할 수 있게 해 주는 인터페이스다. 에이전트는 MCP 서버가 제공하는 도구 목록을 보고, 필요한 도구를 골라 입력값을 넣어 호출한다. 도구 이름은 서버마다 다르지만 흐름은 보통 아래와 비슷하다.
에이전트
└─ MCP 도구 호출: search_page("이번 주 일정")
└─ Notion API
└─ 결과 반환: 페이지 ID, 제목, 일부 내용
에이전트
└─ MCP 도구 호출: fetch_page(페이지 ID)
└─ Notion API
└─ 결과 반환: 블록, 속성, 데이터베이스 정보
에이전트
└─ MCP 도구 호출: update_page(...)
└─ Notion API
└─ 수정 결과 반환
인터페이스 자체는 단순하다. 필요한 도구를 호출하고 결과를 받으면 된다. 하지만 실제 작업에서는 앞선 결과를 봐야 다음 호출을 정할 수 있다. 검색 결과에서 페이지를 고르고, 페이지를 읽은 뒤 블록이나 속성을 확인하고, 그제야 수정 요청을 보낼 수 있다. 이런 작업은 대부분 순서대로 진행된다.
그래서 시간이 걸린다. 에이전트의 도구 호출은 MCP 서버를 거쳐 Notion API와 통신하고, 결과가 다시 모델에게 돌아와야 한다. 다음 행동이 이전 결과에 의존하면 여러 요청을 한 번에 처리하기도 어렵다. 페이지 구조가 복잡할수록 조회와 확인 과정도 길어진다.
토큰을 많이 사용하는 이유도 비슷하다. 모델은 도구를 호출하기 전에 도구의 설명과 입력 형식을 이해해야 하고, 호출 결과로 돌아온 페이지 구조, 블록 목록, 속성 정보를 다시 읽어야 한다. 내가 수정하려는 부분은 한 줄이어도, 그 한 줄을 찾기 위해 넓은 범위의 결과를 모델 컨텍스트에 가져오는 경우가 생긴다. 잘못된 페이지를 찾거나 타겟을 놓치면 이 과정이 다시 반복된다.
Obsidian은 여기서 출발점이 다르다. vault 안의 노트가 폴더와 Markdown 파일로 그대로 저장되어 있다. Codex는 MCP를 통해 원격 Notion API에 요청하는 대신, 로컬 파일 시스템에서 필요한 파일을 바로 찾고 읽을 수 있다.
hyuk-s_obsidian/
├── AGENTS.md # vault 전체 작업 규칙
├── schedule/ # Daily · Weekly · Monthly
├── study/ # 강의와 기술 학습 노트
│ └── AGENTS.md # 학습 노트용 규칙
├── doc/ # 개인 문서와 프로젝트 기록
├── 개발 글/ # Velog 글 작업 공간
│ ├── AGENTS.md # 글의 문체와 검토 기준
│ └── velog 초안/
└── assets/ # 템플릿과 첨부 파일
예를 들어 Codex에게 “이번 주 일정에서 끝난 작업을 완료로 바꾸고, 관련 공부 노트 링크도 추가해 줘”라고 요청한다고 생각해 보자. Codex는 schedule/weekly/에서 이번 주 파일을 찾고, 필요한 줄만 읽으면 된다. 공부 노트도 study/에서 검색할 수 있다. Notion처럼 페이지 ID, 블록 ID, 데이터베이스 속성 타입을 먼저 알아낼 필요가 없다.
물론 로컬 파일을 읽고 수정하는 과정도 에이전트의 도구 호출을 사용한다. 하지만 차이가 있다. 파일 경로와 Markdown 내용은 사람이 읽는 구조와 거의 같고, Codex는 rg 같은 검색 명령으로 필요한 파일과 줄을 한 번에 좁힐 수 있다. 원격 API 요청을 여러 번 보내고 응답을 기다리는 과정도 없다. 필요한 부분만 읽고 바로 수정할 수 있으니, 같은 작업을 할 때 컨텍스트가 덜 복잡해지고 토큰도 상대적으로 덜 사용하게 된다.

로컬 파일 기반이라는 점의 또 다른 장점은 AGENTS.md 같은 하네스 문서를 폴더에 함께 둘 수 있다는 것이다. 하네스 문서는 이 폴더가 어떤 용도인지, 노트를 어떤 형식으로 작성해야 하는지, 수정할 때 무엇을 확인해야 하는지를 적어 둔 작업 규칙이다.
이 문서가 없으면 매번 같은 설명을 프롬프트에 넣어야 한다는 문제가 생긴다. 예를 들어 “이 글을 Velog 초안으로 다듬어 줘”라고만 요청하면 AI는 내가 어떤 톤을 원하는지, 파일을 어디에 저장해야 하는지, 참고 링크를 남겨야 하는지 알 수 없다.
물론 한 번의 작업이라면 프롬프트에 전부 적을 수 있다. 하지만 글을 쓸 때마다 같은 규칙을 다시 입력하는 것은 귀찮고, 어느 순간 빠뜨리는 내용도 생긴다. 작업이 길어질수록 앞에서 전달한 기준이 흐려질 수도 있다.
그래서 vault 루트의 AGENTS.md에는 전체 폴더 구조와 공통 작성 원칙을 두고, study/에는 학습 노트 규칙을, 개발 글/에는 Velog 초안의 문체와 검토 기준을 두었다. Codex는 작업할 파일이 있는 경로의 하네스 문서를 함께 읽고 작업하므로, 매번 같은 형식과 주의 사항을 프롬프트로 설명하지 않아도 된다.
hyuk-s_obsidian/
├── AGENTS.md # vault 전체 작업 규칙
├── study/
│ └── AGENTS.md # 학습 노트용 규칙
└── 개발 글/
└── AGENTS.md # Velog 글의 문체와 검토 기준
이게 강력한 이유는 파일의 위치 자체가 작업 범위가 되기 때문이다. study/ 안의 노트를 수정하면 Codex는 학습 노트 규칙을 기준으로 보고, 개발 글/ 아래 글을 다루면 글 작성 규칙을 기준으로 본다. 하나의 긴 프롬프트로 모든 작업을 통제하는 대신, 실제 파일 구조에 맞춰 규칙도 같이 나눌 수 있다.
Notion에서도 페이지에 작업 규칙을 적어 둘 수는 있다. 하지만 에이전트가 어떤 페이지를 열었을 때 어느 규칙을 함께 봐야 하는지 구조적으로 연결하기는 쉽지 않다. 반면 로컬 폴더에서는 노트와 규칙 파일이 같은 경로에 남는다. 사람도 규칙을 찾기 쉽고, 에이전트도 같은 맥락에서 작업할 수 있다.
내 작업은 대체로 다음 순서로 진행한다.
Codex CLI는 로컬 저장소 안에서 파일을 살펴보고 수정하고, 설치된 도구를 실행하는 방식으로 동작한다.
내가 이 방식을 좋아하는 이유는 대화와 결과물이 따로 놀지 않기 때문이다. 채팅에서 받은 답을 복사해 다시 붙여 넣는 대신, 에이전트가 실제 노트를 수정하고 나는 Obsidian에서 결과를 읽는다. 물론 에이전트가 만든 문장을 그대로 확정하지는 않는다. 개인 경험과 생각이 담긴 노트는 내가 마지막으로 읽고 고친다.
Obsidian에는 Lean Terminal 플러그인을 사용하고 있다. 화면 하단에서 터미널을 열 수 있으므로, 노트를 보다가 별도 터미널 앱으로 이동하지 않고 바로 Codex에게 작업을 맡길 수 있다.
문제는 터미널 패널을 닫거나 Obsidian을 다시 열었을 때다. 진행 중인 Codex 작업까지 같이 사라지면 곤란하다. 그래서 Lean Terminal의 shell path를 작은 wrapper 스크립트로 연결하고, 이 스크립트가 tmux 세션에 붙도록 구성했다.
Lean Terminal
└─ /Users/choi-hyk/.local/bin/obsidian-tmux-shell
└─ tmux -u new-session -A -s obsidian
├─ pane 1: vault 루트에서 실행 중인 Codex CLI
└─ pane 2: 다른 vault 작업을 위한 zsh
실제로 사용하는 스크립트는 아래와 같다.
#!/bin/zsh
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
export LC_CTYPE=en_US.UTF-8
exec /opt/homebrew/bin/tmux -u new-session -A -s obsidian
-s obsidian은 세션 이름이다. Obsidian에서 연 모든 Lean Terminal이 같은 작업 공간을 찾는다.-A는 obsidian 세션이 있으면 연결하고, 없으면 새로 만든다. 그래서 새 터미널을 열어도 이전 Codex 세션으로 돌아갈 수 있다.Lean Terminal은 startup command를 비워 두고 shell path만 이 wrapper로 지정했다. 터미널을 열 때마다 임시 명령을 실행하는 대신, tmux가 유지하는 같은 셸 세션으로 돌아가기 위해서다. 플러그인의 기본 작업 폴더도 vault 루트이므로 Codex는 처음부터 이 저장소의 파일 구조와 AGENTS.md를 기준으로 작업한다.
아래는 실제로 내가 이 글을 작성하면서 터미널에서 Codex CLI를 사용하는 모습이다. Obsidian과 터미널 디자인은 최대한 내가 사용하는 vscode 테마에 맞게 커스텀했다.

Obsidian이 모든 사람에게 Notion보다 좋은 도구라고 생각하지는 않는다. 동시 편집이 중요하거나 팀 문서를 빠르게 공유해야 한다면 Notion이 더 편한 경우도 많다. 하지만 Codex처럼 CLI 기반 에이전트 도구를 자주 사용한다면 Obsidian을 강력하게 추천하고 싶다.