레포 아키텍처: 로컬 ↔ GitHub ↔ Cloudflare ↔ Wrangler
wine-research 레포가 로컬 작업 환경부터 공개 위키 사이트까지 어떻게 흘러가는지, 각 계층이 어떤 역할을 하는지, 그리고 무엇이 어떤 트리거로 동기화되는지 한 장에 정리한다.
요약: 로컬에서 마크다운을 쓰고, _workspace/를 진실의 원천으로 삼아 → GitHub에는 원본을 보관하고 → Astro/Starlight가 위키 사이트로 빌드하고 → Wrangler CLI가 Cloudflare Pages의 production 브랜치로 업로드한다. GitHub와 Cloudflare는 직접 연결돼 있지 않다(이게 핵심 함정).
1. 4계층 관계도
섹션 제목: “1. 4계층 관계도”┌────────────────────────────────────────────────────────────────────────────┐│ [1] 로컬 워크스테이션 ││ ││ /Users/yejinkim/dev/wine-research/ ││ ├── _workspace/ ← 진실의 원천 (모든 .md 리서치/스펙/플레이북) ││ │ ├── 01_sensory_research.md ││ │ ├── 50_label_recognition_research.md ││ │ ├── 53_repo_architecture.md ← (이 문서) ││ │ └── … ││ ├── CLAUDE.md ← 하네스 정의·변경 이력·트러블슈팅 노트 ││ └── site/ ← Astro Starlight 위키 빌드 ││ ├── scripts/sync-docs.mjs (workspace → src/content/docs 복사) ││ ├── src/content/docs/ (sync가 매번 reset+재생성) ││ ├── astro.config.mjs (사이드바·로케일 정의) ││ └── dist/ (astro build 산출물, .gitignore됨) │└──────────────┬──────────────────────────────────┬──────────────────────────┘ │ │ │ git push origin main │ npm run deploy │ (소스 + 작업 이력 보존) │ (정적 사이트 업로드) ▼ ▼┌────────────────────────────────────┐ ┌────────────────────────────────────┐│ [2] GitHub │ │ [3] Cloudflare Pages ││ │ │ ││ rocher71/wine-research │ │ project: wine-wiki ││ ├── branch: main │ │ production branch: production ││ ├── _workspace/*.md (원본) │ │ public URL: ││ ├── CLAUDE.md │ │ https://wine-wiki-kg5 ││ └── site/ (소스만, dist 제외) │ │ .pages.dev ││ │ │ ││ 역할: 코드/문서 버전 관리, 협업 │ │ 역할: 사이트 호스팅, CDN, HTTPS │└────────────────────────────────────┘ └─────────────────▲──────────────────┘ │ │ HTTPS API │ upload dist/ ┌───────────────┴────────────────┐ │ [4] Wrangler CLI │ │ │ │ site/node_modules/wrangler │ │ (devDependency, ^4.90.0) │ │ │ │ `wrangler pages deploy dist │ │ --project-name=wine-wiki │ │ --branch=production` │ │ │ │ 역할: Cloudflare API 클라이언트│ │ (로컬→Cloudflare 단방향 푸시)│ └────────────────────────────────┘한 줄로 외우는 관계
섹션 제목: “한 줄로 외우는 관계”로컬 = 작업장 / GitHub = 보관소 / Cloudflare = 게시판 / Wrangler = 게시판에 붙이는 손
GitHub와 Cloudflare 사이에는 자동 동기화가 없다. Cloudflare Pages가 GitHub 리포지토리를 “watch”하도록 연결돼 있지 않다. 두 곳은 각각 독립적으로 로컬에서 푸시된다.
2. 책임 매트릭스 (어디서 무엇을)
섹션 제목: “2. 책임 매트릭스 (어디서 무엇을)”| 계층 | 무엇을 보관/실행 | 트리거 | 사람이 직접 만지는가 |
|---|---|---|---|
로컬 _workspace/ | 모든 리서치/스펙/플레이북 원본 | 사람이 직접 작성 또는 에이전트가 생성 | ✅ |
로컬 site/src/content/docs/ | 사이트 빌드 입력 (재생성됨) | npm run sync (혹은 dev/build 안에서 자동) | ❌ (sync가 매번 wipe) |
로컬 site/dist/ | 정적 HTML/CSS/JS 빌드 산출물 | astro build | ❌ |
GitHub main | 소스(_workspace, site, CLAUDE.md) | git push | ✅ |
Cloudflare production env | 게시되는 사이트 | wrangler pages deploy --branch=production | ❌ |
Cloudflare Preview env | 미리보기용 (해시 URL) | wrangler pages deploy (기본값) | ❌ |
3. 가장 헷갈리는 부분: _workspace/ ↔ site/src/content/docs/
섹션 제목: “3. 가장 헷갈리는 부분: _workspace/ ↔ site/src/content/docs/” _workspace/ site/src/content/docs/ ┌──────────────────────┐ ┌──────────────────────┐ │ 01_sensory_research │ sync-docs │ tasting/ │ │ 02_flavor_research │ .mjs │ 01_sensory_… │ │ 13_user_feedback │ ─────────────► │ 02_flavor_… │ │ 15_design_system │ (매번 reset) │ feedback/13_… │ │ 30_app_spec_v1 ❌ │ │ tech/15_… │ │ 45_weekly_report ❌ │ │ market/10_… │ │ 50_label_recog │ │ misc/… │ │ 53_repo_architecture │ │ tech/53_repo_… │ └──────────────────────┘ └──────────────────────┘ ※ 30~39, 40~49 prefix는 EXCLUDE 정규식으로 제외 (앱 사양·작업 보고는 비공개)sync-docs.mjs의 그룹 라우팅 규칙
섹션 제목: “sync-docs.mjs의 그룹 라우팅 규칙”site/scripts/sync-docs.mjs:18-22의 GROUPS 정의:
tasting: 01-05, 17 → 사이드바 "테이스팅 차원"regions: 06-09 → "산지"feedback: 13, 16 → "피드백"tech: 14, 15, 50-59 → "기술/엔지니어링"market: 10-29, 60-69 → "시장·마케팅 전략"(fallback) misc → "기타"위→아래 순서로 매칭한다. market의 10-29 광역 매치보다 feedback/tech의 명시 번호를 먼저 두기 때문에 13·14·15·16번이 의도한 그룹으로 들어간다.
EXCLUDE 패턴: /^[34][0-9]_/ → 3039(앱 구현 사양), 4049(작업 보고)는 위키에 올라가지 않는다.
4. 한 번 배포의 전체 시퀀스
섹션 제목: “4. 한 번 배포의 전체 시퀀스” 사람 npm run deploy Cloudflare │ │ │ │ npm run deploy │ │ ├─────────────────────────►│ │ │ │ │ │ ├─ [build] npm run sync │ │ │ ├─ resetDocs() ── docs/ wipe │ │ │ ├─ syncWorkspace() ── 라우팅 │ │ │ └─ writeIndex() ── splash 생성 │ │ │ │ │ ├─ [build] astro build │ │ │ └─ → site/dist/ │ │ │ │ │ ├─ wrangler pages deploy dist │ │ │ --project-name=wine-wiki │ │ │ --branch=production │ │ │ ──────────────────────────────►│ Upload │ │ │ Production env │ │ ◄──────────────────────────────│ deployment URL │ │ │ │ "Deployed to │ │ │ https://wine-wiki-kg5.pages.dev" │ │◄─────────────────────────┤ │소요 시간: 정상 시 sync(~1s) + build(~30-60s) + upload(~30-90s) = 총 2-3분.
5. 환경/브랜치 매핑 — 실수하기 가장 쉬운 지점
섹션 제목: “5. 환경/브랜치 매핑 — 실수하기 가장 쉬운 지점”| 로컬 git branch | Wrangler --branch | Cloudflare 환경 | 결과 URL |
|---|---|---|---|
main (디폴트) | 미지정 → main | Preview | https://<hash>.wine-wiki-kg5.pages.dev (해시 URL만) |
main | production | Production | https://wine-wiki-kg5.pages.dev (정식) |
왜 함정인가: wrangler의 --branch 디폴트는 “현재 git branch명”. Cloudflare Pages 대시보드에서 production branch가 production으로 설정돼 있는데, 우리 로컬 작업은 main에서 이루어진다. 둘이 일치하지 않아 정식 URL이 며칠 전 상태로 멈춰 보인다. → T-002 참조.
해결: site/package.json:9에 --branch=production을 영구 박아둠.
"deploy": "npm run build && wrangler pages deploy dist --project-name=wine-wiki --branch=production","deploy:preview": "npm run build && wrangler pages deploy dist --project-name=wine-wiki"6. 명령어 cheatsheet
섹션 제목: “6. 명령어 cheatsheet”위키 배포 (가장 자주)
섹션 제목: “위키 배포 (가장 자주)”cd site && npm run deploy위키 미리보기 (해시 URL로만)
섹션 제목: “위키 미리보기 (해시 URL로만)”cd site && npm run deploy:preview로컬 위키 dev 서버
섹션 제목: “로컬 위키 dev 서버”cd site && npm run dev # http://localhost:4321GitHub 푸시
섹션 제목: “GitHub 푸시”git add _workspace/ CLAUDE.md site/git commit -m "…"git push origin mainCloudflare 배포 상태 확인
섹션 제목: “Cloudflare 배포 상태 확인”cd site && npx wrangler pages deployment list --project-name=wine-wiki# Environment 열에서 Production / Preview 구분빌드 hang 복구 (디렉토리 rename 직후)
섹션 제목: “빌드 hang 복구 (디렉토리 rename 직후)”xattr -dr com.apple.provenance ./site && \ rm -rf site/node_modules site/.astro site/dist site/.wrangler/tmp && \ cd site && npm install && npm run deploy7. 누가 어디로 가는가 (파일 → 위키 사이드바)
섹션 제목: “7. 누가 어디로 가는가 (파일 → 위키 사이드바)”파일명 → 위키 사이드바 그룹 → URL slug─────────────────────────────────────────────────────────────────────────────────────01_sensory_research.md → 테이스팅 차원 /tasting/01_sensory_research/06_burgundy_terroir.md → 산지 /regions/06_burgundy_terroir/10_vivino_korea_analysis.md → 시장·마케팅 전략 /market/10_vivino_korea_analysis/13_user_feedback.md → 피드백 /feedback/13_user_feedback/15_design_system.md → 기술/엔지니어링 /tech/15_design_system/17_ritual_research.md → 테이스팅 차원 /tasting/17_ritual_research/30_app_spec_v1.md → ❌ EXCLUDE (비공개) —45_weekly_report.md → ❌ EXCLUDE (비공개) —50_label_recognition_research.md → 기술/엔지니어링 /tech/50_label_recognition_research/53_repo_architecture.md → 기술/엔지니어링 /tech/53_repo_architecture/60_marketing_plan.md → 시장·마케팅 전략 /market/60_marketing_plan/65_meta_ads_setup_playbook.md → 시장·마케팅 전략 /market/65_meta_ads_setup_playbook/wine-tasting-notes-reference.md → 통합 레퍼런스 /synthesis/wine-tasting-notes-reference/8. 트러블슈팅 요약
섹션 제목: “8. 트러블슈팅 요약”전체 내용은 CLAUDE.md에. 아래는 발생 빈도가 높은 두 가지.
| ID | 증상 | 원인 | 1줄 해결 |
|---|---|---|---|
| T-001 | npm run deploy가 astro build에서 무한 hang | macOS com.apple.provenance xattr (디렉토리 rename 후 Gatekeeper가 native binary 검증으로 stall) | xattr -dr com.apple.provenance ./site && rm -rf site/{node_modules,.astro,dist,.wrangler/tmp} && cd site && npm install && npm run deploy |
| T-002 | 정식 URL에 변경이 안 보임 | wrangler 기본 --branch=<현재 git branch>(=main)가 Cloudflare의 Preview 환경으로만 푸시. Cloudflare의 production branch는 production | cd site && npx wrangler pages deploy dist --project-name=wine-wiki --branch=production (또는 npm run deploy 사용) |
9. 자주 묻는 질문
섹션 제목: “9. 자주 묻는 질문”Q. GitHub에 push하면 Cloudflare에도 자동 반영되나?
A. 아니다. Cloudflare Pages는 이 레포의 GitHub와 직접 연결돼 있지 않다. npm run deploy를 별도로 실행해야 위키가 갱신된다. (CI/CD를 붙이려면 GitHub Actions에서 wrangler를 호출하는 워크플로를 추가해야 한다 — 현재는 없음.)
Q. _workspace/만 고치면 자동으로 위키에 올라가나?
A. 아니다. _workspace/ → site/src/content/docs/ 복사는 npm run sync(혹은 build/dev/deploy)가 실행될 때만 일어난다. 사람이 명령을 쳐야 한다.
Q. site/src/content/docs/를 직접 고쳐도 되나?
A. 안 된다. sync-docs.mjs가 매번 resetDocs()로 해당 디렉토리를 통째로 지운 뒤 _workspace/에서 새로 생성한다. 반드시 _workspace/ 원본을 고쳐야 한다.
Q. Wrangler를 사용자 계정 인증은 어디서?
A. wrangler login으로 한 번 인증하면 ~/.config/.wrangler/(또는 macOS 키체인)에 토큰이 보관된다. CI에서 돌리려면 CLOUDFLARE_API_TOKEN 환경변수를 사용한다. 현재 이 레포에는 CI가 없으므로 로컬에서 1회 로그인된 상태로 동작.
Q. 비공개로 두고 싶은 파일은?
A. 파일명을 30_–39_(앱 사양) 혹은 40_–49_(작업 보고)로 짓는다. sync-docs.mjs의 EXCLUDE = /^[34][0-9]_/가 위키 빌드 대상에서 제외한다. 단, GitHub에는 그대로 push되므로 정말 비밀이라면 .gitignore에 추가하거나 레포 외부에 둬야 한다.
10. 변경 시 같이 손대야 하는 파일
섹션 제목: “10. 변경 시 같이 손대야 하는 파일”| 변경하고 싶은 것 | 손대는 파일 |
|---|---|
| 위키 사이드바 라벨/순서 | site/astro.config.mjs |
| 파일명 prefix → 그룹 매핑 | site/scripts/sync-docs.mjs (GROUPS) |
| 위키에서 제외할 prefix | site/scripts/sync-docs.mjs (EXCLUDE) |
| 위키 첫 화면(splash) | site/scripts/sync-docs.mjs (writeIndex) |
| 배포 명령 동작 | site/package.json (scripts) |
| Cloudflare 프로젝트명·production branch | Cloudflare Pages 대시보드 + site/package.json의 wrangler 인자 |
| GitHub 원격 | git remote set-url origin <new-url> |