베이스 업데이트 받기
라이선시 fork는 한 번 받고 끝이 아닙니다. base(ddakit/saas-base)가 새 스킬을 추가하거나, 훅 동작을 고치거나, AI_AUTOMATION.md의 도메인 규칙을 갱신할 때마다 그 변경이 fork에 도달해야 라이선스 가치가 유지됩니다. 이 문서는 git rebase를 몰라도 안전하게 받게 해 주는 /update-from-base가 어떤 가정 위에서 동작하는지 정리합니다.
자동으로 적용할 수 있는 영역, 절대 건드리지 않는 영역, 사람이 결정하는 영역. 이 셋으로 나누는 것이 핵심입니다.
영역별 owner
섹션 제목: “영역별 owner”업데이트가 무엇을 덮어쓰고 무엇을 그대로 두는지는 파일이 어느 영역에 속하는지로 결정됩니다.
base-owned: 자동 갱신 대상
섹션 제목: “base-owned: 자동 갱신 대상”base가 기준 문서를 가지고 있는 영역입니다. 사용자가 편집해도 다음 update에서 base 버전으로 돌아갑니다.
| 경로 | 무엇 |
|---|---|
.claude/skills/{base-skill-name}/ | base 스킬 정의입니다 (phase 1~6, implement-*, review-saas, test-saas, deploy-saas, /update-from-base 등) |
.claude/agents/*.md | base agent 정의입니다 (saas-architect, saas-builder, saas-reviewer) |
.claude/hooks/*.sh + .claude/hooks/README.md | base 훅입니다 (session-start, stop-reminder, pre-commit-check) |
.claude/settings.json | base 훅 등록 파일입니다 |
AI_AUTOMATION.md | 도메인 기준 문서입니다 |
AGENTS.md | Codex 진입점입니다 |
base 스킬 본문은 직접 수정하지 않는 것이 원칙입니다. 자기 스킬을 추가하고 싶으면 별도 디렉터리(예를 들어 .claude/skills/my-team-{name}/)에 만듭니다. base-owned 영역에 새 스킬이 추가되어도 사용자 자기 디렉터리는 영향을 받지 않습니다.
user-owned: 절대 건드리지 않는 영역
섹션 제목: “user-owned: 절대 건드리지 않는 영역”| 경로 | 무엇 |
|---|---|
src/ | 사용자 SPA 소스입니다 |
api/functions/, api/migrations/, api/policies.test.sql, api/seed.sql | 사용자 서버 코드와 마이그레이션, 행 수준 접근 정책, 정책 테스트입니다 |
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 | 라이선스입니다 |
라이선스 파일은 base가 갱신해도 자동으로 덮어쓰지 않습니다. base에서 갱신이 있었다면 안내만 띄우고 사용자가 직접 결정합니다.
.env* 파일도 자동 적용 영역 밖입니다. base가 .env.example에 새 변수를 추가했다면 그 변수를 본인 .env.local에 채워 넣으라는 안내도 같이 나옵니다.
mixed: 3-way 안내
섹션 제목: “mixed: 3-way 안내”CLAUDE.md와 README.md 둘입니다. base와 사용자 양쪽 편집이 동시에 들어갈 수 있는 파일이라서 자동으로 갱신하지 않고 diff만 보여주고 묻습니다.
옵션은 셋입니다.
- base 버전으로 통째로 덮습니다. 사용자 편집은 사라집니다.
- 3-way merge 를 합니다.
git merge-file로 합치고 충돌이 나면 직접 풉니다. - 미룹니다. 다음 호출까지 그대로 둡니다.
README.md는 phase 6(/6-cleanup-residue)이 본인 SaaS 이름과 설명으로 다시 쓰는 파일이라, base 변경분으로 통째로 덮으면 사용자 SaaS 이름이 사라집니다. mixed 로 처리하는 이유입니다.
사전 셋업은 한 번만 합니다
섹션 제목: “사전 셋업은 한 번만 합니다”base를 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 main을 실행합니다.git 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.json을 JSON으로 파싱해 보고, 새 스킬의 trigger 키워드를 나열합니다. .claude/state/base-upstream.json을 갱신합니다.- commit을 권합니다. 메시지는
chore(base): sync to {short-hash} ({N}개 파일)형태입니다.
기본 흐름은 30초에서 2분 사이에 끝납니다. mixed 파일 결정에 시간이 더 걸리는 분기가 하나 있습니다.
행 수준 접근 정책 마이그레이션은 user-owned입니다
섹션 제목: “행 수준 접근 정책 마이그레이션은 user-owned입니다”base가 새 행 수준 접근 정책 예시를 AI_AUTOMATION.md 섹션 6.6에 추가했다고 해도, 그 정책이 사용자 api/migrations/에 자동으로 들어가지 않습니다. 사용자 도메인 entity에 따라 정책 모양이 달라지기 때문입니다.
base가 기준을 정하는 것은 “정책을 어디에 두는지의 규칙” 이지 “어떤 정책을 둘지” 가 아닙니다. 후자는 사용자가 결정하고, phase 4의 데이터 스키마 결정과 phase 5의 implement-data-layer에서 채워집니다.
base 스킬을 본인이 수정한 흔적이 있을 때
섹션 제목: “base 스킬을 본인이 수정한 흔적이 있을 때”.claude/skills/{base-skill}/ 안의 파일을 사용자가 commit으로 수정한 적이 있으면 (git log로 감지합니다) 그 파일은 base-owned가 아니라 mixed로 처리됩니다. 자동으로 덮어쓰면 사용자 작업이 사라지기 때문입니다.
이 상황을 피하고 싶으면 base 스킬을 직접 수정하지 말고, 같은 trigger 키워드를 가진 별도 스킬을 자기 디렉터리에 만듭니다. 두 스킬이 같이 있으면 매뉴얼이 같은 키워드로 자기 쪽 스킬을 부릅니다. base가 그 스킬을 갱신해도 자기 스킬은 그대로 남습니다.
base가 force-push 했을 때
섹션 제목: “base가 force-push 했을 때”last_synced_commit이 base/main 이력에 더 이상 없으면 (force-push 된 경우입니다) 도구는 자동 적용을 멈추고 안내만 띄웁니다. 일반적인 경우는 아닙니다. base가 민감한 commit을 지웠거나 라이선스 위반 콘텐츠를 빼야 했을 때 발생합니다.
복구는 손으로 합니다.
- 사용자 영역(
src/,api/,docs/design/,docs/adr/,package.json,index.html,.env*)을 본인 브랜치로 빼 둡니다. - base를
base/main으로 reset 해서 받은 다음 새 base 위에 자기 작업을 다시 올립니다. - cherry-pick 또는 직접 복사로 사용자 영역을 복원합니다.
이 상황은 /recover-from-blocked가 다루는 영역이 아닙니다. force-push가 실제로 일어나면 base 메인테이너가 별도로 안내합니다.
왜 자동 적용이 아니라 영역 분류인가
섹션 제목: “왜 자동 적용이 아니라 영역 분류인가”가장 단순한 설계는 git pull base main 한 줄입니다. 그렇게 하지 않은 이유는 둘입니다.
라이선시는 자기가 구입한 시점의 코드 위에 자기 작업을 쌓은 사람입니다. git pull은 base 변경과 사용자 변경을 같은 층위로 보고 merge commit을 만듭니다. 비개발자는 그 merge commit과 뒤따르는 충돌 해결에서 그대로 막힙니다.
base-owned 영역은 base가 기준을 정한다는 합의가 있습니다. 그 합의가 있는 영역만 자동으로 덮어쓰는 것이 안전합니다. 합의가 없는 영역(mixed)은 사람이 결정하게 둡니다. fork가 갱신되지 않으면 base 가치가 구입 시점에서 멈추고, fork가 아무 보호 없이 갱신되면 사용자 작업이 사라집니다. /update-from-base는 그 둘 사이에 둔 채널입니다.
인접 문서
섹션 제목: “인접 문서”- 비개발자용 문체로 같은 절차를 따라가는 문서는 비개발자용 매뉴얼 업데이트에 있습니다.
- 이 스킬이 base 의 핵심 주장과 라이선스 모델 양쪽의 안전망인 이유는 intro의 “지금 빠져 있는 것” 단락과 함께 보면 이해하기 쉽습니다.