1인 회사를 움직이는 AI 자동화 실전 노트
왜 1인 회사에 AI 자동화인가 — 원칙 다섯 가지
핵심 요점
- 자동화 후보는 반복성, 되돌릴 수 있음, 1차 근거 존재라는 세 기준으로 고르고, 되돌리기 어려운 일은 초안까지만 자동화합니다.
- 에이전트는 읽기 전용 원본에서 만든 위키를 인용해 답하게 하고, 페이지마다 confidence와 '미확인' 표시로 근거 등급을 드러냅니다.
- 외부로 나가는 행동(발행, 메일, push)은 사람이 승인하며, 초안 단계에서 발행 크리덴셜을 불러오지 않는 구조로 강제합니다.
- 비용은 구독 안에서 닫습니다. 결정론으로 할 수 있는 일은 스크립트로 처리하고, 내용이 바뀌었을 때만 LLM을 호출하며, 무거운 잡은 횟수를 제한합니다.
- 새 잡은 처음 한 번 수동으로 돌려 결과를 확인하고, 결과물을 직접 확인한 뒤에야 완료라고 말합니다.
- 반복되는 지시는 SKILL.md(스킬)와 CLAUDE.md(저장소 규칙)로 남겨 절차를 재사용합니다.
대표 한 명이 다섯 부서를 겸하는 현실
직원 없는 기술 회사의 대표는 하루에도 여러 번 역할을 바꿉니다. 오전에는 코드 리뷰와 배포를 하고, 점심 전에 세금계산서를 확인하고, 오후에는 변리사가 보낸 명세서 초안을 검토하고, 저녁에는 블로그 초안과 고객 문의 답장을 씁니다. 하나하나는 어렵지 않지만 맥락을 바꾸는 데 드는 비용이 하루를 다 먹습니다. "그 특허 해외출원 기한이 언제였지?", "지난번 고객사에 어떤 설정으로 납품했지?" 같은 질문에 답하려고 메일과 폴더를 뒤지는 시간도 만만치 않습니다.
저는 이 문제를 코딩 에이전트(Claude Code), MCP로 연결한 사내 도구, 예약 실행 잡, 그리고 회사 자료를 정리해 두는 LLM 위키로 풀고 있습니다. 다만 "AI가 다 해 준다"는 이야기는 아닙니다. 오히려 무엇을 맡기지 않을지 정하는 일이 더 중요했습니다.
무엇부터 자동화할까: 세 가지 기준
후보 업무가 생기면 아래 세 가지 질문을 던집니다. 셋 다 "예"일 때만 바로 자동화합니다.
| 기준 | 질문 | "아니오"일 때 |
|---|---|---|
| 반복성 | 같은 절차를 한 달에 두 번 이상 하는가? | 한 번만 할 일이면 그냥 대화로 처리합니다 |
| 되돌릴 수 있음 | 결과가 틀려도 지우거나 고칠 수 있는가? | 초안까지만 자동화하고, 실행은 사람이 합니다 |
| 근거 존재 | 정답을 확인할 1차 자료(코드, 장부, 공문)가 있는가? | 먼저 자료를 모으는 일부터 합니다 |
예를 들어 "주간 블로그 초안 쓰기"는 반복적이고 근거 자료가 있지만, 발행은 되돌리기 어렵습니다. 그래서 초안 생성까지만 예약 잡으로 돌리고, 발행은 사람이 승인합니다. 반대로 "새 계약 조건 협상"은 반복성도 낮고 되돌리기도 어려워서 자동화 대상에서 뺍니다.
원칙 ① 1차 자료를 근거로 삼습니다
에이전트가 회사 고유의 사실을 모르면 그럴듯한 말을 지어냅니다. 실제로 장부 데이터가 있는데도 "장부가 활성화되지 않았다"고 추론한 브리핑이 나온 적이 있습니다. 이런 일은 기능을 더 붙인다고 해결되지 않았고, 근거를 붙여야만 해결됐습니다.
그래서 원본(코드 저장소, 회계 문서, 특허 서류)은 읽기 전용으로 두고, 에이전트는 그 원본을 읽어 위키를 만들고, 답할 때는 그 위키를 인용하게 합니다. 각 페이지에는 근거의 신뢰도를 적습니다.
confidence: high # 코드·1차 문서로 직접 확인
# medium: 2차 자료·추정 / low: 가설·편차 큰 시장 데이터
본문 중 확인하지 못한 주장에는 "미확인"이라고 따로 표시합니다. Anthropic도 에이전트가 각 단계마다 환경에서 "ground truth"를 얻는 것이 중요하다고 말합니다.
원칙 ② 외부로 나가는 것은 사람이 확인합니다
내부에서 초안을 만들고, 위키를 고치고, 로컬 커밋을 하는 일은 에이전트에게 맡깁니다. 하지만 회사 밖으로 나가는 행동은 반드시 사람이 확인합니다. 블로그 발행, 메일 발송, git push, 고객사 설정 변경 같은 것들입니다.
이 게이트는 프롬프트로 부탁하는 것만으로는 부족합니다. 구조로 막아야 합니다. 제 예약 잡은 초안을 만드는 단계에서 발행용 크리덴셜을 아예 불러오지 않습니다. 그러면 최악의 경우에도 "초안 생성 실패 + 알림"으로 끝나고, 잘못된 글이 발행되지는 않습니다.
[예약 시각] draft 단계 → 초안 + 알림 (발행 키 없음)
[사람] approve → 미리보기
[사람] approve --yes → 실제 발행 (여기서만 키 로드)
MCP 스펙도 신뢰와 안전을 위해 도구 호출을 거부할 수 있는 사람이 항상 루프 안에 있어야 한다고(SHOULD) 권고합니다.
원칙 ③ 비용은 구독 안에서 닫습니다
1인 회사에서 자동화 비용이 사용량에 따라 늘어나면 언젠가 꺼 버리게 됩니다. 저는 예약 잡도 이미 쓰고 있는 코딩 에이전트 CLI를 헤드리스로 실행하는 방식으로 돌리고, 정해진 구독 한도 안에서 끝나게 합니다. 기준은 단순합니다.
- 결정론으로 할 수 있는 일(표 정리, 엔티티 목록 만들기)은 LLM 없이 스크립트로 처리합니다.
- 내용이 바뀌지 않았으면 LLM을 호출하지 않습니다(diff 게이트).
- 토큰을 많이 쓰는 리서치 잡은 주 1회로 제한합니다.
원칙 ④ 결과를 검증하고 나서 완료라고 말합니다
"성공" 로그가 찍혔다고 일이 끝난 것은 아닙니다. 위키 재생성 잡이 아무것도 바꾸지 않고 조용히 넘어간 적이 있었습니다. 내용 해시가 그대로라서 "변경 없음"으로 판단했는데, 사실은 프롬프트를 바꿨는데도 해시에 빌더 버전이 들어 있지 않아서 생긴 일이었습니다. 그 뒤로는 이렇게 합니다.
- 새 예약 잡은 처음 한 번은 수동으로 돌려 로그와 결과물을 직접 확인한 다음에 스케줄을 믿습니다.
- "완료"라고 보고하기 전에 결과물(페이지, 발행 URL, 테스트 출력)을 실제로 열어 봅니다.
원칙 ⑤ 절차는 스킬과 런북으로 남깁니다
같은 지시를 세 번째 붙여 넣고 있다면 그 지시는 문서로 남겨야 합니다. Claude Code의 스킬은 SKILL.md 하나로 절차를 담고, 쓸 때만 본문을 불러오기 때문에 평소에는 컨텍스트를 거의 차지하지 않습니다. 저는 위키 동기화, 앱 릴리스 점검, 현장 테스트 준비 같은 절차를 모두 스킬로 만들어 두었습니다. 덕분에 몇 달 만에 하는 일도 "지난번에 어떻게 했더라"에서 시작하지 않습니다. 저장소 단위 규칙(언어, 커밋 규칙, 금지 사항)은 CLAUDE.md에 둡니다.
이 글의 구성
이어지는 섹션은 다음 순서로 진행합니다.
- 회사 브레인: 1차 자료를 LLM 위키로 정리하는 방법
- 스킬: 반복 절차를 SKILL.md로 남기는 요령
- 예약 초안: 사람 승인을 전제로 한 반자동 생성
- MCP: 사내 도구를 에이전트에 안전하게 연결하는 방법
- 발행 파이프라인: 초안에서 공개 글이 되기까지
- 검증 가드레일: 거짓 "성공"을 걸러내는 장치
각 섹션은 따로 읽어도 되지만, 막히는 부분이 있으면 이 다섯 원칙으로 돌아와 판단하시면 됩니다.
회사 브레인 — 1차 자료를 LLM 위키로
핵심 요점
- raw(불변 원본)·wiki(LLM 생성)·CLAUDE.md(스키마) 3계층에 log.md를 더하고, 코드는 복사하지 않고 읽기 전용으로 참조합니다.
- ingest·query·lint 세 워크플로를 돌리고, 좋은 답변은 새 위키 페이지로 저장해 쌓습니다.
- frontmatter의 confidence·status·updated·sources와 인라인 '미확인' 표기로 신뢰도와 신선도를 관리합니다. sources를 채울 수 없는 페이지는 만들지 않습니다.
- repo는 README → manifest → entrypoint·API·config 순으로 읽는 docs-first + code-aware 방식으로 증분 동기화합니다. 문서와 코드가 다르면 코드를 따릅니다.
- 검색은 qmd 하이브리드(BM25+벡터+리랭크, 온디바이스)를 쓰고 grep 폴백을 남깁니다. update 후 embed, 조사를 뗀 한국어 질의, 검색 라운드트립 확인이 필요합니다.
- 답하는 에이전트는 위키를 읽기만 하고, 쓰기는 원본 추출 파이프라인과 사람의 교정으로만 해서 할루시네이션이 지식베이스에 들어가지 않게 합니다.
왜 '회사 브레인'이 필요한가
직원이 없는 회사에서는 결정 이유, 계약 조건, 코드 구조, 특허 진행 상황이 모두 대표 한 사람의 머릿속과 흩어진 파일에만 있습니다. 에이전트에게 일을 맡기면 매번 처음부터 다시 찾아 읽게 되고, 근거가 없을 때는 그럴듯하게 지어냅니다. 실제로 장부 데이터가 있는데도 "장부가 활성화되지 않았다"고 추론하는 브리핑 오류를 겪었습니다. 이런 문제는 기능을 더 붙여서가 아니라 근거를 붙여서만 해결됩니다.
그래서 Andrej Karpathy가 공개한 LLM Wiki 패턴을 회사용으로 고쳐 씁니다. 질문할 때마다 원문 조각을 검색하는 RAG와 달리, LLM이 계속 유지하는 마크다운 위키를 쌓아 가는 방식입니다.
3계층 구조
| 계층 | 내용 | 누가 쓰나 |
|---|---|---|
raw/ |
원본 소스(회의록, 외부 자료, 프로젝트 문서, 이미지). 불변 | 사람이 넣고, LLM은 읽기만 |
wiki/ |
요약·엔티티·개념·아키텍처·기회 페이지, index.md, overview.md |
LLM이 생성하고 유지 |
스키마(CLAUDE.md) |
폴더 규칙, frontmatter, 워크플로, 금지 사항 | 사람이 정하고 LLM이 따름 |
여기에 연대기 로그 log.md를 하나 더 둡니다. ## [날짜] ingest | 제목처럼 접두어를 일정하게 붙이면 grep 한 줄로 이력을 추적할 수 있습니다.
코드 repo는 위키 안으로 복사하지 않습니다. 스키마에 "읽기 전용 참조 경로"로만 적어 두고, 위키에는 해석한 결과만 남깁니다.
세 가지 워크플로
- Ingest(소스 → 위키): 소스를 읽고, 핵심 내용을 사람과 짧게 논의한 뒤,
sources/에 요약 페이지를 쓰고 관련 프로젝트·개념 페이지를 고치거나 새로 만들어 서로 링크합니다. 마지막으로index.md와log.md를 갱신하고 git에 커밋합니다. push는 사람이 확인한 뒤에만 합니다. - Query(질문 → 답변): 항상
index.md부터 읽고 관련 페이지로 들어가 인용과 함께 답합니다. 좋은 답변은 채팅에서 끝내지 않고 새 페이지로 저장합니다. 비교표나 의사결정 분석이 그렇게 위키에 쌓입니다. - Lint(건강검진): 페이지 사이의 모순, 낡은 주장, 다른 페이지가 링크하지 않는 orphan 페이지, 페이지가 없는 중요 개념, 빠진 상호 링크를 점검하고 결과를
log.md에 남깁니다.
신뢰도와 신선도를 frontmatter로 관리하기
모든 페이지 머리에 다음 항목을 넣습니다.
type: project # person|project|concept|architecture|opportunity|source
status: active # active|draft|hypothesis|archived
confidence: high # high=코드·1차 자료 확인, medium=2차 자료·추정, low=가설
updated: 2026-10-26
sources: ["[[src-some-repo]]"]
운영 규칙은 세 가지입니다.
- 페이지 전체의 confidence와 별개로, 검증하지 않은 개별 주장에는 본문에
미확인이라고 적습니다. - 새 소스가 기존 주장을 대체하면 낡은 주장을 지우거나 "(구)… → (신)…"으로 표시하고,
updated:를 고치고, 근거 소스를 링크합니다. - 자동 삭제는 하지 않습니다. Dataview 같은 쿼리로 "오래됐거나 low인 페이지" 목록을 만들어 두고 사람이 주기적으로 훑어봅니다.
sources:를 채울 수 없는 페이지는 아예 만들지 않는 것이 가장 효과적인 방어선입니다.
코드 repo 동기화: docs-first + code-aware
README만 믿으면 낡은 설명이 위키에 그대로 굳습니다. 그래서 다음 순서로 읽습니다.
- README와 docs로 의도를 파악합니다.
- manifest(
package.json,pyproject.toml, ROSpackage.xml)로 실제 의존성과 버전을 확인합니다. - entrypoint, API·스키마,
docker-compose·config, 서비스 사이의 계약을 확인합니다. - 문서와 코드가 다르면 코드를 따르고, 차이를 페이지에 적습니다.
동기화는 매번 전체를 다시 만들지 않습니다. 커밋 이후 바뀐 repo만 골라 반영하는 증분 방식이며, 이 절차는 하나의 스킬로 묶어 반복합니다.
검색: 하이브리드를 쓰되 grep 폴백을 남긴다
페이지가 적을 때는 index.md만으로 충분합니다. 수백 개가 넘어가면 검색 스크립트를 하나 둡니다.
# search.sh <질의>
if command -v qmd >/dev/null; then
qmd query "$Q" -c my-wiki --files # BM25 + 벡터 + 리랭크
else
grep -rniE "^(title|aliases):.*$Q" wiki -l # 제목·별칭 먼저
grep -rniE "$Q" wiki | head -20 # 그다음 본문
fi
qmd는 SQLite FTS5 기반 BM25와 벡터 검색을 RRF로 합치고, 로컬 GGUF 모델로 리랭크합니다. 모든 처리가 기기 안에서 끝나므로 회사 자료가 외부로 나가지 않습니다. CLI와 MCP 서버를 모두 제공해 에이전트에 검색 도구로 바로 붙일 수 있습니다. 운영하면서 알게 된 주의점은 다음과 같습니다.
- 문서를 추가한 뒤에는
qmd update를 먼저 돌리고qmd embed를 돌립니다. embed만 돌리면 변경된 파일을 다시 스캔하지 않습니다. - 색인은 됐는데 임베딩이 아직 없는 문서가 생길 수 있습니다. 이 경우 벡터 검색은 그 문서를 보지 못합니다.
- 한국어 키워드 검색은 조사를 뗀 명사형으로 질의합니다. 조사가 붙으면 BM25 결과가 0건이 되기도 합니다.
- 넣은 문서가 실제로 검색에 걸리는지 질의를 한 번 던져 확인합니다. 색인이 비어 있는데도 도구가 정상처럼 응답한 적이 있습니다.
그라운딩: 에이전트는 위키를 근거로만 답한다
- 이중 주입: 짧은
overview는 시스템 프롬프트에 항상 넣고, 나머지는 검색 도구로 필요할 때만 꺼냅니다. 스니펫이 잘리면 같은 검색을 반복하지 말고 전문을 열도록 도구 설명에 적어 둡니다. - confidence 노출: 검색 결과에 근거의 등급을 함께 돌려줍니다. 세무 신고서나 장부는 high, 소개 자료는 medium으로 표시해서 모델이 근거의 무게를 알고 답하게 합니다.
- 답하는 에이전트는 읽기만 합니다. 답변하던 에이전트가 위키를 직접 고치면 할루시네이션이 지식베이스에 들어가 다음 답변을 오염시키는 자기 강화 루프가 생깁니다. 쓰기 경로는 두 가지로 제한합니다. 원본 문서에서 추출하는 파이프라인, 그리고 사람의 교정입니다. 자동 스캔 결과도 사람의 승인 없이는
wiki/에 들어가지 않습니다. - 근거가 없으면 없다고 답합니다. 시크릿 필터처럼 분류가 애매한 파일은 유출보다 누락을 택해 제외합니다.
처음 만드는 순서
- 빈 git 저장소에
raw/,wiki/,log.md,CLAUDE.md를 만듭니다. CLAUDE.md에 언어, 폴더 역할, frontmatter, ingest·query·lint 절차, "raw는 수정 금지, 외부 push는 확인 후"를 적습니다.- 가장 자주 묻는 주제의 소스 3~5개로 ingest를 손으로 한 번 돌리면서 스키마의 빈틈을 고칩니다.
index.md를 frontmatter 기반 동적 표로 만들고, 재검증 뷰를 추가합니다.- 페이지가 늘면 검색 스크립트를 두고, 가능하면 qmd를 붙입니다.
- 코드 repo가 있으면 증분 동기화 절차를 스킬로 만듭니다.
- 2~4주마다 lint를 돌리고, 좋은 답변이 위키로 돌아오고 있는지
log.md로 확인합니다.
처음부터 벡터 DB나 UI에 투자하지 않는 것이 좋습니다. 먼저 "근거를 붙이면 답변이 실제로 좋아지는가"만 얇게 검증하고, 효과가 확인되면 넓혀 가는 편이 1인 회사에 맞습니다.
기억과 스킬 — 같은 설명을 두 번 하지 않기
핵심 요점
- 반복되는 설명은 두 종류로 나눕니다. 바뀔 수 있는 한 줄짜리 사실은 파일 메모리에, 순서가 있는 절차는 스킬 런북에 둡니다.
- MEMORY.md는 한 줄짜리 인덱스로만 씁니다. 세션 시작 시 앞 200줄 또는 25KB만 로드되므로 사실 1개당 파일 1개로 분리하고, feedback 기억에는 Why와 How to apply를 적습니다.
- 장기기억 중복 제거가 새 사실을 버리면 안 됩니다. 재확인·갱신(supersede)·새 사실의 3분류로 판정하고, 판정이 실패하면 새 기억으로 저장합니다.
- SKILL.md는 언제 쓰나(description)·절차·검증·gotcha 네 칸으로 쓰고 500줄 이하로 유지합니다. 평소에는 description만 컨텍스트에 올라갑니다.
- 스킬 큐레이션은 최근 30일 사용 기록으로 판단하고, 데이터 없음과 나쁨을 구분합니다. 정리 잡은 제안만 하고 반영은 사람이 합니다.
- 기억은 컨텍스트이지 강제 설정이 아닙니다. 반드시 막아야 하는 동작은 훅으로 강제하고, 실행 권한은 현재 사용자 요청에서만 나옵니다.
설명을 두 번 하게 되는 이유
코딩 에이전트는 세션마다 빈 컨텍스트에서 출발합니다. 그래서 "커밋 메시지 규칙은 이렇게 바뀌었어요", "이 업체 기기는 저 순서로 등록해요" 같은 말을 매번 다시 하게 됩니다. 1인 회사에서는 이 반복이 그대로 대표의 시간으로 빠져나갑니다. 저는 이 반복을 두 종류로 나눠서 처리합니다.
| 종류 | 예시 | 어디에 두나 |
|---|---|---|
| 사실(fact): 바뀔 수 있는 한 줄짜리 판단·선호 | "커밋에 공동작성자 표기를 붙여도 된다", "라이다 로봇은 로봇 쪽 내비게이션으로 주행한다" | 작은 파일 메모리 |
| 절차(procedure): 매번 같은 순서로 하는 여러 단계 | 파트너 온보딩, 현장 테스트 세팅, 앱 릴리스 점검, 블로그 발행 | 스킬(SKILL.md 런북) |
공식 문서도 같은 선으로 나눕니다. 늘 필요한 사실과 규약은 매 세션 로드되는 CLAUDE.md에 두고, 같은 체크리스트나 절차를 채팅에 계속 붙여 넣고 있다면 스킬로 만들라고 안내합니다.
작은 파일 메모리: 인덱스 1개 + 사실 1개 = 파일 1개
제가 쓰는 구조는 단순합니다.
memory/
├── MEMORY.md # 인덱스: 한 줄 = 링크 + 요약 + 날짜
├── commit-trailer-allowed.md # 사실 1개
└── robot-nav-role-split.md # 사실 1개
- MEMORY.md는 목차만 둡니다. Claude Code의 auto memory는 세션을 시작할 때 MEMORY.md의 앞 200줄 또는 25KB만 읽습니다. 본문을 인덱스에 쌓으면 뒤쪽은 조용히 잘려 나갑니다. 세부 내용은 주제 파일에 두고 필요할 때 읽게 합니다.
- 사실 하나에 파일 하나를 씁니다. 사실이 바뀌면 그 파일만 고치면 되고, 정정 이력도 파일 단위로 남습니다.
- feedback 유형에는 반드시 이유를 적습니다. 저는 아래 세 칸을 고정해 둡니다.
---
name: commit-trailer-allowed
description: 커밋에 공동작성자 trailer 허용 (선호 변경, 이전엔 금지)
type: feedback
---
대표가 허용으로 바꿨다. 이전 '금지' 규칙은 폐기.
**Why:** 사용자가 명시적으로 선호를 바꿨다.
**How to apply:** 모든 저장소 커밋에 붙인다. 하위 에이전트 지시에서 '금지' 문구를 뺀다.
이유를 적는 데는 실용적인 목적이 있습니다. 이유가 없으면 에이전트는 비슷해 보이는 다른 상황에도 그 규칙을 그대로 적용합니다. 이유가 있으면 규칙을 어디까지 적용할지 스스로 판단할 수 있습니다. 위 예시처럼 옛 규칙을 덮어쓴 경우에는 "이전 규칙 폐기"를 함께 적어야 옛 지시가 다시 살아나지 않습니다.
제품 쪽 장기기억: 추출 → supersede → 관련도 회상
제가 만드는 에이전트 제품에도 같은 원리를 자동화해 넣었습니다. 직접 만들면서 배운 점은 세 가지입니다.
- 중복 제거가 새 사실을 버리면 안 됩니다. 초기 구현은 단어가 80% 이상 겹치면 새로 뽑은 사실을 저장하지 않았습니다. 그래서 사용자가 이사하면 새 주소는 버려지고 옛 주소만 남았습니다. 판정을 임베딩 유사도 3분류로 바꿨습니다. 아주 비슷하면 재확인으로 보고 새 행 없이 신뢰도만 올립니다. 꽤 비슷하면 갱신으로 보고 옛 기억을
superseded로 표시한 뒤 새 기억을 저장합니다. 나머지는 새 사실로 저장합니다. - 비싼 판정은 프리필터 뒤에 둡니다. 이미 저장된 임베딩으로 같은 소유자·같은 카테고리 안에서 유사한 후보를 상위 몇 개만 고릅니다. 후보가 있을 때만 작은 LLM이 모순·보완·중복·무관 중 하나로 판정합니다.
- 판정이 실패하면 새 기억으로 저장합니다. 중복은 나중에 정리할 수 있지만 잃어버린 사실은 되찾을 수 없습니다.
함정도 하나 있었습니다. superseded는 수명주기 단계(active → warm → cold → archived)와 별개인 상태입니다. 검색이 허용 목록(active, warm)으로만 거르기 때문에, 수명주기 처리 코드가 이 행을 단계 안으로 되돌려 놓으면 낡은 사실이 회상 결과에 다시 나타납니다.
스킬: 반복 업무를 런북으로 굳히기
같은 설명을 세 번째로 하게 되면 그 업무를 스킬로 만듭니다. 제 SKILL.md는 모두 같은 뼈대를 씁니다.
| 칸 | 내용 |
|---|---|
| description(언제 쓰나) | 사용자가 실제로 쓰는 표현을 넣습니다. 예: "새 업체 온보딩", "릴리스 체크리스트", "폰이 로봇에 안 붙어요" |
| 절차 | 순서가 있는 단계. 예: 업체 등록 → 라이선스·권한 → 기기 등록 → 모델 배포 → 서비스 개시 |
| 검증 | 끝났다고 판단하는 근거. 예: 릴리스 후 정상 기기는 통과하고 미등록 서명은 차단되는지 확인 |
| gotcha | 실제로 겪은 함정과 WRONG/RIGHT 예시 |
제 스킬에 들어 있는 gotcha 몇 가지는 다음과 같습니다.
- 앱 릴리스: 디버그 서명에서 릴리스 서명으로 바뀌면 라이선스 검증이 전부 실패합니다. 빌드 후가 아니라 서명 키가 정해지는 시점에 등록합니다.
- 파트너 온보딩: 두 시스템이 "프로젝트"와 "키"를 각자 따로 관리합니다. 이름이 같다고 같은 것으로 보지 않도록 키 종류를 표로 정리해 둡니다. 설정할 대상은 이름으로 고르지 않고, 기기가 실제로 가리키는 레코드를 기준으로 고릅니다.
- 현장 테스트: "전부 실제로 밟은 것"만 함정 표에 올립니다.
- 블로그 발행: 공개 글은 회수할 수 없으므로 비밀 스캔과 사람 승인을 거친 뒤에만 올립니다.
크기는 공식 권장대로 SKILL.md 500줄 이하로 둡니다. 긴 참고 자료는 별도 파일로 분리합니다. 평소 컨텍스트에는 description만 올라가고, 본문은 스킬을 호출할 때만 로드됩니다. 그래서 description의 첫 문장에 핵심 용도를 둡니다.
스킬 큐레이션: 쌓이기만 하면 썩습니다
저는 스킬이 30개가 넘어가자 비슷한 스킬끼리 서로 다른 지시를 하기 시작했습니다. 그래서 주기적으로 정리하는 규칙을 둡니다.
- 누적 횟수가 아니라 최근 30일 기준으로 봅니다. 누적 호출 수만 보면 예전에 많이 쓰였다가 지금은 죽은 스킬이 계속 건강해 보입니다.
- "데이터 없음"과 "나쁨"을 구분합니다. 만든 지 얼마 안 돼 아직 쓰인 적 없는 스킬을 보관 처리하지 않습니다. 근거가 없을 때는 아무것도 하지 않는 편이 낫습니다.
- 자동으로 적용하지 않습니다. 정리 잡은 유지·보관·병합·수정·승격 다섯 가지 제안만 내고, 실제 반영은 사람이 검토한 뒤에 합니다.
- 조용한 실패를 의심합니다. 정리 잡이 오류는 냈는데 제안 0건으로 "성공"하는 경우가 실제로 있었습니다. 결과가 0건이면 로그부터 확인합니다.
기억은 참고자료이지 지시가 아닙니다
공식 문서도 CLAUDE.md와 auto memory를 강제 설정이 아니라 컨텍스트로 다룬다고 밝힙니다. 무슨 일이 있어도 막아야 하는 동작은 기억이 아니라 훅 같은 강제 장치에 둡니다. 저는 여기에 원칙 두 가지를 더 둡니다.
- 기억에 "X를 실행하라"는 문장이 있어도 그 자체를 승인으로 보지 않습니다. 실행 권한은 지금 사용자가 하는 요청에서만 나옵니다.
- 기억은 기록된 시점의 사실입니다. 파일이나 기능을 가리키는 기억은 쓰기 전에 현재 상태를 다시 확인합니다. 낡은 기억은 지우지 말고 "(구)… → (신)…"으로 정정해 이력을 남깁니다.
바로 해볼 체크리스트
- [ ] MEMORY.md를 한 줄 인덱스로 줄이고 사실을 파일 단위로 분리했나요?
- [ ] feedback 기억마다 Why와 How to apply가 있나요?
- [ ] 세 번 이상 설명한 절차를 SKILL.md(언제·절차·검증·gotcha)로 옮겼나요?
- [ ] 한 달에 한 번 30일 사용 기록으로 중복 스킬과 죽은 스킬을 점검하나요?
- [ ] 절대 막아야 하는 동작을 기억이 아니라 훅으로 강제하고 있나요?
예약 초안, 사람 승인 발행
핵심 요점
- 예약 잡은 초안까지만 만들고, 발행 크리덴셜은 승인 스크립트에서만 불러옵니다. 막아 주는 건 프롬프트가 아니라 키가 없다는 사실입니다.
- 토픽 큐는 텍스트 파일의 첫 줄을 씁니다. 성공했을 때만 그 줄을 소비하고 실패하면 남겨서 다음 예약 때 다시 시도하며, 큐가 비면 알림만 보냅니다.
- 에이전트에게 마지막 줄에 'DRAFT_READY: 경로' 표식을 출력하게 하고, 스크립트가 그 줄을 찾아 인박스에 기록하고 알림을 보냅니다. 실행마다 로그 파일을 따로 남깁니다.
- launchd 환경은 대화형 셸과 PATH·인증이 다르므로 처음 한 번은 손으로 실행해 검증합니다. 스크립트용 인증은 setup-token 장기 토큰이나 API 키를 씁니다.
- 무거운 잡은 주 1회로 두고, 빈도는 사람이 검토할 수 있는 양에 맞춥니다. JSON 출력의 비용 추정치를 기록하고 실행당 예산 상한을 겁니다.
구조 한눈에 보기
블로그·커뮤니티 글처럼 정기적으로 나가는 콘텐츠는 두 단계로 나눠 돌립니다.
| 단계 | 누가 | 언제 | 하는 일 | 가진 권한 |
|---|---|---|---|---|
| 초안 | OS 스케줄러 + 헤드리스 에이전트 | 정해진 요일·시각 | 큐에서 토픽 하나를 꺼내 리서치·작성·자체 검수까지 하고 초안 파일 저장 | 읽기/쓰기만, 발행 키 없음 |
| 승인 | 사람 | 알림을 받은 뒤 편할 때 | 미리보기 → 승인 명령으로 빌드·배포 | 발행 키 있음 |
저는 macOS의 launchd에 채널별로 잡을 하나씩 등록해 두었습니다. 예를 들어 마케팅 글은 주 2회, 투자 칼럼은 주 1회, 무거운 리서치 글은 주 1회 오전에 도는 식입니다. 잡이 하는 일은 셸 스크립트 하나를 부르는 것뿐이고, 그 스크립트 안에서 claude -p(Claude Code 비대화형 모드)를 실행합니다.
1. 스케줄러: launchd(또는 cron)
launchd plist에서 신경 쓸 부분은 세 가지입니다.
<key>StartCalendarInterval</key>
<array><dict>
<key>Weekday</key><integer>1</integer>
<key>Hour</key><integer>10</integer><key>Minute</key><integer>30</integer>
</dict></array>
<key>EnvironmentVariables</key><dict>
<key>PATH</key><string>/opt/homebrew/bin:/usr/bin:/bin</string>
</dict>
<key>StandardOutPath</key><string>…/logs/launchd.out</string>
<key>StandardErrorPath</key><string>…/logs/launchd.err</string>
- PATH를 직접 넣습니다. launchd는 로그인 셸 환경을 물려받지 않으니
claude,node같은 명령을 못 찾을 수 있습니다. 스크립트 안에서도 실행 파일은 절대경로로 부르는 편이 안전합니다. - stdout/stderr를 파일로 남깁니다. 잡이 아예 시작도 못 했을 때 볼 곳이 여기뿐입니다.
- 잠자기 동작을 알아 둡니다. launchd 매뉴얼에 따르면
StartCalendarInterval잡은 cron과 달리, 맥이 잠든 동안 놓친 실행을 깨어날 때 한 번으로 묶어서 실행합니다. 전원이 완전히 꺼져 있던 경우는 동작이 확실하지 않으니, "맥이 켜져 있어야 돈다"고 전제하고 운영하는 편이 낫습니다.
2. 토픽 큐: 텍스트 파일 한 줄 = 글 한 편
채널마다 큐 파일을 하나씩 둡니다.
# 주석과 빈 줄은 무시
스타트업 세무 자동화에서 사람이 꼭 봐야 하는 3가지
사내 위키를 LLM으로 유지할 때의 함정
스크립트 동작은 다음과 같습니다.
- 주석과 빈 줄을 뺀 첫 줄을 토픽으로 씁니다.
- 큐가 비어 있으면 알림만 보내고 정상 종료합니다. 쓸 거리가 없는 날 에이전트를 억지로 돌리지 않습니다.
- 초안이 성공했을 때만 그 줄을 큐에서 지우고
.done파일에 날짜와 함께 옮깁니다. 실패하면 토픽을 그대로 두고, 다음 예약 때 같은 토픽으로 다시 시도합니다.
토픽을 추가할 때는 echo "새 토픽" >> queue.txt 한 줄이면 됩니다. 소비 기록이 파일로 남아서 무엇이 언제 나갔는지 따로 관리할 필요도 없습니다.
3. 에이전트 프롬프트: "무인 실행"을 명시하고 끝 표식을 요구
예약 실행용 프롬프트에는 스킬 호출과 함께 규칙을 붙입니다.
/marketing-blog 토픽: '<큐에서 꺼낸 줄>'
이건 사람이 없는 예약 '초안' 실행이다.
1) 중간 승인 단계는 스스로 승인하고, 사용자에게 질문하지 마라.
2) 빌드·배포·검색엔진 핑은 절대 실행하지 마라.
3) 산출물만 저장하고, 검수에 불합격하면 그대로 보고하라.
4) 마지막 줄에 정확히 'DRAFT_READY: <초안 경로>'를 출력하라.
스크립트는 종료 코드만 믿지 않고 로그에서 DRAFT_READY: 줄을 찾습니다. 이 줄이 있으면 인박스 파일에 시각 | 채널 | 토픽 | 초안 경로 한 줄을 추가하고 OS 알림을 띄웁니다. 줄이 없으면 실패 알림에 로그 파일 위치를 담아 보냅니다. 공식 문서도 인증 실패 같은 실행 중 오류는 stdout의 결과로 찍힌다고 설명하므로, 실행마다 타임스탬프가 붙은 로그 파일을 따로 남겨 두면 원인을 바로 찾을 수 있습니다.
4. 승인: 미리보기와 실행을 같은 명령으로
approve.sh marketing # 대기 초안, 토픽, 실행될 배포 명령만 출력
approve.sh marketing --yes # 실제 빌드·배포 → 인박스 줄을 PUBLISHED로 이동
미리보기와 실행이 같은 코드 경로를 쓰기 때문에, 화면에 보인 명령과 실제로 실행되는 명령이 같다는 점을 믿을 수 있습니다. 발행 크리덴셜(API 키, 배포 계정)은 이 승인 스크립트에서만 불러옵니다.
왜 완전 자동 발행을 하지 않는가
- 최악의 결과를 줄이기 위해서입니다. 초안 단계에 발행 키가 없으면, 예를 들어 커뮤니티 발행 스크립트는 인증에서 막혀 물리적으로 실행될 수 없습니다. 프롬프트로 "배포하지 마"라고 적는 건 보조 장치이고, 실제로 막는 건 키가 없다는 사실입니다. 그래서 예약 실행이 잘못되면 "초안 실패 + 알림"으로 끝나고, 잘못된 글이 공개되는 일은 생기지 않습니다.
- 공개 글은 되돌리기 어렵습니다. 검색엔진 색인, 구독자 알림, 컴플라이언스(투자 글의 표현 규정 등)는 지운다고 사라지지 않습니다. 반대로 사람이 쓰는 시간은 하루 1~2분 미리보기면 충분합니다.
- 리스크가 낮고 형식이 고정된 산출물(예: 매일 나가는 플레이리스트)은 완전 자동으로 돌리기도 합니다. 기준은 틀렸을 때 되돌리는 비용입니다.
권한은 한 겹 더 줄일 수 있습니다. Claude Code는 --allowedTools, --permission-mode, 무인 실행용 --permission-prompts none 같은 옵션을 제공하므로, 모든 권한 확인을 끄는 플래그보다 필요한 도구만 허용하는 쪽을 권합니다.
인증이 스케줄러에서 안 풀릴 때
- 터미널에서는 잘 돌던
claude -p가 launchd 아래에서 인증 실패로 끝나는 경우가 있습니다. 환경 변수, 키체인 접근, PATH가 대화형 셸과 다르기 때문입니다. 처음 한 번은 스크립트를 손으로 실행해서 로그와 인박스를 확인한 뒤에 예약을 믿으세요.--dry옵션으로 실행될 프롬프트와 명령만 출력해 보는 것도 도움이 됩니다. - 공식 인증 문서는 스크립트용으로
claude setup-token이 발급하는 장기 토큰(CLAUDE_CODE_OAUTH_TOKEN)이나ANTHROPIC_API_KEY를 안내합니다.--bare모드는 OAuth와 키체인을 읽지 않으므로 API 키 방식이 필요합니다. - 그래도 안 풀리면 생성은 대화형으로 돌리고, 큐·인박스·승인 구조만 그대로 유지해도 충분히 쓸모가 있습니다.
키 로테이션 체크리스트
- [ ] 발행 키는 승인 스크립트가 읽는 로컬 env 파일에만 두고, 저장소에는 커밋하지 않습니다(
.gitignore). - [ ] 키가 채팅·로그·스크린샷에 한 번이라도 노출되면 바로 재발급합니다. 노출이 없어도 주기적으로 교체합니다.
- [ ] 초안 러너가 그 env 파일을
source하지 않는지 확인합니다. 이게 이 구조 전체를 지탱하는 전제입니다.
비용과 빈도
멀티에이전트 리서치처럼 한 번에 토큰을 많이 쓰는 잡은 주 1회로 묶고, 가벼운 글은 주 2회 정도로 둡니다. 빈도는 "사람이 검토할 수 있는 양"에 맞추는 것이 좋습니다. 승인 대기 초안이 쌓이기 시작하면 빈도를 줄이라는 신호입니다. --output-format json을 쓰면 실행마다 total_cost_usd 추정치를 받을 수 있으니 로그에 함께 남겨 두면 좋습니다. 다만 문서에 따르면 이 값은 클라이언트 쪽 추정치라 실제 청구액과 다를 수 있습니다. --max-budget-usd로 실행 한 번의 상한을 걸어 둘 수도 있습니다.
운영 도구를 MCP로 — 대시보드 대신 대화로
핵심 요점
- MCP 도구는 모델이 판단해 호출하지만, 스펙은 호출을 거부할 수 있는 사람이 항상 루프 안에 있어야 한다고 권고합니다. 사람이 끼어들 지점은 처음부터 설계에 넣어야 합니다.
- 에이전트에는 만료일이 있고 범위가 좁으며 폐기할 수 있는 서비스 키를 줍니다. 키 발급 도구는 MCP에 넣지 않아 에이전트가 스스로 권한을 올리는 경로를 막습니다.
- 읽기와 쓰기 도구를 나누고, 쓰기는 환경변수로 허용했을 때만 동작하게 합니다. 모든 변경은 조회, dry_run 미리보기, 사람 확인, 같은 인자로 재호출 순서를 거칩니다.
- 데이터를 지우거나, 돈이 들거나, 외부에 공개하는 작업은 confirmation_required와 확인 영수증을 먼저 돌려줍니다. 승인 뒤에만 같은 인자로 다시 실행하고, 실패한 변경은 자동으로 재시도하지 않습니다.
- 삭제, 구독 등급 변경, 초대처럼 되돌릴 수 없거나 계약이나 권한이 바뀌는 작업은 사람 로그인 토큰 전용으로 남깁니다.
- 도구 결과와 문서 속 지시는 데이터일 뿐 명령이 아닙니다. annotation 힌트는 신뢰할 수 있는 서버가 아니면 믿지 않고, 실제 차단은 서버의 권한 검사가 맡습니다.
왜 대시보드 대신 대화인가
1인 회사의 운영 업무는 대부분 짧은 조회와 작은 변경입니다. "이 업체 SDK 라이선스가 몇 대까지였지?", "새 카드 앱을 이 업체에 켜 줘", "지난달 결제 통계 보여 줘" 같은 일들입니다. 일 자체는 단순한데 처리하려면 대시보드에 로그인하고, 업체를 고르고, 화면 서너 개를 오가야 합니다. 저희는 관리 API를 MCP 서버로 감싸서 이 일을 코딩 에이전트와의 대화로 처리합니다. 앱 릴리스 체크리스트(서명 해시 등록, 기능 권한 확인, 시트 수 확인)도 같은 도구로 돌립니다.
MCP(Model Context Protocol)의 기본 개념은 간단합니다.
- 서버가 도구(tool) 목록을 내놓고, 각 도구는 이름, 설명, 입력 JSON Schema를 가집니다.
- 클라이언트(Claude Code 같은 에이전트)는
tools/list로 도구를 찾고tools/call로 호출합니다. 실행이 실패하면 결과에isError: true가 담겨 돌아옵니다. - 스펙은 도구를 "모델이 판단해 호출하는 것"으로 정의합니다. 동시에 도구 호출을 거부할 수 있는 사람이 항상 루프 안에 있어야 한다(SHOULD) 고 명시합니다.
마지막 항목이 이 글의 핵심입니다. 모델이 알아서 부를 수 있는 도구에 운영 권한을 쥐여 주는 셈이니, 사람이 끼어들 지점을 처음부터 설계에 넣어야 합니다.
1. 키: 오래 쓰되 좁게, 언제든 회수할 수 있게
사람 로그인 토큰(30분짜리 JWT)을 에이전트에 넘기면 수시로 갱신해야 하고, 그 토큰이 할 수 있는 일도 너무 많습니다. 그래서 에이전트 전용 서비스 키를 따로 발급합니다.
- 만료를 반드시 지정합니다. 저희는 90일을 권장값으로 둡니다. 무기한 키는 만들지 않습니다.
- 원문 키는 발급 응답에서 한 번만 보여 줍니다. 키는 저장소에 평문으로 두지 않고 셸 환경변수로만 주입합니다.
- 발급은 MFA를 켠 관리자 계정만 할 수 있습니다.
- 키 발급 도구는 MCP에 넣지 않습니다. 조회와 폐기(revoke) 도구만 둡니다. 에이전트가 스스로 더 센 키를 만들어 권한을 올리는 경로를 막기 위해서입니다.
- 키의 종류마다 할 수 있는 일을 서버가 나눕니다. 저희는 운영자 키, 기기 키, 테넌트 범위 키를 구분합니다. 기기나 테넌트에 묶인 키로 업체 정보를 바꾸려 하면 역할과 관계없이 403을 돌려줍니다. 기기에서 키가 유출되더라도 그 키로 다른 업체의 설정을 바꿀 수 없게 하려는 것입니다.
MCP 보안 모범사례 문서도 같은 방향을 권합니다. 처음에는 읽기 위주의 최소 권한만 주고, 권한이 필요한 작업이 실제로 나올 때 단계적으로 올리라는 것입니다. *이나 full-access 같은 포괄 권한은 피해야 할 실수로 꼽힙니다.
2. 읽기와 쓰기를 나누고, 쓰기는 기본으로 닫아 둡니다
도구를 만들 때 하나하나 읽기용인지 쓰기용인지 구분해 둡니다.
| 영역 | 읽기 (안전) | 쓰기 (위험, 미리보기 기본) |
|---|---|---|
| 업체·조직 | list / get | create / update |
| 기기 | list / get | provision / deactivate |
| 라이선스·권한 | get / events | set / issue / set_status |
| 통계·결제 | 전부 읽기 | 쓰기 도구 없음 |
쓰기 도구는 환경변수로 명시적으로 허용했을 때만 동작합니다. 플래그가 없으면 서버는 읽기 전용으로 뜹니다.
export OPS_API_TOKEN=... # 서비스 키 (만료일 있음)
export OPS_API_ALLOW_WRITE=1 # 없으면 읽기 전용
통계나 결제처럼 읽기만 필요한 서버는 아예 따로 띄우는 편이 낫습니다. 외부 문서나 웹 페이지를 읽어 오는 도구와 쓰기 권한이 있는 도구가 같은 세션에 함께 있으면, 오염된 내용이 그대로 쓰기 호출로 이어질 수 있기 때문입니다.
3. 변경은 "조회, 미리보기, 확인, 같은 인자로 재호출" 순서로
저희 운영 런북에서 모든 쓰기는 다음 네 단계를 거칩니다.
*_get으로 현재 값을 확인합니다.*_set(..., dry_run=true)로 바뀔 내용을 미리 보여 줍니다.- 사람이 확인합니다.
- 같은 인자에
dry_run=false만 바꿔 다시 호출합니다.
1단계를 빼먹기 쉽지만 가장 중요합니다. 설정 목록을 통째로 교체하는 도구가 있기 때문입니다. 현재 값을 보지 않고 "카드 하나 추가"를 실행하면 기존에 켜져 있던 카드가 전부 사라집니다.
사내의 다른 MCP 서버에서는 이 흐름을 서버 쪽 규약으로 굳혔습니다. 데이터를 지우는 작업, 접근 권한을 바꾸는 작업, 돈이 드는 작업, 외부에 공개하는 작업은 먼저 confirmation_required와 확인 영수증을 돌려줍니다. 에이전트는 바뀔 내용을 사람에게 그대로 보여 주고, 승인을 받은 뒤에만 같은 인자에 영수증을 붙여 다시 호출합니다. 인자를 하나라도 바꾸면 처음부터 다시 확인을 받아야 합니다. 실패한 변경은 자동으로 재시도하지 않습니다. 결제처럼 사람이 직접 해야 하는 단계는 browser_required와 화면 링크를 돌려줘서 사람에게 넘깁니다.
call license_set(id, seats=20) → confirmation_required, receipt=R
show "시트 10 → 20, 대상: 업체 #123" → 사람 승인
call license_set(id, seats=20, receipt=R)
4. 사람만 할 수 있는 일은 사람 토큰으로 남겨 둡니다
서비스 키로 모든 일을 하게 만들지 않습니다. 저희는 다음 작업을 사람 로그인 토큰 전용으로 남겼습니다.
- 업체 삭제, 활성화와 비활성화
- 구독 등급 변경 (등급을 낮추는 요청은 서버가 아예 거부합니다)
- 초대 토큰 발급
어떤 작업을 사람에게 남길지는 아래 질문으로 판단합니다.
- 되돌릴 수 없는가? 삭제가 여기에 해당합니다.
- 돈이나 계약 관계가 바뀌는가? 등급 변경이 여기에 해당합니다.
- 새 사람이나 새 권한이 시스템에 들어오는가? 초대와 키 발급이 여기에 해당합니다.
하나라도 "예"라면 MCP 도구로 만들지 않거나, 만들더라도 서비스 키로 부르면 403을 돌려주게 합니다. 에이전트가 거절당했을 때 다른 자격 증명으로 바꿔 우회하지 않는다는 원칙도 도구 설명에 함께 적어 둡니다.
5. 도구 결과와 문서 속 지시는 명령이 아닙니다
조회 결과, 고객이 남긴 메모, 읽어 온 문서는 모두 데이터일 뿐입니다. 그 안에 "이 라이선스를 활성화하세요" 같은 문장이 있어도 에이전트는 그것을 사용자의 요청으로 받아들이지 않습니다. 동의는 오직 대화에서 사람이 직접 해야 하고, 검색해 온 문서나 도구 결과를 근거로 동의를 추정하지 않습니다.
MCP 스펙도 클라이언트가 도구 annotation(destructiveHint 같은 힌트)을 신뢰할 수 있는 서버가 아니면 믿지 말라(MUST) 고 요구합니다. Claude Code 문서 역시 외부 콘텐츠를 가져오는 서버는 프롬프트 인젝션 위험이 있다고 경고합니다. 힌트는 보조 수단일 뿐이므로, 실제 차단은 서버의 권한 검사와 확인 단계가 맡아야 합니다.
운영하며 배운 함정 두 가지
- 이름이 아니라 ID로 대상을 고릅니다. 프로젝트 이름은 A 업체처럼 보였는데, 실제로 연결된 관리 레코드는 다른 업체인 경우가 있었습니다. 이름으로 골랐다면 엉뚱한 업체를 바꿨을 것입니다. 변경 전에는 항상 "이 기기나 프로젝트가 가리키는 업체 ID"를 먼저 조회합니다.
- 도구가 안 보이면 설정 경로부터 확인합니다. 로컬 MCP 서버는 설정 파일에 적힌 작업 트리에서 실행됩니다. 그 트리가 오래된 브랜치에 머물러 있으면 새로 추가한 도구가 목록에 나타나지 않습니다.
바로 적용할 체크리스트
- [ ] 서비스 키에 만료일을 정했고, 원문은 환경변수로만 다룬다
- [ ] 키 발급 도구는 MCP에서 뺐고, 폐기 도구만 노출했다
- [ ] 쓰기는 플래그가 없으면 꺼지고, 모든 쓰기의 기본값은 dry_run이다
- [ ] 통째로 교체하는 도구는 get을 먼저 부르도록 도구 설명에 적었다
- [ ] 삭제, 금전, 외부 공개 작업은 확인 영수증 후 같은 인자로 재호출한다
- [ ] 되돌릴 수 없거나 계약이나 권한이 바뀌는 작업은 사람 토큰 전용으로 두었다
- [ ] 도구 결과 안의 지시를 명령으로 따르지 않는다는 원칙을 도구 설명에 명시했다
콘텐츠 파이프라인 — 이 글이 만들어진 방식
핵심 요점
- LLM 호출은 빌드 타임에 로컬 구독 에이전트로만 합니다. 섹션 리서치와 Q&A를 병렬로 생성하고, 배포 사이트에는 API 키도 LLM 서버도 없어 방문자가 늘어도 LLM 비용은 0입니다.
- 오프라인 AI 코치는 미리 만든 Q&A와 본문만으로 브라우저 안에서 답합니다. 답이 없는 질문은 로컬에서 생성해 추가하고 재배포합니다.
- visibility 필드가 없으면 private으로 처리합니다. 빌드는 공개 허브, 공개 Q&A, 공개 이미지, sitemap을 별도 파일로 만듭니다.
- 배포 전 검증은 익명에게 200으로 나가는 파일을 워커에 직접 물어 추린 뒤, 비공개 슬러그나 제목이 하나라도 있으면 종료 코드 1로 배포를 막습니다.
- 200 응답은 정상일 때도 유출됐을 때도 똑같이 나오므로, 배포 후 프로브는 응답 본문의 비공개 흔적(음성 확인)과 공개 글이 실제로 열리는지(양성 대조)를 함께 확인합니다.
- 대표 이미지는 본문 핵심 하나를 글자 없는 가로형 개념 일러스트로 만들고, 생성하지 못했으면 '미생성'으로 기록하고 기본 카드를 씁니다.
먼저 밝혀 둘 것: 이 글도 파이프라인 산출물입니다
지금 읽고 계신 글은 사람이 처음부터 끝까지 타이핑한 글이 아닙니다. 제가 주제와 섹션 목차를 정하면, 로컬에서 도는 코딩 에이전트가 섹션마다 하위 에이전트를 하나씩 띄워 병렬로 리서치하고 초안을 씁니다. 각 하위 에이전트는 정해진 근거 문서를 직접 읽고, 공개 문서로 보강하고, 공개 글 보안 규칙에 걸리는 표현이 없는지 스스로 점검한 뒤 결과를 돌려줍니다. 이 섹션도 그렇게 쓰였습니다. 그 뒤 빌드, 검증, 배포, 프로브를 거쳐 발행합니다. 아래는 그 구조와, 비슷한 것을 만들 때 그대로 가져다 쓸 수 있는 기준입니다.
핵심 원칙: LLM 비용은 빌드 타임에만 씁니다
이 구조는 처음부터 하나의 질문에 맞춰 설계했습니다. "사이트에 방문자가 늘면 비용도 같이 늘어나는가?" 답이 '아니오'가 되도록 만들었습니다.
| 단계 | 어디서 도는가 | 비용 성격 |
|---|---|---|
| 섹션 리서치·집필 | 로컬 코딩 에이전트(하위 에이전트 병렬) | 이미 내고 있는 구독 안 |
| 예상 Q&A 생성 | 같은 로컬 에이전트 | 구독 안 |
| 사이트 서빙 | 정적 호스팅(Cloudflare Pages) | 런타임 LLM 호출 0 |
| 새 질문 답변 추가 | 로컬에서 다시 생성한 뒤 재배포 | 구독 안 |
배포된 사이트에는 LLM API 키가 없고, 모델을 부르는 프록시 서버도 없습니다. 실시간 채팅이 꼭 필요하면 엣지 함수로 API를 붙이는 선택지도 있지만, 그 순간 "방문 = 토큰 과금" 구조가 되므로 명시적으로 원할 때만 켜는 옵션으로 남겨 두었습니다. 방문자 인증을 위한 엣지 워커는 하나 있지만 LLM과는 무관하고, 정적 호스팅 무료 티어 범위에서 돕니다.
빌드 타임 생성: 순서와 산출물 형식
- 목차 설계: 주제를 8~14개 섹션으로 나누고, 섹션마다
id,title,scope(반드시 다룰 범위)를 구체적으로 적습니다. scope가 모호하면 섹션끼리 내용이 겹칩니다. - 섹션 병렬 생성: 섹션당 에이전트 하나가 아래 형식으로 결과를 돌려줍니다.
{ "id": "...", "title": "...", "summary": "...", "markdown": "...", "key_points": ["..."], "sources": [{"title": "...", "url": "..."}] } - Q&A 팩 생성: 섹션 파일을 에이전트가 직접 읽고 섹션당 5개 안팎의 예상 질문을 만듭니다. 형식은
{q, a, keywords, section_id}입니다. - 빌드: 섹션과 Q&A를 HTML 하나에 인라인으로 넣습니다. 외부에서 불러오는 것은 마크다운 렌더러와 새니타이저 정도입니다.
실제로 겪은 함정이 하나 있습니다. 오케스트레이션 스크립트에는 파일시스템 접근이 없지만, 그 스크립트가 띄운 에이전트는 파일을 읽을 수 있습니다. 그래서 목차와 경로는 실행 인자로 넘기지 않고 스크립트 상수에 박아 넣은 뒤 실행합니다. 인자 전달에 기대면 조용히 빈 값으로 도는 일이 생깁니다.
오프라인 AI 코치
사이드바의 'AI 코치'는 브라우저 안에서만 동작합니다. 질문이 들어오면 미리 만들어 둔 Q&A의 질문·키워드와 본문을 대조해 가장 가까운 답을 보여줍니다. 맞는 답이 없으면 가까운 후보 몇 개와 함께 "이 질문은 아직 답이 없다"고 솔직하게 표시합니다. 그 질문은 제가 로컬에서 ask 명령으로 본문 근거 답변을 하나 생성해 Q&A에 추가하고 다시 배포합니다. 결과적으로 자주 묻는 질문일수록 정적 답변이 쌓이는 구조가 됩니다.
판단 기준은 이렇습니다. 독자 질문이 대부분 본문 범위 안에 있다면 오프라인 코치로 충분합니다. 본문 밖 질문까지 받아야 한다면 그때 런타임 API 비용을 감수할지 결정하시면 됩니다.
공개/멤버 티어: 기본값은 비공개입니다
블로그 하나에 공개 글과 멤버 전용 글이 함께 있습니다. 글마다 visibility를 적고, 값이 없으면 private으로 처리합니다. 누락이 곧 유출로 이어지지 않도록 안전한 쪽을 기본으로 둔 것입니다. 기준은 회사 기밀 여부입니다. 전략, 가격, 계약, 개인 재무는 비공개, 일반적으로 쓸모 있는 기술·시장 분석은 공개입니다. public으로 올리는 글은 발행 전에 사람이 한 번 더 확인합니다.
빌드는 결과물을 처음부터 분리해 만듭니다.
- 멤버용 전체 허브와 공개 글만 담은 허브를 별도 파일로 만듭니다.
- 코치 Q&A도 전체본과 공개본으로 나눕니다. 비로그인 코치는 공개본만 읽습니다.
- 대표 이미지도 공개 글 것만 따로 복사합니다. 이미지 파일명에 비공개 글 제목이 묻어나가는 것을 막기 위해서입니다.
- sitemap에는 공개 글만 올리고, 멤버 경로는 robots에서 Disallow합니다. 다만 robots.txt는 접근 차단 수단이 아니므로, 실제 차단은 엣지 워커가 로그인 쿠키를 검증해 처리합니다.
배포 전 격리 검증과 배포 후 실측 프로브
발행 절차는 다섯 단계이고, 어느 단계도 건너뛰지 않습니다.
1. 글 작성 (visibility 명시)
2. 빌드 (인증 워커 정본을 산출물로 복사)
3. 격리 검증 ← 실패하면 여기서 멈춤
4. 배포 (항상 산출물 전체)
5. 실측 프로브
3단계 검증 스크립트는 파일 목록을 추측하지 않습니다. 워커 로직에 직접 물어 **"익명 방문자에게 200으로 나가는 파일"**만 추립니다. 그리고 그 파일들 안에서 비공개 글의 슬러그와 제목을 찾습니다. 하나라도 나오면 종료 코드 1로 배포를 막습니다. 본문이 지나치게 짧은 글(서버 렌더 평문 500자 미만)도 여기서 걸러집니다. 생성이 중간에 끊긴 글을 내보내지 않기 위해서입니다.
5단계에서 상태 코드만 보지 않는 데는 이유가 있습니다. 공개 티어가 생긴 뒤로 비로그인 홈은 정상일 때도 200을 돌려주고, 유출이 일어나도 200을 돌려줍니다. 상태 코드로는 안전과 유출을 구별할 수 없습니다. 그래서 프로브는 라이브 응답 본문에 비공개 흔적이 없는지(음성 확인)와 공개 글이 실제로 열리는지(양성 대조)를 모두 확인합니다. 그리고 정적 호스팅은 배포마다 산출물 디렉터리를 통째로 교체하므로, 부분 배포는 하지 않습니다.
대표 이미지 원칙
- 본문을 먼저 읽고 핵심 주장 하나를 시각적 구상 하나로 옮깁니다. 주제와 상관없는 로봇·뇌·회로 장식은 쓰지 않습니다.
- 개념 일러스트가 실제 실험 사진이나 측정 결과처럼 보이게 만들지 않습니다.
- 기본은 글자 없는 가로형이고, OG 이미지를 겸하면 약 1.91:1로 만듭니다. 주 피사체는 썸네일에서도 읽히도록 중앙 안전 영역에 둡니다.
- 글을 쓴 에이전트와 이미지를 그린 모델은 서로 다릅니다. 이미지 비용은 텍스트 구독과 별개이므로 '무료'라고 쓰지 않습니다.
- 생성한 이미지는 직접 열어 피사체 오류, 원치 않는 글자, 크롭 상태를 확인합니다. 도구가 없거나 생성에 실패하면 프롬프트와 '미생성' 상태를 기록하고 기본 그라데이션 카드를 유지합니다. 만들지 않은 이미지를 만들었다고 보고하지 않습니다.
따라 하실 때 체크리스트
- [ ] 생성(LLM)과 서빙(정적)을 물리적으로 분리했는가
- [ ] 공개 여부 필드가 없을 때 비공개로 처리되는가
- [ ] 공개용 산출물(허브·Q&A·이미지·sitemap)을 별도 파일로 만드는가
- [ ] 검증이 "익명에게 실제로 나가는 파일"을 기준으로 하는가, 실패하면 배포를 막는가
- [ ] 배포 후 상태 코드가 아니라 응답 본문으로 확인하는가
검증과 가드레일 — 거짓 초록을 경계하라
핵심 요점
- 초록 CI·200 응답·기동 로그는 결과가 아니라 주장입니다. 응답 본문 프로브, 산출물 직접 열기, 고유 문구로 다시 검색하기처럼 '좋은 게 보이는지'를 기준으로 판정합니다.
- '완료' 보고는 문서에 명령어로 박아 둔 검증(빌드 → 격리 검증 → 배포 → 라이브 실측)의 출력을 근거로만 합니다. 확인하지 못한 것은 '미확인'으로 남깁니다.
- 무인 작업은 좁은 allow 목록과 deny 규칙으로 돌리고, 되돌리기 어려운 작업은 사람이 확인합니다. 권한 오류가 나면 권한을 넓히지 말고 알리게 합니다.
- 비밀번호는 키체인에서 클립보드로 바로 보내 화면과 대화 기록에 남기지 않습니다. 시크릿은 저장소 밖 실행 환경에 두고, 빈 값 폴백은 예외로 처리합니다.
- 대화형 작업은 구독으로, 제품 호출은 종량 API로 나눕니다. 멀티에이전트는 작업 복잡도에 맞춰 규모를 정합니다(채팅 대비 에이전트 약 4배, 멀티에이전트 약 15배 토큰).
- 모든 LLM 호출이 지나는 단일 게이트웨이에서 예산 확인과 귀속 기록을 하고, 우회는 import 검사 테스트로 막습니다.
신호가 없는 것보다 거짓 신호가 더 나쁩니다
침묵하는 실패는 언젠가 눈에 띕니다. 더 위험한 쪽은 "정상"이라고 적극적으로 보고하는 신호가 사실은 거짓인 경우입니다. 사람이 그 신호를 근거로 다음 결정을 내리기 때문입니다. "CI가 초록이니 머지한다", "헬스체크가 200이니 배포에 성공했다" 같은 식으로요. 저희 회사 위키에는 이런 사례만 모아 둔 페이지가 따로 있습니다. 실제로 겪은 유형을 일반화하면 아래와 같습니다.
| 겉으로 보인 신호 | 실제로 일어난 일 |
|---|---|
| 테스트 잡 초록 | continue-on-error나 || true가 실패를 가리고 있었습니다. 마스크를 걷어 보니 실패가 10건 넘게 쌓여 있었습니다 |
| CI 워크플로가 있음 | 트리거 브랜치 이름이 틀려서 한 번도 실행된 적이 없었습니다 |
| 워커 기동 성공 로그 | if not exists() 폴백이 매번 발동해서, 항상 mock 워커만 떠 있었습니다 |
/health 200, "model loaded" |
가중치 파일이 없어서 학습되지 않은 랜덤 가중치로 추론하고 있었습니다. 탐지 결과는 0건이었습니다 |
| 배포 헬스체크 전부 200 | 감시 목록에 기존 라우트만 들어 있었습니다. 새로 추가한 라우트는 404인 채로 "배포 성공" 판정을 받았습니다 |
| 감사 로그 기능 배포 완료 | 로거 설정 때문에 INFO 로그가 전부 버려져서 한 줄도 남지 않았습니다 |
| 백테스트 "종목 50개" | 날짜 필터가 빠져서 실제로는 한 종목을 50번 센 결과였습니다 |
공통 구조는 몇 가지로 정리됩니다. 폴백이 일어났다는 사실이 신호에 담기지 않습니다. 테스트용 물건(mock, 더미 값)이 기본 경로에 들어와 있습니다. 게이트가 존재하는 것을 게이트가 거른다는 뜻으로 착각합니다.
대처는 "신호 말고 결과를 직접 보는 것"입니다.
- 실측 프로브: 상태 코드만으로는 판정하지 않습니다. 저희 블로그는 비로그인 홈이 정상일 때도 200을 돌려주는데, 비공개 글이 새어 나가도 200입니다. 그래서 배포 후 검사는
curl상태 코드 대신 응답 본문을 봅니다. 비공개 제목이 노출되지 않았는지(음성 대조), 공개 글이 실제로 열리는지(양성 대조)를 둘 다 확인합니다. - 산출물 직접 열기: 뮤테이션 테스트가 전부 통과했는데도, 대시보드를 실제 데이터로 띄워 보고 나서야 열 하나가 늘 비어 있다는 걸 알게 된 적이 있습니다. 파이프라인 끝까지 한 번 밀어 보고(생성 → 등록 → 조회 → 화면) 사람이 눈으로 확인합니다.
- 검색에 실제로 걸리는지 확인: "색인 완료" 로그를 믿지 않습니다. 방금 넣은 문서에만 있는 고유한 문구로 직접 검색해서 그 문서가 나오는지 봅니다.
- 양성 신호로 판정: "에러가 안 보인다"는 판정 기준이 될 수 없습니다. 빈 grep 결과는 관측에 실패했다는 증거일 수도 있습니다. "기대한 행이 생겼다"처럼 무언가가 보이는 것을 기준으로 삼습니다. skip된 테스트도 초록으로 세지 않습니다.
어떤 신호를 근거로 쓰기 전에 한 번만 물어보면 됩니다. "실패했다면 이 줄이 안 찍혔을까?", "틀린 구현도 이 단언을 통과할 수 있지 않을까?"
"완료"라고 말하기 전에 검증 명령을 실행합니다
에이전트는 "고쳤습니다"라고 말하는 데 거리낌이 없습니다. 그래서 CLAUDE.md나 스킬 문서에 완료 조건을 명령어로 박아 둡니다.
# 발행 스킬의 완료 조건 (일반화)
build # 1. 빌드
verify_public # 2. 격리 검증 — 빨간불이면 배포 금지
deploy # 3. 배포
probe_live # 4. 라이브 실측 — 이 출력을 근거로 "완료" 보고
규칙은 세 가지입니다.
- 검증 출력을 직접 확인하기 전에는 성공이라고 말하지 않습니다.
- "N개 통과"를 적을 때는 어디서 돌린 결과인지(로컬/CI/운영)를 함께 적습니다.
- 결과를 확인하지 못했으면 성공도 실패도 아닌 미확인으로 남깁니다. 저희 위키는 운영 환경에 대한 주장을 실측 전까지
미확인으로 표기합니다. 커밋 메시지에 적힌 "서버에 런타임이 없다"를 그대로 옮겨 적었는데, 실측해 보니 다 갖춰져 있었던 일이 있었기 때문입니다.
권한, 비밀번호, 시크릿
권한 모드는 작업에 맞춰 고릅니다. Claude Code는 매번 묻는 기본 모드, 편집만 자동 승인하는 acceptEdits, 계획만 세우는 plan, 분류기가 위험한 동작을 막는 auto, 미리 허용한 것 외에는 거부하는 dontAsk, 그리고 모든 확인을 건너뛰는 bypassPermissions를 제공합니다. 마지막 모드는 공식 문서도 격리된 컨테이너나 VM에서만 쓰라고 권합니다. 예약 잡처럼 무인으로 도는 작업에는 dontAsk와 좁은 allow 목록을 함께 씁니다. 그리고 deny 규칙은 모든 모드에서 적용된다는 점을 활용합니다.
되돌리기 어려운 작업은 사람이 확인합니다. force push, 운영 재시작, 공개 발행, 삭제, 결제, 외부 메일이 여기에 해당합니다. 스킬 문서에 "이 스킬의 범위는 읽기까지다. 운영 변경이 필요하면 절차를 따르고 사용자 확인을 받는다"처럼 범위를 명시해 둡니다. 권한 오류가 나면 권한을 넓히지 말고 사람에게 알리라는 문장도 넣습니다.
비밀번호는 화면에 띄우지 않습니다. 운영 모니터링 대시보드를 여는 스킬은 이렇게 동작합니다. 공개 포트 없이 포트 포워딩으로만 붙습니다. 그 포워딩에는 세션 열기와 닫기만 가능한 최소 권한 프로필을 씁니다. 비밀번호는 키체인에서 꺼내 클립보드로 바로 보냅니다.
security find-generic-password -s <서비스명> -w | pbcopy
# 에이전트는 "비밀번호는 클립보드에 있습니다"까지만 말합니다
이렇게 하면 비밀번호가 대화 기록, 터미널 스크롤백, 스크린샷 어디에도 남지 않습니다. 키체인에 넣을 때도 명령 인수가 아니라 표준 입력으로 넣어서 프로세스 목록에 노출되지 않게 합니다.
시크릿은 저장소 밖에 둡니다. 크론 시크릿이나 배포 인증 같은 값은 실행 환경에 두고, README에는 변수 이름만 적습니다. 코드에서 SECRET || "" 같은 빈 값 폴백은 금지합니다. 시크릿이 없을 때 "빈 키로 정상 동작"해 버리기 때문입니다. 반드시 예외로 처리합니다. GitHub의 push protection 같은 시크릿 스캐닝을 마지막 그물로 켜 둡니다.
비용 가드레일
구독인지 종량 API인지 먼저 구분합니다. 사람이 대화형으로 쓰는 코딩 에이전트는 구독 한도 안에서 돌립니다. 저희가 배포하는 스킬 패키지도 사용자 자신의 Claude Code 세션이 직접 생성을 맡고, 서버에서는 데이터만 받습니다. 그래서 호출할 때마다 붙는 종량 과금이 없습니다. 반대로 제품 안에서 고객 요청을 처리하는 호출은 종량 API이므로 반드시 계측합니다. 참고로 Anthropic이 공개한 엔터프라이즈 평균은 개발자 1인당 활동일 기준 약 13달러입니다.
멀티에이전트 규모를 조절합니다. Anthropic의 리서치 시스템 글에 따르면 에이전트는 채팅의 약 4배, 멀티에이전트는 약 15배의 토큰을 씁니다. 같은 글은 규모 기준도 제시합니다. 단순 조회는 에이전트 1개, 비교 작업은 2~4개, 복잡한 리서치는 10개 이상입니다. Claude Code 문서도 agent teams가 plan mode에서 일반 세션의 약 7배를 쓴다고 안내합니다. 저희 기준은 이렇습니다.
- 서브에이전트에게는 장황한 출력(테스트 로그, 문서 수집)만 맡기고, 요약만 돌려받습니다.
- 단순 작업은 작은 모델에 맡깁니다.
- 끝난 에이전트는 바로 종료합니다.
LLM 게이트웨이로 모든 호출을 계측합니다. 저희 SDK에는 모든 provider 호출이 반드시 지나가는 단일 진입점이 있습니다. 이 진입점을 만들기 전에는 내부 호출 약 40곳이 계량되지 않고 있었습니다. 게다가 예산 가드는 인자 하나가 밀린 채 except: pass에 삼켜져서 한 번도 동작한 적이 없었습니다. 비용 장치 자체가 거짓 초록이었던 셈입니다. 지금 구조는 이렇습니다.
요청 경계에서 bind_call_context(user, agent, source) 1회
→ 예산 확인(초과면 토큰을 쓰기 전에 거절)
→ 레이트리밋 → 모델 라우팅 → 엔타이틀먼트(라우팅된 모델 기준)
→ provider 호출 → 비동기 계량 큐
귀속 정보가 없는 호출은 source="unattributed"로 기록 (기록 자체는 무조건)
이 진입점을 우회할 수 없게 만드는 것은 규칙 문서가 아니라 테스트입니다. 패키지 전체의 import를 파싱해서, provider 모듈을 직접 부르는 코드가 있으면 CI를 실패시킵니다. unattributed 비율이 높은 호출부가 다음에 라벨을 붙일 우선순위가 됩니다.
참고 출처
↗ Claude Code Docs — Manage costs effectively↗ Claude Code Docs — Permission modes↗ Claude Code Docs — Authentication (credential management)↗ Anthropic Engineering — How we built our multi-agent research system↗ GitHub Docs — Working with secret scanning and push protection↗ Scripting OS X — Get Password from Keychain in Shell Scripts오늘부터 시작하는 4주 로드맵
핵심 요점
- 순서는 규칙(스키마 파일·위키·메모리), 반복 업무 스킬 2개, 읽기 전용 MCP, 예약 초안과 승인 발행입니다. 앞 단계가 뒤 단계의 안전장치가 됩니다.
- 주마다 확인 가능한 완료 기준을 둡니다. 출처를 단 답변, 스킬 3회 연속 무수정 승인, 쓰기 시도 시 권한 오류, 승인된 초안 1편 발행입니다.
- MCP 도구의 readOnlyHint는 힌트일 뿐입니다. 실제로 막는 것은 읽기 범위로 제한한 서비스 키입니다.
- 예약 잡은 초안 생성과 발행을 나누고, 생성 단계 환경에는 발행 키를 넣지 않습니다. 이렇게 하면 최악의 결과가 '초안 생성 실패 + 알림'에 그칩니다.
- 절약 시간과 재작업률을 같이 봅니다. 재작업률이 높으면 자동화를 늘리지 말고 규칙부터 고칩니다.
시작하기 전에: 순서가 중요한 이유
AI 자동화를 처음 할 때 가장 흔한 실패는 4주차 일(예약 실행, 외부 발행)부터 손대는 것입니다. 에이전트가 회사 규칙을 모르는 상태로 무인 실행을 붙이면, 같은 실수를 사람이 매번 고쳐야 합니다. 아래 로드맵은 규칙 → 반복 업무 → 읽기 연결 → 예약 실행 순서로 쌓습니다. 앞 단계가 뒤 단계의 안전장치가 됩니다.
시작 전에 기록할 것 두 가지만 정합니다.
| 지표 | 정의 | 기록 방법 |
|---|---|---|
| 절약 시간 | (예전에 손으로 걸리던 시간 − 지금 검토·수정에 쓰는 시간) × 주간 횟수 | 작업마다 로그 한 줄에 "예전 n분 / 지금 m분" |
| 재작업률 | 에이전트 결과 중 사람이 다시 고치거나 버린 비율 | 승인 단계에서 "그대로 / 수정 / 폐기" 표시 |
절약 시간만 보면 착시가 생깁니다. 재작업률이 높으면 시간을 아낀 게 아니라 검토하는 일로 바뀐 것뿐입니다. 두 지표를 꼭 같이 보세요.
1주차: 스키마 파일과 작은 위키, 메모리 규칙
할 일
- 저장소(또는 문서 폴더) 루트에
CLAUDE.md같은 스키마 파일을 하나 만듭니다. 장황한 소개는 넣지 않고 규칙만 씁니다. 예를 들면 원본 폴더는 읽기만 하기, 위키 페이지 형식, 커밋은 하되 push는 사람 확인 뒤에 하기 같은 것들입니다. - 폴더는 세 층으로 나눕니다. 원본(
raw/, 고치지 않음), 에이전트가 만드는 지식(wiki/), 스키마 파일입니다. - 모든 위키 페이지 맨 위에 출처와 신뢰도를 적습니다.
---
type: project
status: active
confidence: high # high=1차 소스 직접 확인 / medium=추정 / low=가설
updated: 2026-10-26
sources: ["[[src-readme]]"]
---
- 메모리 규칙: 메모리에는 "다음 대화에서도 유효한 사실이나 선호"만 남기고, 규칙이 바뀌면 옛 항목을 지우거나 "(구)→(신)"처럼 대체됐다고 표시합니다.
완료 기준(확인 방법)
- 새 세션에서 "우리 위키에서 X는 어디에 정리돼 있어?"라고 물었을 때, 에이전트가 목차(
index.md)부터 읽고 출처를 달아 답합니다. - 위키 페이지가 5~10개 있고, 모든 페이지에
sources와confidence가 있습니다.
흔한 실수: 스키마 파일에 회사 소개를 길게 씁니다. 지시가 길어질수록 잘 안 지켜집니다. 에이전트가 같은 실수를 두 번 할 때만 한 줄씩 늘리세요. 메모리와 스키마 파일은 '맥락'일 뿐 강제 규칙이 아니라는 점도 기억해야 합니다. 꼭 막아야 하는 행동은 훅(hook) 같은 실행 단계의 차단 장치에 맡깁니다.
2주차: 가장 자주 하는 일 2개를 스킬로
할 일
- 지난 2주 동안 세 번 이상 똑같이 설명했던 일을 찾아 두 개를 고릅니다. 예를 들면 "새 자료를 위키에 넣기", "코드 변경을 위키에 동기화하기" 같은 일입니다.
- 각 일을
SKILL.md하나로 정리합니다. 언제 쓰는지(description), 단계, 끝났는지 확인하는 방법, 하지 말 것을 씁니다.
---
name: wiki-sync
description: 위키를 최신 코드와 동기화. "동기화", "소스 반영" 요청 시 사용
---
1. 지난 동기화 이후 바뀐 저장소만 찾는다
2. README → 설정 파일 → 진입점 순으로 읽는다
3. 페이지 갱신 + index·log 기록 + 커밋 (push 금지)
4. 끝나면 바뀐 파일 목록과 확인 결과를 보고
완료 기준: 스킬 이름을 직접 부르지 않고 평소 말투로 요청해도 그 스킬이 실행됩니다. 실행 결과가 3번 연속 "그대로"로 승인되면 됩니다.
흔한 실수: 스킬을 10개씩 한꺼번에 만듭니다. 설명이 겹치면 엉뚱한 스킬이 실행됩니다. 둘을 안정시킨 다음에 늘리세요.
3주차: 사내 API 하나를 읽기 전용 MCP로
할 일
- 자주 들여다보는 사내 시스템(통계, 고객 목록, 라이선스 상태 등) 하나를 골라 MCP 서버로 감쌉니다. 처음에는 조회 도구만 엽니다.
- 도구에
readOnlyHint: true같은 annotation을 붙입니다. 다만 이것은 클라이언트에 주는 힌트일 뿐 보안 장치가 아닙니다. 실제로 막는 것은 권한 범위(scope)를 읽기로 제한한 서비스 키입니다. - 키는 환경 변수나 OS 키체인에서 서버가 직접 읽게 합니다.
완료 기준
- "이번 주 지표 요약해줘"라는 요청이 대시보드를 열지 않아도 끝납니다.
- 쓰기를 시도하면 서버가 권한 오류를 냅니다. 이 오류가 나는 것을 직접 확인합니다.
흔한 실수: 편하다는 이유로 관리자 키를 그대로 붙입니다. 읽기 전용으로 한 달 써 보고, 쓰기 도구는 확인 단계(사람에게 바뀌는 내용을 먼저 보여 주기)를 갖춘 뒤에 하나씩 추가하세요.
4주차: 예약 초안 1개 + 승인 발행
할 일: 정기적으로 올리는 글 하나(주간 블로그, 뉴스레터 등)를 예약 잡으로 만듭니다. 핵심은 초안 생성과 발행을 서로 다른 단계로 나누는 것입니다.
[예약 시각] draft 단계: 토픽 큐의 첫 줄 → 헤드리스 에이전트 → 초안 저장 + 알림
※ 이 단계 환경에는 발행 키를 넣지 않음
[사람] approve 단계: 미리보기 → 확인 후 --yes 로 실제 발행
이렇게 나누면 예약 실행에서 일어날 수 있는 최악의 결과는 "초안 생성 실패 + 알림"입니다. 잘못된 글이 나가는 일은 생기지 않습니다. 발행 키가 없으니 실수로라도 올라갈 수 없습니다.
완료 기준
- 예약을 믿기 전에 같은 잡을 한 번은 직접 돌려 봅니다. 스케줄러 환경에서는 인증이 안 풀려 실패하는 경우가 흔하므로 로그를 확인합니다.
- 큐에 토픽이 없으면 알림만 보내고 건너뜁니다.
- 승인한 초안 1편이 실제로 발행됩니다.
하지 말아야 할 것 세 가지
- 완전 자동 외부 발행: 공개 채널에 나가는 글은 사람 승인 단계를 둡니다. 생성 단계에서 발행 권한을 아예 빼 두는 것이 가장 확실합니다.
- 키를 프롬프트에 넣기: 대화나 프롬프트에 붙인 키는 로그·기록에 남는다고 보아야 합니다. 키는 환경 변수나 키체인에 두고, 한 번이라도 노출됐다면 바로 교체(rotation)합니다.
- 검증 없는 완료 보고: "완료했습니다"라는 말은 근거가 아닙니다. 스킬마다 끝났는지 확인하는 방법(테스트 실행, 바뀐 파일 목록, 발행된 URL 응답 확인)을 정해 두고, 그 결과를 함께 보고하게 합니다.
4주 뒤 점검
로그를 모아 두 지표를 계산합니다. 절약 시간이 늘고 재작업률이 떨어졌다면 다음 업무로 넓힙니다. 재작업률이 높다면 새 자동화를 붙이지 말고, 실패한 사례를 스키마 파일과 스킬 규칙에 한 줄씩 반영하는 것이 먼저입니다.
이 노하우를 서비스로 — Zelly Tree 소개
핵심 요점
- Zelly Tree StartupOps는 이 노트의 원칙(회사 자료 근거, 확인 후 답변, 에이전트 팀, 쓴 만큼 과금)을 직접 셋업하지 않고 쓸 수 있게 만든 웹 서비스입니다.
- Virtual CFO, 정부지원사업, 특허, Growth, CEO Concierge의 다섯 에이전트가 있고, 필요한 에이전트는 Agent Designer로 직접 만들 수 있습니다.
- 사용 흐름은 자료 올리기, 말로 맡기기, 결과 받고 한글·워드로 내보내기의 세 단계입니다.
- API 키로 Claude Code·Claude Desktop에 연결하면 평소 쓰는 Claude 안에서 회사 데이터 조회 도구를 부를 수 있습니다.
- 과금은 1원=1크레딧 충전식으로, 호출마다 토큰 단가로 차감됩니다. 구독이 없고 잔액은 만료되지 않으며, 가입하면 10,000크레딧을 받습니다.
- 회사 데이터는 격리 저장하고 LLM 학습에 쓰지 않습니다. 모델은 AWS Bedrock의 Claude가 중심이고, 데이터는 AWS 서울 리전에 저장합니다.
왜 서비스로 만들었나
이 노트에서 설명한 방식에는 공통된 전제가 있습니다. 첫째, 회사 자료를 먼저 모아 둡니다. 둘째, 에이전트는 그 자료를 도구로 직접 찾아본 뒤에 답합니다. 셋째, 회계·정부과제·특허처럼 영역을 나눠 각 영역을 맡는 에이전트를 둡니다. 넷째, 비용은 실제로 쓴 만큼만 냅니다.
그런데 이 전제를 직접 갖추려면 시간이 듭니다. 코딩 에이전트를 설치하고, 자료 폴더 규칙을 정하고, 도구를 연결하고, 프롬프트를 다듬어야 합니다. 개발을 하지 않는 1인 창업자에게는 이 준비가 가장 큰 진입 장벽입니다. 그래서 같은 원칙을 미리 갖춰 둔 웹 서비스로 만든 것이 Zelly Tree StartupOps(zellytree.com)입니다. 모비오랩스가 운영하고, 이 노트의 에이전트 기반(ai-teammate)을 그대로 씁니다.
무엇을 해 주나
랜딩 페이지는 스타트업 운영을 다섯 영역으로 나누고, 영역마다 전문 에이전트를 하나씩 둡니다. 맞는 에이전트가 없을 때 직접 설계하는 도구도 따로 있습니다.
| 에이전트 | 하는 일 |
|---|---|
| Virtual CFO | 영수증·세금계산서를 읽어 장부에 기록하고, 손익과 현금 흐름을 그래프로 보여 줍니다. 세무 Q&A, 세금 일정, 비용 분석, 월간 경영 리포트도 맡습니다. |
| 정부지원사업 Agent | 기업마당·K-Startup 공고 가운데 회사 조건에 맞는 것을 고르고, 사업계획서 초안을 쓰고, 공고 마감일을 알려 줍니다. |
| 특허 Agent | 코드와 제품을 분석해 특허 가능성을 평가하고, 선행기술을 검색하고, 청구항·명세서 초안을 씁니다. |
| Growth Agent | SEO 글, 광고 카피, 랜딩 페이지 문구 초안을 쓰고, 키워드를 분석합니다. |
| CEO Concierge | 매일 아침 그날 챙길 일만 골라 브리핑하고, 회의 준비와 의사결정 요약을 돕습니다. |
| Agent Designer | 필요한 에이전트를 직접 설계합니다. 페르소나·스킬·프롬프트를 자동으로 만들어 줍니다. |
랜딩 페이지에는 브리핑·재무제표·대화 화면이 실려 있습니다. 모두 가상의 회사로 만든 예시이므로, 그 안의 숫자를 실제 성과로 읽으시면 안 됩니다.
확인한 뒤 답한다는 원칙
이 노트에서 가장 강조한 원칙은 짐작으로 답하지 않는 것이었습니다. 서비스에도 같은 원칙이 들어 있습니다. 랜딩 페이지에 따르면 에이전트는 회사 장부·문서·공고를 도구로 직접 찾아보고, 찾은 근거를 답과 함께 보여 줍니다. 데이터가 없으면 없다고 말합니다. 예를 들어 비용을 물으면 "장부의 분개 몇 건을 근거로 계산했다"는 식으로 출처를 밝히는 구조입니다.
3단계 사용 흐름
- 회사 자료 올리기(BRING): 사업자 정보, 영수증·세금계산서, 사업계획서, 특허 문서를 끌어다 놓습니다. 한글·워드·PDF·사진 파일을 모두 읽습니다.
- AI 팀원에게 맡기기(ASK): CFO·정부지원·특허·그로스·비서 에이전트에게 말로 부탁합니다. 에이전트는 올린 자료를 근거로 도구를 직접 써서 답합니다.
- 결과 받고 내보내기(GET): 재무제표, 공고 목록, 문서 초안, 기한 알림으로 결과를 받습니다. 문서는 한글·워드 파일로 내려받을 수 있습니다.
처음 쓰실 때는 아래 순서를 권합니다.
- [ ] 사업자 정보부터 넣기. 공고 매칭과 계획서 초안의 정확도가 여기서 정해집니다.
- [ ] 최근 몇 달치 영수증·세금계산서를 한 번에 올리고, 만들어진 장부를 직접 검토하기
- [ ] 이미 있는 사업계획서·특허 문서를 올려 에이전트가 참고할 자료 늘리기
- [ ] 첫 질문은 답을 이미 아는 것으로 하기(예: 지난달 주요 지출). 근거 표시가 맞는지 확인하는 용도입니다.
Claude Code·Claude Desktop에서 쓰기
이미 Claude를 쓰고 계시다면 웹 화면에 들어가지 않아도 됩니다. 랜딩 페이지에 따르면 API 키 하나로 Claude Code나 Claude Desktop에 연결하면, 평소 쓰는 Claude 안에서 회사 데이터를 조회하는 도구를 부를 수 있습니다. 구체적인 연결 방법은 서비스 설정 화면에 안내되어 있습니다.
Claude Code에서 원격 MCP 서버를 등록하는 일반적인 형태는 아래와 같습니다. 실제 주소와 인증 방식은 설정 화면에 나온 값을 쓰셔야 합니다.
# 일반 형식(Claude Code 문서 기준). <...> 자리는 설정 화면에 안내된 값으로 바꿉니다.
claude mcp add --transport http <이름> <설정 화면의 서버 주소>
API 키는 비밀번호와 같습니다. 저장소나 공유 문서에 붙여 넣지 마시고, 유출이 의심되면 즉시 새로 발급받으세요.
쓴 만큼 과금: 크레딧 방식
과금은 구독이 아니라 충전식입니다. 랜딩 페이지에 공개된 조건은 다음과 같습니다.
- 1원이 1크레딧입니다. 사용량은 호출할 때마다 토큰 단가로 차감됩니다.
- 충전은 토스로 합니다. 구독이 없고, 잔액은 만료되지 않습니다.
- 가입하면 신용카드 없이 10,000크레딧을 받습니다.
- 결제 후 7일 이내라면 사용하지 않은 잔액을 전액 환불받을 수 있습니다. 일부를 쓴 뒤 남은 잔액도 신청하면 환불됩니다.
| 충전 상품 | 결제 금액 | 크레딧 | 랜딩에 명시된 추가 혜택 |
|---|---|---|---|
| Starter | ₩10,000 | 10,000 | 5개 에이전트 모두 사용, RAG 검색과 도구 호출, 사용량 대시보드, 이메일 지원 |
| Growth | ₩50,000 | 60,000(+20%) | 월간 자동 리포트, 우선 응답 |
| Scale | ₩100,000 | 130,000(+30%) | 커스텀 에이전트, 우선 지원, 전담 채널 |
상품 선택 기준은 간단합니다. 가입 크레딧으로 먼저 한 달치 업무를 돌려 보고, 사용량 대시보드에 찍힌 실제 소모량을 보고 정하시면 됩니다.
데이터 처리 원칙
랜딩 페이지 FAQ에 적힌 원칙은 다음과 같습니다.
- 회사 데이터는 서비스 자체 DB에 격리해 저장하며, LLM 학습에 쓰지 않습니다.
- 모델은 AWS Bedrock의 Claude 모델이 중심이며, 작업 난이도에 맞춰 자동으로 고릅니다.
- 파일과 데이터베이스는 국내(AWS 서울 리전)에 저장합니다.
- 결제·계정 정보는 토스페이먼츠의 PCI-DSS 환경에서 처리합니다.
Bedrock이 프롬프트와 응답을 어떻게 다루는지는 AWS의 Bedrock 데이터 보호 문서에서 직접 확인하실 수 있습니다. 민감한 계약서나 개인정보가 담긴 자료를 올리기 전에는 서비스의 개인정보처리방침도 읽어 보시길 권합니다.
직접 만들까, 서비스를 쓸까
판단 기준은 하나입니다. 이 노트의 방식을 직접 꾸리는 일이 즐겁고, 자기 업무에 맞게 계속 고쳐 쓰고 싶다면 직접 만드는 편이 낫습니다. 반대로 회계·공고·특허 업무를 당장 덜어 내고 싶다면 셋업이 끝난 서비스로 시작하는 편이 빠릅니다. 두 방식을 섞어 써도 됩니다. Claude Code에 서비스를 연결해 두면 직접 만든 스킬과 서비스의 도구를 한 세션에서 함께 부를 수 있습니다.
시작은 가입부터입니다. 신용카드는 필요 없고, 받은 크레딧으로 바로 써 볼 수 있습니다.
https://zellytree.com