Skip to content

Deployment Pipeline

2026-09-10 #1491 승인 정책 (v0.17.8 대상): 일반 작업→release는 precheck 후 Merge Check만 수행하고, 최종 release→staging에서 full을 실행한다. staging→main은 동일 tree의 유효한 full 기록과 현재 staging 배포·smoke를 확인한다. 진단용 dry-run·부분 재현은 유지한다. 앱 main 6afe58ec 설치를 확인했다. 정상 큐의 새 실행·운영 배포 결과는 적용 기록에서 확인하며 아래 과거 실행 이력과 구분한다.

#1463 저장소 분리 이후 적용: 아래 앱 release·DB·데이터 보호 규칙은 앱 저장소에 적용한다. 과거 docs/admin 결합 빌드·같은 SHA 배포·앱 main 문서 직행·문서 문자열 검사 설명은 이관 전 기록이다. 모든 문서와 작업 기록은 dekerd/Barbelic-docs에서 관리하며, 현행 저장소 경계문서·관리자 독립 운영이 그 부분을 대체한다. 세션 제목은 현재 Phase/전체 Phase 규칙을 따른다. 코드 준비·원격 활성화·실제 배포 성공은 독립 운영 문서의 기록으로 구분한다.

2026-09-09 개정 (이슈 #1456·#1457·#1458, 오너 결정) — 명령·워크플로 이름이 바뀌었다. 작업 PR 통합 요청 npm run merge:request(구 landing:request), PR 전 사전 검증 npm run ci:precheck-local(최신 release 합치기 → verify → 서버 변경 시 DB 단계, 브라우저 없음), 전체 검증 npm run ci:full-local(릴리스 큐 runner 안에서만; 세션은 --only <단계> 재현만). 워크플로 표시 이름: 3-Release Merge Request(release-merge-request.yml) · GitHub Full CI(policy-contract.yml) · 1-Production Deploy/2-Staging Deploy(production-deploy.yml·staging-deploy.yml, 단계 정의는 deploy-steps.yml) · Production smoke (manual) · Social login daily check · Daily data backup. landing:lock·db:preflight 스크립트는 폐기됐다(GitHub concurrency 가 잠금, DB 검사는 사전 검증과 큐가 한다). 아래 본문의 옛 이름은 그 개정 전 기록이다. 2026-09-15 개정(#1659, 오너 지시) — 승격 CI·배포 실행의 이름은 [vX.Y.Z] Release -> Stage Merge · [vX.Y.Z] Staging Deploy · [vX.Y.Z] Stage -> Main Merge · [vX.Y.Z] Production Deploy 4종으로 고정하며 버전은 저장소 변수 RELEASE_VERSION에서 읽는다(실행 이름).

2026-09-15 개정(오너 지시, #1659) — v0.19.3 출시부터 QA1은 완전히 퇴역한다(워크플로 어디서도 자동 실행되지 않음). 승격 CI는 두 레인뿐이다: release→staging = precheck 레인(static-checks + migration-smoke: 마이그레이션 전체 재적용·schema.sql 대조), staging→main = QA2 레인(qa2-product-contracts)뿐. 동일 tree 성공 기록 재사용, QA1 단위·브라우저·viewport·e2e-evidence 잡, 배포 뒤 QA1 CRUD smoke 잡(smoke (staging/production)), prod-smoke.yml·social-login-daily-check.yml, 앱의 qa1.lock.json·scripts/qa1.mjs·npm run test·smoke:*·QA1 npm 자동 훅은 모두 제거됐다. 배포 단계는 DB → Edge → 프론트(→ Production은 release-tag)이며, 아래 본문의 smoke·browser-journeys 서술은 그 이전 기록이다.

브랜치·CI·승격의 정본은 **릴리스 프로세스**다. 확정 경로는 작업 → release → staging → main(Production)이며, release→staging은 최신 base의 최종 후보에서 GitHub full CI를 실행하고 staging→main은 유효한 동일 tree 성공 기록과 현재 staging 배포·smoke 증거를 확인한다. 이 문서는 새 브랜치 매핑과 DB → Edge Functions → frontend → smoke의 기술 순서를 설명한다.

DB 계약을 먼저 배포하고 검증한 뒤, 그 계약을 사용하는 Edge Functions와 프론트엔드를 배포한다. 브랜치 경로를 바꾸어도 이 순서는 유지한다.

v0.18.0 worker 전환 준비 — R05 #1544

R05 후보에 구현·로컬 리허설된 순서이며 실제 release/staging 반영은 R05 장부에서 확인한다. 기존 release-v0171-projection-rollout.mjs는 검증된 migration suffix가 있을 때 dry-run 이후, SQL 적용 전에 quiesce-stats-worker.mjs로 기존 worker가 쓰던 claim/compute advisory key를 한 psql 연결에서 확보한다. 진행 중 compute가 끝나고 live computed 결과가 정상 publish될 때까지 기다린 뒤 DDL을 시작한다. cron 설정과 pending/claimed/failed 작업·사용자 사실은 바꾸지 않는다.

준비는70초를 넘으면 적용 전에 실패하고, 성공·실패 모두 연결을 닫아 기존 worker를 재개한다. 준비 연결 유실은 실행 중 migration CLI의 AbortSignal로 전달한다. 이미 적용된 suffix면 정지하지 않으며 worker가 아직 없는 이전 스키마도 구분한다. 배포 DB 단계는 psql 가용성을 확인한 뒤 기존 DB→Edge→웹→smoke 순서를 유지한다. 새 배포 큐나 전역 승격 락을 추가하지 않는다.

실제188→219 전체31개 이관에서4년 입력의 최대 파일346ms/잠금0, 실제 사본359ms/잠금0을 확인했다. 기존60초는 파일별 최대 적용 시간이고 전체31개 wall69,825ms/70,191ms와 별개다. 적용 전 준비70초를 파일 적용·잠금 예산의 완화로 사용하지 않는다. 원래 무보호 잠금 실패와 원본·영수증·중단/재개 비교는 R05 실행 증거에 보존한다.

배포 후 사후 브라우저 자동 실행 중단 — #1564

2026-09-11 사용자가 반복 실패와 GitHub Actions 사용량을 확인한 뒤, 사후 브라우저 검사를 끄고 이후 배포에서도 실행하지 않도록 지시했다. 이후 “hotfix 트랙으로 검사없이 stage, main 머지”를 명시했다. staging #1566main #1567에 반영 완료했으며 main 1587cb7aa368294b9390c63f25b139193dd1ceb3에서 두 잡 비활성화를 확인했다. #1564 작업 기록에 기존 로컬 검증과 이번 검사 생략 병합을 구분한다.

  • deploy-steps.ymlbrowser-journeysbrowser-journeys-report는 모두 if: $false로 비활성화돼 있다. 이후 Production 배포를 해도 두 잡은 runner를 할당받지 않고 skipped로 남으며 검사·경고 댓글을 실행하지 않는다.
  • DB → Edge → frontend → 기본 CRUD smoke → Production 태그 순서와 필수 검사 조건은 유지한다. 공용 CASE·Playwright 설정·Full CI의 브라우저 검사도 유지한다.
  • 중단은 사후 실패14개를 수리하거나 통과 처리한 것이 아니다. 실제 운영 화면에서만 발생하는 문제의 자동 점검 범위는 줄어든다. 재개는 사용자 지시와 운영 환경에 맞는 검증을 갖춘 별도 변경으로 처리한다.
  • 수동 Production smoke (manual)과 소셜 로그인 정기 검사는 이번 자동 실행 중단 범위 밖이며 변경하지 않는다. 현재 요청으로 수동 브라우저 검사를 새로 실행하지 않는다.
  • 이번 한 건은 사용자가 승인한 hotfix 예외로 기존 관리자 권한·CI 생략 커밋을 사용했다. 원격 CI·Merge Check·추가 로컬 검사·자동 배포는 실행하지 않았으며 보호 규칙도 수정하지 않았다. 워크플로는 main 반영 자체로 다음 실행에 적용된다. 앱 재배포·새 태그 발행이나 미실행 검사의 성공을 주장하지 않는다. 이후 작업의 일반 검증 절차를 바꾸는 예외가 아니다.

v0.17.7 사후 브라우저 점검 분리 — #1485 (이전 운영)

2026-09-09 사용자 지시로 준비해 v0.17.7 Production에 반영한 변경이다. 아래는 #1564 중단 변경이 운영에 반영되기 전의 동작이다. 작업 기록에 커밋과 배포 증거를 구분해 기록한다.

  • 1-Production Deploy2-Staging Deploy가 공통 deploy-steps.yml을 호출한다. 앱 배포 순서는 DB → Edge → frontend → smoke이며, Production의 release-tag는 smoke 성공 직후 실행한다. 태그 실패는 여전히 배포 실패다.
  • Production browser-journeys는 smoke 뒤 태그와 독립적으로 실행한다. 잡의 continue-on-error: true로 사후 점검이 배포 성공을 막지 않으며 실행 상한은 20분에서 40분으로 늘린다. 테스트별 제한·실패 판정과 점검 계정의 production-smoke 직렬 실행은 유지한다.
  • 준비·테스트·결과 업로드가 모두 성공해야 마지막 단계가 성공 표식을 출력한다. 별도 browser-journeys-report 잡은 smoke 성공 뒤 이 표식이 없으면 실패·준비 오류·시간초과를 #1485에 실행 링크와 커밋을 포함한 댓글, Actions 경고 및 요약으로 남긴다. 알림 잡도 비차단이다. 댓글 전송 자체가 실패하면 Actions 경고·로그에서 확인할 수 있다.
  • 호출자 두 잡이 contents: writeissues: write를 전달하며, 재사용 워크플로에서는 태그 잡에만 contents 쓰기, 결과 보고 잡에만 issues 쓰기를 준다. Production 브라우저 trace·영상·스크린샷 비활성화와 JSON 결과 목록만 업로드하는 범위는 유지한다.
  • 태그 또는 배포 성공은 사후 브라우저 점검 통과를 뜻하지 않는다. 점검의 성공 표식과 실패 댓글을 별도로 확인한다. 전체 워크플로는 사후 잡 종료까지 실행 중일 수 있으나 태그 생성은 이를 기다리지 않는다.
  • CASE-011의 사전 DB 초안 정리는 추가하지 않았다. 문제 실행의 첫 실패는 해당 테스트 실행 중 새로 저장된 browser localStorage 초안이며, 기존 점검 계정 데이터가 원인이라는 추정은 확인되지 않았다. 기존 테스트가 소유한 데이터의 정리만 유지한다.

잡 의존성과 비차단 실패 처리는 GitHub Actions 워크플로 문법을 따른다.

2026-09-09: #1436에서 새 연결을 설치·활성화했다. 실제 배포 성공 여부는 실행 기록과 구분한다. 새 매핑은 main=Production, staging=staging이다. repository variable DEPLOYMENT_BRANCH_CUTOVER=true가 설정되어 새 Deploy가 활성화됐다. Vercel 앱·docs의 Production=main, custom staging=staging과 docs staging 공개 Supabase 값·BARBELIC_TARGETVERCEL_DOCS_PROJECT_ID 설정을 원격 확인했다. 로컬 docs Production 및 staging 관리자 빌드·산출물 검사는 통과했지만 원격 main 설치·활성화·Linux Deploy 검증은 아직 완료하지 않았다. 실제 완료 상태는 릴리스 프로세스의 체크리스트에 실행 링크·SHA와 함께 기록한다.

환경 분리 이후의 배포 (deploy.yml)

이 절은 전환 코드를 설치하고 활성화 변수를 설정한 뒤의 동작이다. 아래 "Production Deployment"의 수동 명령들은 비상 복구 절차로만 남고, 평시 배포는 전부 .github/workflows/deploy.yml이 한다.

  • staging = staging. release/vX.Y.Z → staging 승격 PR 병합 후 deploy.yml이 staging Supabase (ajqddlglezvxzsawquku)에 마이그레이션·Edge Function을 적용하고 Vercel staging 환경에 배포한다.
  • main = Production. 릴리스 = 검증한 staging → main PR 병합(오너 승인 필수). 머지 푸시가 deploy.yml의 production 레인을 시작한다. GitHub Environment production의 required reviewer(오너)가 설정돼 있으면 Production 잡은 오너의 승인 클릭까지 멈춘다.
  • 순서는 잡 의존으로 강제된다: database(링크 프리플라이트 → [production] staging-applied 게이트 → dry-run → [production] dry-run 목록 = release diff → db push → remote-schema 게이트) → functions(배포 + 버전 헤더 프로브) → frontend(Vercel CLI prebuilt) → smoke(CRUD roundtrip). DB·함수가 초록이기 전에는 프론트가 나가지 않는다.
  • 앱과 docs의 vercel.json에서 main·staging Git 직접 배포를 끄고 DB보다 프론트가 먼저 배포되는 것을 막는다. 앱의 git 통합은 PR 프리뷰에 사용하며, 프리뷰는 Vercel Preview 환경변수로 staging을 본다.
  • 사람이 Production에 db push --linked·functions deploy를 하지 않는다(오너 결정 D5). staging 조작은 전용 폴더에서만 하고 본체 체크아웃의 링크는 바꾸지 않는다 (environment-separation-runbook.md의 링크 규칙).
  • 빌드는 자기 배포 대상을 선언한다 (이슈 #1332, 빌드·배포 대상 표). frontend 잡이 BARBELIC_TARGET=<staging|production> 으로 vercel build 를 돌리고, Vite 플러그인(vite.deploymentTarget.mjs)이 대상과 VITE_SUPABASE_* 가 어긋나면 빌드를 실패시킨다 — staging 값이 빠졌다고 Production 을 바라보게 두지 않는다. 배포 직전 scripts/check-build-artifact.mjs 가 산출물의 대상 도장(__BARBELIC_DEPLOYMENT__)과 서버 비밀 부재를 확인한다(값은 출력하지 않는다). smoke·DB 비밀번호·프로젝트 ref 가 없으면 해당 잡이 이름을 적고 실패한다(건너뜀·다른 환경 값 대체 없음).
  • 브랜치 생성 push는 배포를 일으키지 않는다. 수동 실행도 Production은 main, staging은 staging에서만 허용한다. resolve와 DB·Edge·프론트 쓰기 잡에서 대상 브랜치의 최신 SHA를 다시 확인한다. 성공했던 resolve를 재사용하는 실패 잡 재실행도 오래된 SHA이면 중단한다.
  • 작업→release의 release별 슬롯은 최신 후보의 Merge Check·병합까지만 보유한다. 명시적 dry-run만 같은 슬롯에서 진단용 full을 실행한다. 두 승격은 담당자가 한 후보씩 진행하며 별도 전역 락이나 CI·병합·배포를 묶는 새 큐를 사용하지 않는다.
  • 릴리스 버전(SemVer): 릴리스 PR 제목에 vX.Y.Z를 넣는다(수정만=patch, 기능 포함=minor, 큰 이정표=major — 정식 출시가 v1.0.0). 릴리스 PR은 Create a merge commit으로만 머지한다(squash/rebase 금지 — staging과 main의 승격 이력을 보존한다). v0.17.7의 #1485 적용 후에는 Production smoke가 성공하면 release-tag 잡이 머지 커밋 메시지에서 버전을 찾아 git 태그 + GitHub Release(자동 릴리스 노트)를 만든다. 버전이 없으면 태그만 건너뛴다(배포는 정상). DB·Edge·frontend·smoke가 실패하면 버전이 붙지 않는다. 사후 브라우저 점검 결과는 별도다.
  • docs/admin도 앱과 같은 환경·배포 SHA로 전달한다. DB·Edge 검증 이후 Actions가 별도 docs Vercel 프로젝트를 빌드·배포하며, BARBELIC_TARGET=staging 또는 production을 명시한다. staging 관리자 셸에는 staging Supabase 공개 URL/key를 사용한다. 관리자 패널에 별도 제품 버전을 만들지는 않지만 서버 호환을 바꾸는 관리자 변경은 승격 PR의 포함·검증 목록에 기록한다.
  • 순수 Markdown을 main에 CI·배포 생략 예외로 직접 합치면 문서 사이트는 다음 해당 환경 배포에서 갱신된다. main 푸시로 docs/admin이 먼저 공개되던 이전 Git 배포 경로는 새 프로세스의 기준이 아니다.

Required Release Order

아래는 활성화 이후 release → staging → main의 배포 순서다. 코드 설치와 활성화 확인은 릴리스 프로세스의 전환 목록을 따른다.

  1. release/vX.Y.Z → staging의 최신 base에서 GitHub Full CI의 precheck 레인(static-checks + migration-smoke)을 통과하고 병합합니다(2026-09-15 개정). staging 배포(database·functions·frontend)·QA의 성공 SHA와 tree를 확인합니다.
  2. staging → main 릴리스 PR(제목 [Release vX.Y.Z] 요약, Create a merge commit)의 최신 base에서 GitHub Full CI의 QA2 레인(qa2-product-contracts)을 통과하고 오너가 승인합니다(2026-09-15 개정). QA한 tree와 동일한 결과를 병합하면 아래 3~8을 deploy-steps.yml(1-Production Deploy·2-Staging Deploy 가 호출) production 레인으로 집행합니다.
  3. Supabase DB migration을 dry-run 하고, dry-run 목록이 릴리스 diff와 같은지 확인합니다(release diff 게이트).
  4. Supabase DB migration을 적용합니다.
  5. 원격 migration 이력과 exact RPC contract를 검증합니다.
  6. Supabase Edge Functions를 배포하고 health probe를 확인합니다.
  7. Vercel production deployment 완료를 기다립니다(frontend 잡 — DB·함수가 초록이기 전에는 시작하지 않습니다).
  8. 운영 canary(smoke)를 수행합니다.
  9. smoke 성공 직후 Production 태그·GitHub Release를 생성합니다. #1564 반영 이후 사후 브라우저 점검과 경고 잡은 비활성화돼 실행하지 않습니다.

DB 검증보다 frontend activation이 앞설 수 없습니다. 특히 writer RPC를 교체하는 릴리스는 구 RPC fallback을 두지 않으므로 이 순서를 위반하면 저장 CRUD가 즉시 실패합니다.

Pre-Merge Gate

릴리스 브랜치에서 다음 명령을 모두 통과시킵니다.

bash
npm run check:deployment
npm run check
npm run build

npm run check:deployment는 Edge Function manifest, 문서 링크, 배포 순서를 검사합니다. npm run check는 migration 및 정책 계약과 TypeScript/tests를 함께 검사합니다.

Production Deployment

1. DB migration dry-run

bash
npx supabase@latest db push --linked --dry-run

출력에 릴리스에 포함된 migration만 나타나는지 확인합니다.

2. DB migration apply

bash
npx supabase@latest db push --linked

3. Remote DB contract gate

bash
npm run check:remote-schema

이 gate는 다음을 모두 검증해야 합니다.

  • 모든 로컬 migration version이 supabase_migrations.schema_migrations에 적용됨
  • current write RPC의 exact signature가 존재함
  • current writer body가 구 writer/reconcile RPC를 호출하지 않음
  • 구 workout writer/reconcile RPC가 원격에서 제거됨
  • rolling read contract인 get_volume_overview(date,integer) selector의 v3/v4가 존재하고 각 payload를 유지하며, v2 presentation이 private get_volume_overview_v2_core(date)로 남아 있음. 공개 한 인자 오버로드 get_volume_overview(date)20260820120300이 드롭했으므로 원격에 있으면 안 됩니다

현재 세션(완료·계획·그룹 운동 계획) write contract(이슈 #1215, 2026-09-04):

txt
public.save_session_v5(jsonb,uuid,text)
public.delete_session_v5(uuid,bigint,uuid,text)

아래 옛 문은 원격에 LG426 스텁(아무것도 쓰지 않고 "앱 업데이트 필요"를 던지는 함수)으로만 남아 있어야 한다 — 실제 쓰기 본문이 남아 있으면 안 된다:

txt
public.save_workout_v4(jsonb,uuid,text)
public.update_completed_session_v4(uuid,jsonb,bigint,uuid,text)
public.delete_completed_session_v4(uuid,uuid,bigint,uuid,text)
public.save_plan_v3(jsonb,uuid,text)
public.delete_planned_session_v2(uuid,uuid,timestamptz,uuid,text)

4. Supabase Edge Functions deploy

운영 project ref는 kobxeylancdimqhfkbnl입니다.

bash
npx supabase@latest functions deploy stats-process-refresh-jobs --project-ref kobxeylancdimqhfkbnl
npx supabase@latest functions deploy wodup-process-import-jobs --project-ref kobxeylancdimqhfkbnl
npx supabase@latest functions deploy wodup-start-import --project-ref kobxeylancdimqhfkbnl

각 함수의 x-barbelic-deployment-version 헤더(전이 기간 동안 구 x-lift-guild-deployment-version도 병행 노출)와 deployment_version 응답을 확인합니다.

5. Frontend activation

DB와 Edge Function 검증이 끝난 뒤에만 프론트를 활성화합니다.

새 브랜치 매핑 활성화 이후(맨 위 절): 평시에는 staging → main 승격 PR 병합이 deploy-steps.yml(1-Production Deploy·2-Staging Deploy 가 호출)의 production 레인을 돌려 여기까지 자동 집행합니다. staging 병합은 staging 환경에 배포합니다. 아래 문장은 비상 복구로 사람이 직접 배포할 때의 기준입니다.

txt
https://www.barbelic.com

공개 웹과 새 iOS 빌드의 정규 주소는 모두 https://www.barbelic.com이다. preview 성공은 운영 활성화 완료를 의미하지 않습니다.

Post-Deployment Verification

bash
npm run check:remote-schema
curl -I https://www.barbelic.com/
curl -i https://kobxeylancdimqhfkbnl.supabase.co/functions/v1/wodup-start-import
curl -i https://kobxeylancdimqhfkbnl.supabase.co/functions/v1/wodup-process-import-jobs
curl -i https://kobxeylancdimqhfkbnl.supabase.co/functions/v1/stats-process-refresh-jobs

운영 canary는 기존 세션 복구, 홈 로딩, 달력 조회, 세션 생성·수정·삭제, 계획 생성·삭제를 포함합니다. 관리자 운영 콘솔의 Edge Function 배포DB migration 상태도 확인합니다. 정규 웹 origin의 /은 직접 200을 반환해야 한다.

Vercel 자동 배포 범위

새 배포 경로의 main/staging은 앱·docs 모두 직접 Git 배포를 끄고 Actions가 DB·Edge 이후 배포한다. 아래 브랜치 필터는 Git 프리뷰 범위에 적용한다.

에이전트 브랜치는 프리뷰를 만들지 않는다. vercel.jsongit.deploymentEnabledclaude/*agent/*를 끄고, 앱 프로젝트만 claude/ui-*를 다시 켠다 — 디자인 딜리버리는 프리뷰가 리뷰 수단 자체이기 때문이다. 한 브랜치가 여러 규칙에 걸리면 하나라도 true면 배포되므로 claude/ui-v64는 앱 프리뷰를 받고 문서 프리뷰는 받지 않는다.

특정 브랜치의 프리뷰가 필요하면 그 맵에 "<브랜치>": true 한 줄을 넣거나, 브랜치 이름을 두 접두사 밖으로 두면 된다.

전면 차단("deploymentEnabled": false)은 쓰지 않는다. 두 가지 이유가 있다. 첫째, Vercel 문서는 이 설정의 전신인 github.enabled = false가 있으면 Deploy Hook이 아예 트리거되지 않는다고 명시한다 — 전면 차단 후 수동 배포 수단까지 잃을 위험이 있다. 둘째, deploymentEnabled가 무시되고 빌드가 계속 걸린다는 보고(vercel/vercel#11176)가 있어, 차단을 신뢰하려면 실측이 먼저다. 필요해지면 Deploy Hook을 먼저 만들어 동작을 확인한 뒤에 바꿀 것.

과거 Git 직접 배포에서는 Settings → Environments → Production → Branch Tracking → Auto-assign Custom Production Domains를 끄고 Staged 배포를 수동 promote하여 공개 시점을 제어했다. 새 main/staging 경로의 평시 배포는 Actions가 DB·Edge 성공 뒤 실행하므로 이 수동 promote 절차를 요구하지 않는다.

staged 배포도 빌드는 그대로 한다 — 도메인만 안 붙일 뿐이라 하루 배포 한도는 똑같이 소비한다. 한도 문제를 푸는 것은 위의 브랜치 필터와 아래의 경로 필터이고, staged는 언제 보일지를 정하는 별개 수단이다.

관리자 셸 — barbelic-docs /admin

관리자 페이지(운동 종목 카탈로그·계열·디테일·Wodup 매핑·Wodup 인입·운영 콘솔)는 앱이 아니라 문서 사이트 프로젝트(barbelic-docs)의 /admin/ 에서 서비스한다 (2026-08-21 이전). 소스는 앱과 같은 src/react(관리자 UI·컨트롤러는 제자리)이고, 엔트리만 다르다.

  • 엔트리: admin/index.htmlsrc/react/vite/admin/main.tsx (독립 셸: 로그인 게이트 → is_lift_guild_admin 권한 게이트 → 운동 종목 카탈로그 위에서 DesktopAdminFeature 구동).
  • 빌드: vite.admin.config.mjs (레포 루트 기준, 산출물 admin/index.html + admin/assets/*). 로컬 확인은 npm run dev:adminhttp://127.0.0.1:5175/admin/, 번들 확인은 npm run build:admindist-admin/admin/.
  • docs 동봉: docs/package.jsonbuildvitepress builddocs/scripts/build-admin.mjs를 실행해 BARBELIC_ADMIN_OUT_DIR=.vitepress/dist로 같은 산출물 안에 /admin/을 만든다. Actions는 DB·Edge 이후 같은 SHA의 docs/admin을 별도 Vercel 프로젝트에 배포한다. 빌드의 BARBELIC_TARGET과 해당 환경의 공개 Supabase 값이 일치해야 하며 /admin/index.html 존재와 대상 도장을 검사한다. docs/vercel.json의 main/staging 직접 Git 배포는 끄고 기존 경로 필터는 프리뷰 범위에만 사용한다.
  • 서버 측은 무변경: 관리자 RPC·RLS는 전부 is_lift_guild_admin()으로 잠겨 있어 origin과 무관하다. 임퍼서네이션(view-as-user)은 앱 오버레이이므로 앱에 남는다.
  • 데스크톱 앱 쪽은 제거됐다: 사이드바 '관리자' 항목·/admin(?view=dashboard.admin, #dashboard.admin) URL 라우트·adminRuntime 공급이 없고, 앱 번들은 관리자 feature를 싣지 않는다. 관리자 UI·컨트롤러 파일(ui/desktop/screens/DesktopAdmin*.tsx, features/admin/, controllers/admin*.ts, desktop-admin.css)은 독립 셸이 제자리에서 import 한다.

오너 콘솔 선행 작업(한 번):

  1. Vercel barbelic-docs → Settings → General → Root Directory(docs) 아래 "Include source files outside of the Root Directory in the Build Step" 를 켠다. 꺼져 있으면 build-admin.mjs가 레포 루트를 찾지 못해 관리자 셸만 건너뛰고(경고 출력) 문서 사이트는 그대로 배포된다 — 실패가 아니라 /admin/ 404 로 나타난다.
  2. Supabase → Authentication → URL Configuration → Redirect URLs 에 docs 도메인 https://<docs-domain>/admin/** 을 추가한다. 카카오 OAuth 콜백은 현재 origin으로 돌아오므로 이 등록이 없으면 관리자 로그인이 완료되지 않는다. (로컬은 http://127.0.0.1:5175/**.)
  3. Vercel barbelic-docs 의 Node.js 버전이 루트 package.json engines(^22.13.0 || >=24)를 만족하는지 확인한다 — 관리자 셸 빌드는 레포 루트에서 npm ci 후 vite를 실행한다.
  4. docs 프로젝트의 Production은 main, custom staging은 staging으로 맞춘다. docs staging의 공개 Supabase URL/key와 인증 redirect는 staging 프로젝트에 맞추고 운영 값과 섞지 않는다. 앱과 별도 docs 프로젝트 ID를 Actions의 repository variable VERCEL_DOCS_PROJECT_ID로 지정한다. 환경 생성·설정 확인과 실제 배포·로그인 검증은 각각 기록한다.

Vercel 빌드 스킵 조건

아래는 2026-08-20에 도입한 Git 직접 빌드 필터의 배경과 프리뷰 운영 규칙이다. 새 main/staging 배포에는 이 경로 필터로 앱·docs/admin을 생략하지 않고 Actions가 같은 SHA·환경을 함께 배포한다.

이 저장소는 Vercel 프로젝트 둘(barbelic 앱, barbelic-docs 문서 사이트)에 함께 연결돼 있다. 기본 동작은 모든 푸시가 두 프로젝트를 다 빌드하는 것이라, 마이그레이션만 바꾼 커밋도 문서 사이트를 다시 빌드하고 문서만 바꾼 커밋도 앱을 다시 빌드했다. 2026-08-20에 하루 배포 100건(Hobby 한도)을 소진해 Deployment rate limited — retry in 24 hours로 체크가 막혔다.

그래서 각 vercel.jsonignoreCommand로 경로 필터를 걸었다. 명령이 0으로 끝나면 빌드를 건너뛰고, 0이 아니면 빌드한다 (git diff --quiet의 반환값이 그대로 맞아떨어진다).

  • (vercel.json) — index.html, app-config.js, vite.config.mjs, package.json, package-lock.json, vercel.json, src/, public/, api/ 중 하나라도 바뀌어야 빌드한다. 이 목록이 곧 vite 빌드와 Vercel 함수의 입력 전부다.
  • 문서 (docs/vercel.json) — 이 프로젝트의 루트 디렉터리가 docs/./ 에 더해, 동봉하는 관리자 셸의 입력(../admin, ../src, ../app-config.js, ../vite.admin.config.mjs, ../package.json, ../package-lock.json)을 감시한다.

목록에 없는 것(supabase/, tests/, test/, e2e/, scripts/, ios/, error-cases/)은 빌드 산출물에 들어가지 않으므로 스킵이 정답이다. 2026-08-20 커밋 27건에 시뮬레이션하면 Production 빌드가 54회에서 24회로 준다.

앱 목록에 새 빌드 입력을 추가할 때는 ignoreCommand도 같이 늘릴 것. 빠뜨리면 그 변경만 담은 커밋이 조용히 배포되지 않고 직전 배포가 그대로 서비스된다 — 실패가 아니라 무반응이라 알아채기 어렵다. 반대로 판단이 서지 않으면 목록에 넣는 쪽이 안전하다.

스킵된 배포는 Vercel에서 별도 rate-limit 버킷(Skipped deployments per minute)으로 세고, 빌드 시간을 소비하지 않는다.

Rollback Notes

단계별 절차는 rollback.md가 정본이다 — 층별 판단, Vercel Instant Rollback 클릭 순서, 되돌린 뒤 브랜치·버전 정리, 수정 릴리스 후 도메인 되살리기까지. 아래는 그 문서가 따르는 방침이다.

  • frontend-only regression은 Vercel의 이전 production deployment로 되돌립니다 (Instant Rollback). 되돌리면 실서비스 도메인 자동 배정이 꺼지므로, 이후 수정 릴리스는 배포가 초록이어도 도메인이 옛 배포본을 가리킬 수 있습니다 — frontend 잡의 "public origin must serve this deployment" 검사가 이 어긋남을 잡습니다.
  • Volume v4 frontend rollback은 보존된 selector v3를 사용하므로 DB v3/v4 selector를 forward-fix 없이 제거하지 않습니다. v2 presentation은 20260820120300 이후 private core라 클라이언트 rollback 경로가 아닙니다 — v2로 되돌려야 하면 selector를 경유하는 forward-fix migration이 필요합니다.
  • DB migration은 이미 적용된 파일을 수정하지 않고 새 forward-fix migration을 추가합니다.
  • writer contract regression은 구 RPC를 다시 살리는 방식으로 우회하지 않습니다. current contract를 고치는 migration을 먼저 적용한 뒤 frontend를 재배포합니다.
  • Edge Function regression은 이전 정상 함수 버전을 재배포하거나 hotfix 함수를 배포합니다.

docs Vercel CLI 실행 위치

CLI는 저장소 루트에서 --local-config docs/vercel.json을 지정해 실행한다. Vercel 프로젝트 Root Directory는 docs를 유지한다. CLI까지 docs 디렉터리에서 실행해 경로가 중복 적용되지 않게 한다. docs 설정의 installCommand: npm ci로 자체 lockfile의 VitePress 의존성을 설치하며, 관리자 빌드 스크립트는 필요한 루트 의존성을 별도로 준비한다.