iOS용 Markdown 에디터인 'Stilo'는 구현의 대부분을 Claude Code에 맡겨 만들었습니다. 제가 직접 작성한 것은 주로 사양서입니다. Stilo에서는 이 사양서를 'HANDOFF'라고 부릅니다.
대상 독자: Claude Code를 사용하지만, 규모가 큰 개인 프로젝트 전체를 맡기기 어려워하는 사람 -
이 글에서 알 수 있는 것: Stilo에서 Claude Code에게 무엇을 넘겼는지, 사양서의 어느 부분이 효과적이었는지, 폐기된 설계나 지식을 남기는 방법 -
전제: 이 글은 Stilo 1개 사례에 대한 것입니다. 모든 프로젝트에 적용할 수 있는 방법론으로 작성한 것은 아닙니다 -
사용 환경: Claude Code는 2026년 9월 18일자 최신 버전(이후에도 항상 최신 버전으로 업데이트). 모델은 기본적으로 Claude Fable 5.1을 사용했으며, 사용 한도를 소진했을 때는 Claude Opus 5로 전환했습니다. 진행 방식은 여러 에이전트를 조합하는 오케스트레이션입니다.
먼저 결과를 말씀드리겠습니다. 넘긴 것은 HANDOFF 3개와 작업 규칙(CLAUDE.md)이었습니다. 2026년 9월 18일에 Phase 1(파일 읽기/쓰기)부터 시작하여, 9월 20일에 첫 번째 버전(1.0)을 App Store에 제출했습니다. 리포지토리 연동 기능을 추가한 1.1은 9월 25일에 심사에 제출했고, 다음 날인 26일에 승인되었습니다. 9월 24일 기준으로 머지된 PR(Pull Request)은 총 35개입니다.
HANDOFF를 작성하는 것은 저에게 특별한 공이 아닙니다. 이전부터 LLM을 사용한 사양 주도 개발(SDD, Specification Driven Development)이나 테스트 주도 개발(TDD, Test Driven Development)을 해왔기 때문에, 사양서 없이 맡긴다는 발상이 애초에 없었습니다. 이 글은 그 사양서를 작성하는 방식을 개인 개발 프로젝트인 Stilo에서 어떻게 활용했는지에 대한 기록입니다.
무엇을 넘겼나
리포지토리의 최상단에는 다음 파일들을 배치했습니다.
stilo/
├── CLAUDE.md ← 작업 규칙 (읽는 순서와 지켜야 할 것)
├── HANDOFF_stilo.md ← 기반 및 Phase 1 (파일 읽기/쓰기)
...
CLAUDE.md는 간결하게 작성했습니다. 서두에는 '읽는 순서'가 있습니다.
## 읽는 순서
1. `HANDOFF_stilo.md` — 기반 및 Phase 1 (파일 I/O). **첫 번째 작업은 여기서 Phase 1**
2. `HANDOFF_stilo_pro.md` — Phase A~F(결제/한국어 특화/내보내기/클라우드 연동)와 현지화 방침
...
그 뒤에는 '지켜야 할 것'이 약 7줄 이어집니다(후술). 세부 사양은 모두 HANDOFF에 작성하고, CLAUDE.md는 '어디부터 읽어야 하는지'와 '모든 작업에서 지켜야 할 규칙'만을 담았습니다.
HANDOFF의 구조
결제 및 한국어 작성을 통합한 HANDOFF_stilo_pro.md의 목차를 발췌합니다.
## 0. 배경과 방침 (구현자가 판단에 어려움을 느낄 때의 기준)
### 0.1 비즈니스상의 위치 설정
### 0.2 판단 기준 (우선순위)
...
서두의 '0.'에는 판단이 모호할 때의 기준을 배치했습니다. Stilo의 경우, 우선순위에 따라 다음 5가지였습니다.
### 0.2 판단 기준 (우선순위)
1. 무료 버전만으로 일상적인 Markdown 편집이 완결되어야 함. 무료 버전 기능을 줄여 Pro에 맞추는 변경은 금지.
2. 결제 실패 시 사용자 데이터가 손실되지 않거나/편집이 중단되지 않아야 함.
...
(3번째 항목인 '버튼 필수'는 Stilo의 설계 판단입니다. 가이드라인이 요구하는 것은 복원 가능한 결제에 대한 복구 메커니즘을 준비하는 것입니다.)
사양서에는 쓰지 못하는 것들이 반드시 남게 됩니다. 그럴 때 Claude Code가 스스로 판단할 수 있도록 '무엇을 우선시할지'를 미리 넘겨주는 위치에 놓은 것입니다.
실제로 작성해 보니, 특히 효과적이라고 느낀 것은 '하지 않을 것', '수용 조건', '모순 발생 시의 우선순위' 세 가지였습니다.
효과적이었던 점 1: '하지 않을 것'을 쓰기
0.4 하지 말아야 할 것
- 브랜치 생성, 머지(Merge), 리베이스(Rebase), 히스토리 조회, diff 표시 (Git 클라이언트를 목표로 하지 않음)
- 레포지토리의 clone (전체 내용을 로컬에 보유하지 않고 필요한 파일만 가져오기)
...
StoreKit 섹션에도 '하지 말아야 할 것'이 있습니다. 영수증 검증 서버를 구축하지 않는 것, StoreKit 1을 혼용하지 않는 것, 가격을 하드코딩(hardcode)하지 않는 것 등이 있습니다.
제 감각으로는 Claude Code는 친절하기 때문에, 제가 작성하지 않은 부분이라도 '있으면 좋을 것 같은' 기능을 추가해 줍니다. 레포지토리 연동의 경우, 브랜치 전환이 가능하면 생성도 하고, 생성이 가능하면 머지도 한다와 같이 자연스럽게 확장됩니다. 이것 자체는 선의이지만, 개인 개발에서는 확장이 곧 유지보수 부담으로 돌아옵니다. '하지 말아야 할 것'을 명시하는 것은 범위를 좁히기 위함이 아니라, 범위 외에서 혼란을 막기 위함이라고 생각합니다.
저 자신도 빠뜨린 부분 때문에 고생한 적이 있습니다. Claude Code를 사용하기 시작했을 초기에 GitHub에 대해 어느 정도까지 조작할지 결정하지 못했습니다. 그 결과 main 브랜치에 불필요한 임시 파일까지 머지되어 돌아가기 위해 수고로움이 따랐습니다. 그때 이후로는 Git 작업은 커밋(Commit)과 푸시(Push)까지만 (더 신중하게 진행하고 싶을 때는 main에 직접 넣지 않고 PR을 생성하는 단계까지)로 정했고, .gitignore
도 주기적으로 재검토합니다.
효과적이었던 점 2: 수용 조건(Acceptance Criteria)을 먼저 작성하기
HANDOOFF의 후반부에는 페이즈별 수용 조건을 작성했습니다. 예를 들어 결제 기반(Phase A)은 다음과 같습니다.
### Phase A — StoreKit 기반 (PR 1)
- StoreManager / Entitlement / ProductIDs / Stilo.storekit
- 수용 조건: Transaction Manager에서 구매, 복원, 박탈이 isPro에 반영되는 것.
...
더불어 완료 보고서의 형식도 정했습니다.
## 12. 완료 보고 포맷 (각 PR)
- 변경 파일 목록
- 수용 조건 충족 여부 (○/×)
...
이렇게 해두니, Claude Code가 작성하는 완료 보고서(PR 설명)가 수용 조건에 대한 ○와 × 표로 돌아옵니다. 실제 Phase A의 PR에서 발췌한 내용입니다.
## 수용 조건 (HANDOFF 9 Phase A)
| 조건 | 결과 |
|---|---|
...
중요한 것은 '완료했습니다'가 아니라, 무엇이 완료되었고 무엇이 완료되지 않았는지가 나열되는 것입니다. 저는 표의 ×와 '알려진 제한 사항(既知の制限)'을 읽으면 어디를 제 눈으로 확인해야 할지 알 수 있습니다. CLAUDE.md
에도 '충족할 수 없는 항목은 ×와 이유를 PR 설명에'라고 적어두었습니다.
다른 PR (레포지토리 연동의 Phase G-0)에서는 수동으로 확인한 로그까지 보고서에 포함되어 있었습니다.
14:54:56 purchase pro → owned -> pro Pro만 구매 → 레포지토리는 닫힘
14:55:42 purchase repo → owned -> repo 레포지토리만 구매 → Pro는 닫힘
14:57:06 purchase pro → owned -> pro,repo 모두 소유
...
Phase G-0의 수용 조건인 '독립적으로 구매 및 박탈이 가능함'이 그대로 확인 가능한 형태로 되어 있습니다.
저는 이 표를 특별한 용도로 사용하지 않습니다. 일반적인 진행 상황 관리에 사용합니다. 어느 Phase가 어디까지 완료되었는지 확인하고, ×나 미실시 항목을 보면서 다음에 무엇을 우선순위로 할지 재정비합니다.
효과적이었던 점 3: 모순될 때의 우선순위를 미리 정해두기
사양서가 여러 개 있으면 반드시 어딘가에서 충돌이 발생합니다. Stilo에서는 HANDOFF(동작/행동)와 DESIGN_SPEC.md (외관/디자인)가 별도의 문서이기 때문에 더욱 그렇습니다.
그래서 CLAUDE.md
에 한 줄만 적었습니다.
모순될 경우: 동작은 HANDOFF, 외관은 DESIGN_SPEC를 따른다. 차이점은 PR 설명에 작성한다.
Stilo 개발에서 이렇게 하니 Claude Code는 모순을 발견해도 멈추지 않고 진행하며, 어떻게 판단했는지 그 과정을 PR에 남겨줍니다. 세로 쓰기 미리보기의 PR에는 다음과 같은 섹션이 있었습니다.
DESIGN_SPEC과의 차이점
- VerticalPreview.dc.html의 내비게이션 바 왼쪽 '< 편집'은 기존의 뒤로 가기 버튼(파일 목록)을 유지하고, 표시 모드는 오른쪽 상단의 피커에서 전환하는 구조를 그대로 채택했습니다. 중앙 세그먼트는 채택하지 않았습니다.
사양서 간의 차이점뿐만 아니라, 사양서와 현실 사이의 괴리도 마찬가지로 다루어졌습니다. 과금(Billing) 관련 PR에서는 Sandbox에서 currentEntitlements가 계속 빈 값을 반환하는 문제가 있었습니다. Claude Code는 이 문제에 대한 대처 방안으로 HANDOFF에는 없는 처리(예: Transaction.all까지 확인하는 것)를 추가했습니다. learnings.md에는 'HANDOFF 3.2는 currentEntitlements만'이라고 쓰여 있었기 때문에, 이는 사양서와 다른 부분임을 명확히 기재했습니다.
폐기된 설계의 보존 방법
HANDOFF 내부에는 도중에 폐기한 설계도 있습니다. 과금(Billing) HANDOFF의 13절 '클라우드 연계 팩'이 그것입니다 (왜 폐기했는지는 시리즈의 다른 글에 작성했습니다).
폐기할 때, 해당 절을 지우지 않고 서두에 다음과 같이 추가했습니다.
## 13. 후속 단계 — Phase F: 클라우드 연계 팩 (폐기됨. HANDOFF_stilo_repo.md로 대체)
> 2026-09-18에 폐기함. (폐기 이유)
> 후속 작업은 HANDOFF_stilo_repo.md입니다 (리포지토리 연동・GitHub 선행).
...
'구현 지침으로는 읽지 말 것'이라는 문구는 Claude Code를 위한 것입니다. 삭제하면 혼란은 없지만, 나중에 자신이 같은 아이디어를 떠올렸을 때 왜 포기했는지 알 수 없게 됩니다. 남겨둘 경우, 읽는 사람이 실수로 구현하지 않도록 명확하게 적어 둘 필요가 있었습니다. CLAUDE.md의 '읽는 순서'에도 리포지토리 연동 HANDOFF가 13절을 '대체한다'고 쓰여 있습니다.
CLAUDE.md의 '준수 사항'과 learnings.md
CLAUDE.md 후반부에는 어떤 Phase에서도 준수해야 할 사항들을 나열했습니다.
## 준수 사항
- 작업은 Phase / PR 단위로 진행할 것. 1 PR에 여러 Phase를 혼합하지 말 것
- 문자 입력 변경은 `UITextInput.replace(_:withText:)` 경유 (Undo 기능을 손상시키지 않도록)
...
마지막 줄의 learnings.md는 Claude Code가 PR마다 '막혔던 점', '알게 된 점'을 추가해 나가는 파일입니다. 세션이 바뀌어도 이전에 겪었던 함정을 반복하지 않기 위함입니다.
learnings.md가 실제로 효과를 본 경우도 있고, 그렇지 않은 경우도 있습니다. learnings.md에는 Claude Code 자신이 다음과 같이 작성한 부분이 있습니다.
⌘U로 통과할 수 있다고 제가(Claude) 안내한 것은 오류였으며, 이미 learnings에 적혀 있었다. 착수 전에 learnings를 읽을 것.
적어 놓았더라도, 읽히지 않으면 의미가 없습니다. 이 문구는 그 반성으로 learnings.md에 남겨진 것입니다.
learnings.md의 가치는 프로젝트를 하나만 진행할 때보다 여러 개를 병행할 때 더 크다고 느낍니다. 저는 동시에 10여 개의 프로젝트를 운영하고 있으며, learnings.md에는 해당 프로젝트만의 이야기가 아니라 다른 곳에도 통용되는 지식(좋은 것이든 나쁜 것이든)이 뒤섞입니다. 그래서 프로젝트 진행 속도가 안정되면 learnings.md를 정리하여 범용적인 지식을 다른 프로젝트에도 반영해 나갈 계획입니다. learnings.md를 본격적으로 사용하기 시작한 것은 Stilo 덕분이라, 그 운영 방식은 앞으로 키워나갈 것입니다.
요약
- Stilo의 경우, HANDOFF 3개와 짧은
CLAUDE.md를 전달했고, Phase 1에 착수한 지 3일 만에 첫 버전을 App Store에 제출할 수 있었습니다. 효과적이었던 것은 '하지 말아야 할 것', '수용 조건과 O/X 완료 보고', 그리고 '모순 발생 시 우선순위'의 세 가지였습니다.
- 폐기된 설계는 삭제하지 않고 '폐기됨'과 이유를 남겨, 읽는 사람이 실수로 구현하지 않도록 명시했습니다.
여기에 적은 내용은 Stilo 단일 사례입니다.
이 시리즈의 이전 글:
- StoreKit 2만으로 만드는 일회성 결제 — RevenueCat 없는 최소 구성
- 무료로 할 수 있는 것을 유료가 되지 않게 한다 — 개인 개발 앱의 Free / Pro 경계 설정
- iOS에서 일본어 세로쓰기 프리뷰를 만들기 — 장음 부호(ルビ), 세로/가운데/가로, 변형되는 요소
- "나중에 번역하기"를 그만두기 — String Catalog의 번역률을 CI에서 100%로 고정하기
- 업데이트가 멈춘 앱을 찾아 빈틈에 들어가기 — 개인 개발의 경쟁사 조사와 함정
이 진행 방식을 다른 프로젝트에도 적용할 수 있는 형태로 정리한 책을 11월에 Zenn에서 출간할 예정입니다.
Discussion