베이스 업데이트 받기
라이선시 fork는 한 번 받고 끝이 아닙니다. 베이스(ddakit/saas-base)가 새 스킬을 추가하거나, 훅 동작을 고치거나, AI_AUTOMATION.md의 도메인 규칙을 갱신할 때마다 그 변경이 fork에 도달해야 라이선스 가치가 유지됩니다. 이 자리는 git rebase 모르고도 안전하게 받게 해주는 /update-from-base가 어떤 가정 위에서 동작하는지를 정리합니다.
자동 적용 가능, 절대 안 건드림, 사람이 결정. 이 셋의 분기가 핵심입니다.
영역별 owner
섹션 제목: “영역별 owner”업데이트가 무엇을 덮어쓰고 무엇을 그대로 두는지는 파일 자리로 결정됩니다.
base-owned — 자동 갱신 대상
섹션 제목: “base-owned — 자동 갱신 대상”베이스가 SSOT인 자리. 사용자가 편집해도 다음 update에서 베이스 버전으로 돌아갑니다.
| 경로 | 무엇 |
|---|---|
.claude/skills/{base-skill-name}/ | 베이스 스킬 정의 (phase 1~6, implement-*, review-saas, test-saas, deploy-saas, /update-from-base 등) |
.claude/agents/*.md | 베이스 agent 정의 (saas-architect, saas-builder, saas-reviewer) |
.claude/hooks/*.sh + .claude/hooks/README.md | 베이스 훅 (session-start, stop-reminder, pre-commit-check) |
.claude/settings.json | 베이스 훅 등록 |
AI_AUTOMATION.md | 도메인 SSOT |
AGENTS.md | Codex 진입점 |
베이스 스킬 본문은 직접 수정하지 않는 것이 원칙입니다. 자기 스킬을 추가하고 싶으면 별도 디렉토리(예: .claude/skills/my-team-{name}/)에 만듭니다. base-owned 영역에 새 스킬이 추가되어도 사용자 자기 디렉토리는 영향받지 않습니다.
user-owned — 절대 안 건드림
섹션 제목: “user-owned — 절대 안 건드림”| 경로 | 무엇 |
|---|---|
src/ | 사용자 SPA 소스 |
api/functions/, api/migrations/, api/policies.test.sql, api/seed.sql | 사용자 서버 코드, 마이그레이션, RLS 정책, RLS 테스트 |
package.json | 사용자 의존성 |
vite.config.ts, tsconfig.json | 사용자 빌드 설정 |
index.html | CSP, og meta, 도메인별 자리 |
public/ | favicon, robots.txt, sitemap.xml |
docs/design/saas-spec.md | 사용자 작성 사양 |
docs/adr/*.md (README.md 제외) | 사용자 결정 기록 |
docs/deployment/, docs/prompts/, docs/ai-rules/ | 사용자 산출 |
.claude/state/*, .claude/session-state/*, .claude/agent-memory/* | 런타임, 세션, 메모리 |
.env* | 환경변수 |
LICENSE, COMMERCIAL-LICENSE.md | 라이선스 |
라이선스 파일은 베이스가 갱신해도 자동으로 덮어쓰지 않습니다. 베이스에서 갱신이 있었다면 안내만 띄우고 사용자가 수동 결정합니다.
.env* 파일도 자동 적용 영역 밖입니다. 베이스가 .env.example에 새 변수 자리를 추가했다면 그 변수를 본인 .env.local에 채워 넣는 자리도 같이 안내됩니다.
mixed — 3-way 안내
섹션 제목: “mixed — 3-way 안내”CLAUDE.md, README.md 둘. 베이스와 사용자 양쪽 편집이 동시에 들어갈 수 있는 자리라서 자동 갱신하지 않고 diff만 보여주고 묻습니다.
옵션 셋:
- 베이스 버전으로 통째 덮기. 사용자 편집은 사라집니다
- 3-way merge.
git merge-file로 합치고 충돌이 나면 직접 풉니다 - 미루기. 다음 호출까지 그대로 둡니다
README.md는 phase 6(/6-cleanup-residue)이 본인 SaaS 이름과 설명으로 다시 쓰는 자리라, base 변경분을 통째 덮으면 사용자 SaaS 이름이 사라집니다. mixed 처리가 들어간 이유입니다.
사전 셋업 — 한 번만
섹션 제목: “사전 셋업 — 한 번만”베이스를 git remote로 등록합니다.
git remote add base https://github.com/ddakit/saas-base.gitgit remote -vgit remote -v 출력에 base가 fetch URL로 두 줄 보이면 정상입니다. 첫 /update-from-base 호출이 이 단계를 자동으로 안내하므로 미리 칠 필요는 없습니다.
상태 파일은 .claude/state/base-upstream.json에 들어갑니다.
{ "remote": "base", "branch": "main", "last_synced_commit": "<hash>", "last_synced_at": "<ISO>"}last_synced_commit부터 base/main까지의 변경분만 보면 되니, 처음 한 번 채워지고 나면 다음부터는 도구가 알아서 잡습니다.
한 사이클
섹션 제목: “한 사이클”/update-from-base 또는 자연어로 “베이스 업데이트 받아줘”라고 부르면 다음을 거칩니다.
git status --porcelain으로 dirty 검사. 저장 안 된 변경이 있으면git stash권유git fetch base maingit diff --name-status <last_synced_commit>..base/main로 변경 파일을 영역별로 분류- base-owned 변경 요약 출력. 새 스킬 N개, 갱신된 훅 M개, AI_AUTOMATION.md 갱신 여부 한 줄씩
- 사용자 yes 시
git checkout base/main -- .claude/skills/ .claude/agents/ .claude/hooks/ .claude/settings.json AI_AUTOMATION.md AGENTS.md - mixed 파일은 한 개씩 옵션 셋 중 골라 적용
- 적용 후 검증.
.sh파일에chmod +x,settings.jsonJSON 파싱, 새 스킬의 trigger 키워드 나열 .claude/state/base-upstream.json갱신- commit 권유. 메시지는
chore(base): sync to {short-hash} ({N}개 파일)
기본 흐름은 30초에서 2분 사이로 끝납니다. mixed 파일 결정에 시간이 더 걸리는 한 분기가 있습니다.
RLS 마이그레이션 자리는 user-owned
섹션 제목: “RLS 마이그레이션 자리는 user-owned”베이스가 새 RLS 정책 예시를 AI_AUTOMATION.md 섹션 6.6에 추가했다고 해도, 그 정책이 사용자 api/migrations/에 자동으로 박히지 않습니다. 사용자 도메인 entity에 따라 정책 모양이 달라지기 때문입니다.
베이스가 SSOT인 자리는 “정책을 어디에 두는지의 규칙”이지 “어떤 정책을 둘지”가 아닙니다. 후자는 사용자 결정 자리고, phase 4의 데이터 스키마 결정과 phase 5의 implement-data-layer에서 채워집니다.
베이스 스킬을 본인이 수정한 흔적이 있을 때
섹션 제목: “베이스 스킬을 본인이 수정한 흔적이 있을 때”.claude/skills/{base-skill}/ 안 파일을 사용자가 commit으로 수정한 적이 있으면 (git log로 감지) 그 파일은 base-owned가 아니라 mixed로 처리됩니다. 자동 덮어쓰면 사용자 작업이 사라지기 때문입니다.
이걸 피하고 싶으면 베이스 스킬을 직접 수정하지 말고, 같은 trigger 키워드를 가진 별도 스킬을 자기 디렉토리에 만듭니다. 두 스킬이 같이 있으면 매뉴얼이 같은 키워드로 자기 쪽 스킬을 부릅니다. 베이스가 그 스킬을 갱신해도 자기 스킬은 그대로 살아있습니다.
베이스가 force-push 했을 때
섹션 제목: “베이스가 force-push 했을 때”last_synced_commit이 base/main 히스토리에 더 이상 없으면 (force-push 된 경우) 도구는 자동 적용을 멈추고 안내만 띄웁니다. 일반 케이스는 아닙니다. 베이스가 sensitive한 commit을 지웠거나 라이선스 위반 콘텐츠를 빼야 했을 때 발생합니다.
복구는 손작업입니다.
- 사용자 영역(
src/,api/,docs/design/,docs/adr/,package.json,index.html,.env*)을 본인 브랜치로 빼 둡니다 - 베이스를
base/main으로 reset 받아 새 베이스 위에 자기 작업을 다시 올립니다 - cherry-pick 또는 직접 복사로 사용자 영역을 복원합니다
이 자리는 /recover-from-blocked가 다루는 영역이 아닙니다. force-push가 실제로 일어나면 베이스 메인테이너가 별도로 안내합니다.
왜 자동 적용이 아닌 영역 분류인가
섹션 제목: “왜 자동 적용이 아닌 영역 분류인가”가장 단순한 설계는 git pull base main 한 줄입니다. 그렇게 하지 않은 이유 둘.
라이선시는 자기가 산 시점의 코드 위에 자기 작업을 쌓은 사람입니다. git pull은 베이스 변경과 사용자 변경을 같은 레이어로 보고 merge commit을 만듭니다. 비개발자에게는 그 merge commit과 그 뒤에 따라오는 충돌 해결이 그대로 막히는 자리입니다.
base-owned 영역은 베이스가 SSOT라는 합의가 있습니다. 그 합의가 있는 자리만 자동 덮어쓰는 게 안전합니다. 합의가 없는 자리(mixed)는 사람이 결정하게 둡니다. fork가 진화하지 않으면 베이스 가치가 한 번 산 시점에서 멈추고, fork가 무방비로 진화하면 사용자 작업이 날아갑니다. /update-from-base는 그 둘 사이에 박은 채널입니다.
인접 자리
섹션 제목: “인접 자리”- 비개발자 호흡으로 같은 절차를 따라가는 자리는 비개발자용 매뉴얼 업데이트에 있습니다
- 이 스킬이 베이스 thesis와 라이선스 모델 양쪽의 안전망인 이유는 intro의 “지금 빠져 있는 것” 단락 옆에 두고 같이 봅니다