콘텐츠로 이동

베이스 업데이트 받기

라이선시 fork는 한 번 받고 끝이 아닙니다. base(ddakit/saas-base)가 새 스킬을 추가하거나, 훅 동작을 고치거나, AI_AUTOMATION.md의 도메인 규칙을 갱신할 때마다 그 변경이 fork에 도달해야 라이선스 가치가 유지됩니다. 이 문서는 git rebase를 몰라도 안전하게 받게 해 주는 /update-from-base가 어떤 가정 위에서 동작하는지 정리합니다.

자동으로 적용할 수 있는 영역, 절대 건드리지 않는 영역, 사람이 결정하는 영역. 이 셋으로 나누는 것이 핵심입니다.

업데이트가 무엇을 덮어쓰고 무엇을 그대로 두는지는 파일이 어느 영역에 속하는지로 결정됩니다.

base가 기준 문서를 가지고 있는 영역입니다. 사용자가 편집해도 다음 update에서 base 버전으로 돌아갑니다.

경로무엇
.claude/skills/{base-skill-name}/base 스킬 정의입니다 (phase 1~6, implement-*, review-saas, test-saas, deploy-saas, /update-from-base 등)
.claude/agents/*.mdbase agent 정의입니다 (saas-architect, saas-builder, saas-reviewer)
.claude/hooks/*.sh + .claude/hooks/README.mdbase 훅입니다 (session-start, stop-reminder, pre-commit-check)
.claude/settings.jsonbase 훅 등록 파일입니다
AI_AUTOMATION.md도메인 기준 문서입니다
AGENTS.mdCodex 진입점입니다

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.htmlCSP와 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에 채워 넣으라는 안내도 같이 나옵니다.

CLAUDE.md와 README.md 둘입니다. base와 사용자 양쪽 편집이 동시에 들어갈 수 있는 파일이라서 자동으로 갱신하지 않고 diff만 보여주고 묻습니다.

옵션은 셋입니다.

  1. base 버전으로 통째로 덮습니다. 사용자 편집은 사라집니다.
  2. 3-way merge 를 합니다. git merge-file로 합치고 충돌이 나면 직접 풉니다.
  3. 미룹니다. 다음 호출까지 그대로 둡니다.

README.md는 phase 6(/6-cleanup-residue)이 본인 SaaS 이름과 설명으로 다시 쓰는 파일이라, base 변경분으로 통째로 덮으면 사용자 SaaS 이름이 사라집니다. mixed 로 처리하는 이유입니다.

base를 git remote로 등록합니다.

Terminal window
git remote add base https://github.com/ddakit/saas-base.git
git remote -v

git 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 또는 자연어로 “베이스 업데이트 받아줘” 라고 부르면 다음을 거칩니다.

  1. git status --porcelain으로 dirty 상태를 검사합니다. 저장하지 않은 변경이 있으면 git stash를 권합니다.
  2. git fetch base main을 실행합니다.
  3. git diff --name-status <last_synced_commit>..base/main으로 변경 파일을 영역별로 분류합니다.
  4. base-owned 변경 요약을 출력합니다. 새 스킬 N개, 갱신된 훅 M개, AI_AUTOMATION.md 갱신 여부를 한 줄씩 적습니다.
  5. 사용자가 yes 를 고르면 git checkout base/main -- .claude/skills/ .claude/agents/ .claude/hooks/ .claude/settings.json AI_AUTOMATION.md AGENTS.md를 실행합니다.
  6. mixed 파일은 한 개씩 옵션 셋 중 하나를 골라 적용합니다.
  7. 적용한 다음 검증합니다. .sh 파일에 chmod +x를 적용하고, settings.json을 JSON으로 파싱해 보고, 새 스킬의 trigger 키워드를 나열합니다.
  8. .claude/state/base-upstream.json을 갱신합니다.
  9. 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가 그 스킬을 갱신해도 자기 스킬은 그대로 남습니다.

last_synced_commit이 base/main 이력에 더 이상 없으면 (force-push 된 경우입니다) 도구는 자동 적용을 멈추고 안내만 띄웁니다. 일반적인 경우는 아닙니다. base가 민감한 commit을 지웠거나 라이선스 위반 콘텐츠를 빼야 했을 때 발생합니다.

복구는 손으로 합니다.

  1. 사용자 영역(src/, api/, docs/design/, docs/adr/, package.json, index.html, .env*)을 본인 브랜치로 빼 둡니다.
  2. base를 base/main으로 reset 해서 받은 다음 새 base 위에 자기 작업을 다시 올립니다.
  3. 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의 “지금 빠져 있는 것” 단락과 함께 보면 이해하기 쉽습니다.