v0.18.0 B02 — 환경 변수를 빠뜨린 스테이징 빌드가 조용히 Production 데이터베이스를 바라보던 것에서 배포 대상 원천 한 곳·빌드/배포 경계의 명시 오류·도구 버전 원천·자원 예산 계약·설치본 호환 표·Android release 실측까지 (2026-09-07)
- 기간: 2026-09-07 ~ 2026-09-07 (세션 2개 —
d24267a7분석·계획 게시 → 오너 "고" → Phase 1~3 + Phase 4 착수(사용량 한도로 중단),7cd84cddPhase 4 이어받기 → Phase 5 랜딩. 오너 지시 "1332 이어서 진행해줘 묻지말고 phase 끝까지 완주") - 랜딩: PR #1356(Phase 1~5 한 PR,
landing:request자동 squash) — 마이그레이션·엣지 함수·화면 변경 없음. 앱 번들에 실리는 변경은app-config.js(도장__BARBELIC_DEPLOYMENT__를 읽고 없으면 시작 거부)와 상수 모듈appResourceContract.ts(import 없음 → 트리셰이킹)뿐. 빌드 시간 코드vite.deploymentTarget.mjs(대상 선언·검증·도장). 워크플로 6개·package.json·.nvmrc·playwright.config.mjs변경. 선행 G0580572704·G03e94f0d34·G04beff760b머지 확인 뒤 착수, 랜딩 전 origin/mainba0f4035(#1345·#1346) 리베이스 - 설계서: 없음 — 분석·Phase 계획은 이슈 #1332 댓글("예상 효과·개선사항" 절 포함)
- 정본:
docs/platform/build-targets.md(target 표·경계 검사·도구 원천·호환 표·native 빌드 증거) ·deployment-targets.json(배포 대상 값의 원천, 공개 값만) ·docs/gates/resource-budgets.md+ 코드src/react/services/appResourceContract.tsRELEASE_RESOURCE_CONTRACT(45항목) ·.nvmrc·package.jsonvercelToolchain·supabaseToolchain·vite.deploymentTarget.mjs·scripts/check-build-artifact.mjs·scripts/check-deployment-pipeline.mjs(복사본 12곳 + 설치본·SW·Edge 버전 대조) · 배포 파이프라인·환경 분리 런북 갱신 절 - 도구:
scripts/resources/probe-production.mjs(Management API 읽기 전용, 결과scripts/resources/evidence/production-2026-09-07.json) ·scripts/check-build-artifact.mjs(--dir --target, 값 출력 없이 도장·비밀·source map 검사) · Android 빌드 로그·app-release.aab는 세션 scratchpad(레포 밖) - 게이트: 새 테스트
tests/react/deploymentTargets.test.mjs12건 ·ciToolchain.test.mjs7건 ·resourceContract.test.mjs5건, 갱신appConfigEnv.test.mjs(7건, "값 없으면 Production" 정답 고정 제거)·ciLocal.test.mjs.test/e1rmPolicy.test.mjs(매니페스트 등재) 의 워크플로- run:목록 기대값 변경은tests/audit/pending-changes.json신고. CI:policy-contract.ymlstatic-checks 에build:admin·check:artifacts, 새docs-build잡(verify 필수).deploy.yml: 배포 직전 산출물 검사·비밀값 존재 검사·Production smoke URL 고정. 로컬npm run check전체 통과(정적 게이트 + 단위 2,990). 마이그레이션·pgTAP 미접촉. 검증: ci:local full(--full) · scope full(바뀐 파일 41개) · verify 통과 · 샌드박스(Postgres 17.6.1.127 = Production 핀) 마이그레이션 전체 적용·schema 스냅샷 대조·pgTAP 109파일 1,900 assert 통과(11:22Z) · e2e local 11/11 · empty 7/7 · cardio 6/6 · persistence 1/1 통과 · browser 단계는 다른 세션의 4173 포트 점유로 exit 3(2회) → 같은 샌드박스·CI=true·미리보기 4179 포트에서test:e2e-browser38/38(CASE-017 은 미리보기 서버에 샌드박스 Supabase env 를 넘기지 않은 첫 실행에서 410 으로 실패 → env 를 준 뒤 재실행 통과) ·test:e2e-viewport14/14 (11:50Z) - 버그리포트: 없음(수리 건 아님 — 발견 사항은 §4 "작업 중 드러난 것" 과 HQ 갱신안)
- 계약: 새 계약 문서 2(build-targets·resource-budgets). 기존
RELEASE_PERFORMANCE_BUDGETS(G04) 상한 무변경(넓히지 않음).docs/process/deployment-pipeline.md·environment-separation-runbook.md에 대상 선언 규칙 추가
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 0 | 조사(환경 값 복사본 8곳·워크플로 6개·native 셸·자원 한도 실태)·계획 게시 | ✅ 이슈 댓글 |
| Phase 1 | 배포 대상 원천 deployment-targets.json·빌드 대상 선언/검증 플러그인·index.html 도장·산출물 검사·deploy.yml 비밀/대상 검사·복사본 12곳 대조·target 표 문서 | ✅ 4a191520(리베이스 전 d704c571) |
| Phase 2 | Node 원천 .nvmrc(워크플로 13곳)·Vercel CLI 핀·setup-cli SHA 핀·Production 카나리 trace/video 끄기·비밀 env: 경계·docs-build 잡 | ✅ a51ea6c0(f269d353) |
| Phase 3 | 자원 예산 계약 45항목(Production 실측·플랫폼 문서·코드 상수·목표·미측정 구분)·읽기 전용 프로브·대조 테스트 | ✅ 844c3078(b12ff08b) |
| Phase 4 | 관리자·웹·서버·설치본 호환 표·설치본/SW/Edge 버전 대조·Android release 빌드 실측·iOS 미실행 인계·N01 인계 | ✅ d43fcd3b·ce8a8391·b50a3855 |
| Phase 5 | 이 기록·등록 2곳·ci:local(브라우저 e2e 가 잡은 Playwright 대상 선언 누락 수리 24ccc66d)·PR·landing:request·staging 확인·HQ 갱신안·인계 | ✅ PR #1356 |
1. 배경
v0.18.0 은 web·관리자·Edge 함수·iOS/Android 셸이 같은 소스에서 나가되 실행 환경·비밀·도구·자원 예산은 다르다는 전제로 작업을 나눈다(총괄 F23·F29·F30·C08). G05 가 실제 진입점을, G04 가 자원 기준선을 만들었지만 "이 빌드가 어느 환경으로 나가는가" 는 여전히 환경 변수의 유무로 추측했고, 환경 값의 복사본은 여덟 곳에 손으로 적혀 있었으며, 연결 수·timeout·본문 크기 같은 한도는 어디에도 선언돼 있지 않았다.
2. 문제 제기
변수를 빠뜨린 스테이징 빌드가 오류 없이 Production 데이터베이스를 바라봤다
app-config.js 의 기본값이 Production 프로젝트라서 Vercel staging 환경에 VITE_SUPABASE_URL 이 없으면 빌드·배포·smoke 전부 초록인 채 스테이징 앱이 Production 에 기록을 저장한다. appConfigEnv.test.mjs 는 이 동작을 정답으로 고정하고 있었고, 배포 뒤 번들이 어느 프로젝트를 가리키는지 확인하는 단계가 없었다.
비밀값이 없어도 배포가 초록으로 끝났다
deploy.yml smoke 잡은 계정·URL 비밀값이 없으면 "건너뜀" 안내만 내고 성공했다. DB 비밀번호를 production && PROD || STAGING 식으로 고르면 Production 값이 비어 있을 때 staging 비밀번호로 넘어가 supabase link 에서 원인 불명의 인증 오류로 죽었다.
같은 값이 여덟 곳에 손으로 적혀 있었다
Production ref·공개 origin·인증 origin·콜백 scheme 이 app-config.js·config.toml·deploy.yml·스크립트 2·iOS xcconfig 2 + Swift·Android gradle + Kotlin 에 각각. 한 곳을 바꾸면 나머지가 어긋나도 아무 검사가 잡지 않았다. 릴리스 식별(커밋 SHA)은 아무 데서도 주입하지 않아 오류·성능 이벤트가 청크 파일 이름으로 기록됐다(G04 발견).
도구 버전의 원천이 흩어져 있었다
Node 는 워크플로 13곳에 22 손 기입 + node_modules 캐시 키에 node22 고정(Node 를 올려도 옛 캐시 재사용), Vercel CLI 는 배포 경로에서 @latest, supabase/setup-cli@v1 은 움직이는 브랜치. 관리자 셸 빌드와 docs 프로젝트(VitePress, 자체 lock)는 CI 가 한 번도 설치·빌드하지 않아 깨지면 Vercel 배포 시점에만 드러났다.
Production 세션 토큰이 artifact 에 남을 수 있었다
Production 카나리(prod-smoke.yml)가 실제 계정으로 로그인한 뒤 Playwright trace·video 를 30일 artifact 로 올렸다 — trace 에는 세션 토큰이 들어갈 수 있고 GitHub 의 비밀값 마스킹은 artifact 내용에 적용되지 않는다. deploy.yml smoke 는 비밀값 4개를 $GITHUB_ENV 로 잡 전체에 퍼뜨렸다.
자원 한도가 계약이 아니라 플랫폼 기본값이었다
config.toml 에 연결 수·timeout·파일 크기 한도가 없고, Vercel/Edge 함수 한도는 어디에도 적혀 있지 않았다. D08(통계 worker)·I01(인입 파일)·R04(부하)가 참조할 숫자가 없었다. G04 실측의 통계 worker 한 라운드 127초는 cron 이 상속하는 statement_timeout 120초와 같은 크기다.
설치본과 웹·서버의 조합을 말해 주는 표가 없었다
iOS 1.0 (1)·Android 1.0.0 (1)·package.json 0.1.0 을 잇는 것이 없고, 두 셸 모두 www.barbelic.com 을 열어 웹 번들은 Production 이 서비스하는 것을 그대로 쓴다는 사실이 문서에 없었다. CI 에 native 빌드는 없다.
3. 해결 방안
원칙 (오너 결정 없음 — 전제로 진행, 2026-09-07)
- 전제 1: Production 빌드는 checked-in 기본값(= Production 공개 값)을 계속 허용한다. 막는 것은 "Production 이 아닌 대상이 Production 값을 받는 것" 과 "Production 대상이 staging 값을 받는 것" 이다.
- 전제 2: 배포 대상은 빌드가 선언한다 — GitHub Actions 는
BARBELIC_TARGET, Vercel git 통합 빌드는 시스템 변수(VERCEL_TARGET_ENV/VERCEL_ENV, 기본 노출이라 오너 설정 불필요). - 전제 3: 의존성 전면 최신화·Dependabot·새 빌드 체계 전환은 하지 않는다(이슈 명시).
- 전제 4: 자원 한도는 G04 와 같은 형식으로, 값마다 출처(실측/코드/문서 인용/목표/미측정)를 붙이고 재지 않은 처리량은 보장하지 않는다. G04 상한은 넓히지 않는다.
- 전제 5: native 빌드는 실제로 돌릴 수 있는 것(Android, 이 PC)만 돌리고, iOS 는 미실행이라고 그대로 적는다. Windows 의 웹 빌드나 문자열 검사로 대신하지 않는다.
접근
| 안 | 내용 | 채택 |
|---|---|---|
| A. 대상 명시 + 원천 한 곳 + 경계 검사 | 원천 파일 deployment-targets.json · 빌드 대상 선언과 대조(어긋나면 빌드 실패) · 확정 값을 index.html 도장으로 · 배포 직전 산출물 검사 · 복사본 12곳 대조 · 도구 버전 원천 1곳 · 자원 계약 표 + 상수 + 대조 테스트 | 채택 — 같은 종류의 문제(값 빠뜨림·복사본 어긋남·버전 드리프트)가 다른 파일에서 다시 나도 검사가 잡는다 |
B. Vercel 환경 변수에 VITE_* 를 전부 넣고 기본값 삭제 | 로컬 dev·Node 테스트·Production 빌드까지 변수 필수 | 불채택 — 오너 손 설정이 선행돼야 하고 빠지면 Production 도 못 나간다 |
C. 워크플로에 if [ -z ] 검사만 추가 | 비밀값 누락만 막는다 | 불채택 — 증상 처리. 대상 추측·복사본·버전 드리프트는 그대로 |
| D. 의존성 전면 최신화·Dependabot | — | 불채택 — 이슈가 금지 |
4. 적용한 내용
Phase 1 — 배포 대상 원천·빌드/시작 경계 명시 오류·산출물 검사 (4a191520)
deployment-targets.json: production/staging/preview/local 의 Supabase ref·URL·공개 키·Vercel 환경·브랜치와native(서버 origin·인증 origin·콜백 scheme·app id). 공개 값만.vite.deploymentTarget.mjs:BARBELIC_TARGET→VERCEL_TARGET_ENV→VERCEL_ENV→ (vite dev만)local순으로 대상을 정하고, 대상 없음 / staging·preview 에 값 없음 / 대상과 다른 프로젝트 값 /VITE_이름에 SERVICE_ROLE·SECRET·PASSWORD·TOKEN 이면 빌드 실패. 확정 값과 릴리스 SHA(VERCEL_GIT_COMMIT_SHA→GITHUB_SHA→dev)를index.html첫 줄__BARBELIC_DEPLOYMENT__·__BARBELIC_RELEASE__로 새긴다. 관리자 셸(vite.admin.config.mjs)은 대상 production 고정.app-config.js: 브라우저에서는 도장만 읽고 없으면 시작 거부. Node(스크립트·테스트)는 종전 기본값.scripts/check-build-artifact.mjs: 산출물의 도장 대상·다른 ref 불포함·잡 환경 비밀값 문자열 불포함·source map 없음 검사(값은 출력하지 않음).deploy.yml배포 직전과 CIcheck:artifacts에서 실행.deploy.yml: smoke 비밀 없음 → 실패(건너뜀 없음), DB 비밀번호 빈 값 → 명시 실패(다른 환경 값으로 대체 없음), Production smoke URL 을 고정 ref 에서 만들고PROD_SUPABASE_URL과 대조, functions 잡 ref 재확인,$GITHUB_ENV승격 제거.check-deployment-pipeline.mjs: 원천 값의 복사본 12곳 대조(app-config·config.toml·deploy.yml·xcconfig 2·Swift·gradle·Kotlin·capacitor·스크립트 2).- 문서
docs/platform/build-targets.md§1~§6: target 표(web/관리자/docs/API/Edge/iOS/Android/Node 별 진입점·명령·공개/비밀 값·읽는 코드·전달 경로·runtime·toolchain·버전 원천)·경계 검사 표·로컬 사용법·오너 손 목록.
Phase 2 — CI toolchain 원천·SHA 핀·비밀 경계·docs-build 잡 (a51ea6c0)
- Node:
.nvmrc(22) 를 워크플로 13곳node-version-file이 읽고npm-installaction 의 node_modules 캐시 키를hashFiles('.nvmrc')로 파생. Vercel CLI:package.jsonvercelToolchain.cli정확 버전(배포 경로@latest제거).supabase/setup-cli: 커밋 SHA + 태그 주석 3곳. prod-smoke.yml: 비밀값 없으면 이름을 적고 실패,E2E_EVIDENCE=off로 trace·video·screenshot 생성 안 함, 결과 JSON 만 7일 보관.user-data-copy.yml:run:본문의secrets.*를env:로.policy-contract.yml:docs-build잡(docs locknpm ci→ VitePress 빌드 → 관리자 셸 동봉 → 산출물 검사)을 docs 입력 변경 PR 과 main 푸시마다 실행,verify필수 조건에 포함. static-checks 에build:admin·check:artifacts.- 테스트
ciToolchain.test.mjs7건: 손으로 적은node-version:0곳·@latest금지·서드파티 action SHA 핀만 허용.
Phase 3 — 자원 예산 계약 (844c3078)
RELEASE_RESOURCE_CONTRACT45항목(appResourceContract.ts) +docs/gates/resource-budgets.md. 값마다 status: measured(Production 실측) / code(소스 상수) / documented(플랫폼 문서 인용) / target(목표, 현재 값 아님) / unmeasured(재지 않음, 보장 없음).- Production 읽기 전용 프로브(
scripts/resources/probe-production.mjs): 컴퓨트 Micro(2 vCPU 공유·1GB), 직접 연결 60(예약 3 제외 57)·풀러 200(transaction), 역할별statement_timeout= anon 3초 / authenticated 8초 / service_role·cron 120초, lock·idle-in-transaction timeout 무제한, cron 배치 = 통계 25/분·백필 50/시·PR 롤오버 10/분, 프로브 시점 앱 연결 4. - 문서 인용: Edge 함수 256MB·400초·CPU 2초·idle 150초, Vercel 함수 Hobby Fluid 300초·2GB·본문 4.5MB. 코드 상한: API 본문 4096B(
api/auth/_shared.jsBODY_LIMIT_BYTES, B01 뒤 한 곳), Motra 30MiB·InBody 20MiB·Wodup 1만 줄, Edge 배치 10/50/5. 목표: worker 한 라운드 60초 이하(D04·D08). 미측정: worker 동시성·Edge 요청 본문 한도·parse 메모리. - 대조 테스트
resourceContract.test.mjs5건: 코드 ↔ 증거 JSON ↔ 문서 표 ↔ 실제 소스 상수, 증거 파일에 비밀 없음.
Phase 4 — 호환 표·설치본 버전 대조·Android release 실측 (d43fcd3b·ce8a8391·b50a3855)
build-targets.md§7 호환 표: 웹 앱(릴리스 PR 머지)·스테이징(main 푸시)·관리자 셸(main 푸시 즉시, Production 고정,admin-hold게이트)·Edge 함수(응답 헤더 배포 버전)·서비스 워커(CACHE_VERSION)·iOS1.0 (1)·Android1.0.0 (1). 설치본은 웹 번들을 싣지 않고 origin 을 열므로 "설치본 버전 = 어느 bridge capability 를 주는가"(iOS 6종·Android 3종).check-deployment-pipeline.mjs: 설치본 버전(gradleversionName/versionCode·pbxprojMARKETING_VERSION/CURRENT_PROJECT_VERSION)·SW 캐시 버전·Edge 배포 버전 문자열이 표에 그대로 있는지 대조.- Android release 빌드 실측(§8-1):
BARBELIC_TARGET=production npm run build→cap sync android→gradlew.bat bundleRelease— BUILD SUCCESSFUL 1분 3초(122 tasks),app-release.aab6,289,676 bytes, SHA-2569ec568f7…daa4b, 번들 안 도장 production·kobxeylancdimqhfkbnl, 서버 비밀 문자열 0건, 서명 없음(keystore 없음). toolchain: Windows 11·JDK 21.0.12·SDK 36·Gradle 8.14.3·AGP 8.13.0·Kotlin 2.2.0·Capacitor core/android 8.5.0. - iOS(§8-2): Windows 라 미실행 — R05 가 오너 Mac 에서 돌릴 명령·확인 항목·기록할 값. N01 인계(§8-3): capability 목록·
CACHE_VERSION·native원천.
Phase 5 — 기록·랜딩 (PR #1356)
- 이 기록·등록 2곳(사이드바·README)·게이트/플랫폼 표 행 2개,
npm run ci:local, PR,npm run landing:request, staging 실행 확인, HQ 갱신안(#1279 댓글)·인계 댓글.
주요 결정과 그 근거
- 부재를 Production 으로 해석하지 않는다. 대상은 명시 입력이고 값은 대상과 대조된다. 실패는 빌드 시점에 사람이 읽는 문장으로 난다.
- 도장(
__BARBELIC_DEPLOYMENT__)이 효력을 갖는다. staging 번들에도app-config.js의 Node 전용 기본값(Production 공개 값) 문자열은 남지만, 브라우저는 도장만 읽는다. 산출물 검사도 도장으로 판정한다. - 비밀값 검사는 이름만 본다. 값은 출력하지도 비교에 남기지도 않는다(산출물 검사·프로브 요약·증거 JSON 전부).
- 관리자 셸은 대상 production 고정. main 푸시마다 Production DB 를 보는 것이 정의(운영 정본 §4-5)이므로 docs 프로젝트 프리뷰에서도 바뀌지 않는다.
- 자원 한도는 status 를 붙여 적는다. "worker 한 라운드 60초 이하" 는 target 이지 현재 값이 아니고, 재지 않은 동시 처리량은 unmeasured 로 남긴다.
작업 중 드러난 것
test/e1rmPolicy.test.mjs(매니페스트 등재)가 CI 워크플로의- run:목록을 고정하고 있어build:admin·check:artifacts·docs-build추가분을pending-changes.json에 신고했다(매니페스트는 손대지 않음).- 새 문서가 docs 빌드를 깨뜨렸다(Vue 템플릿이 이중 중괄호를 변수로 해석) — 종전에는 Vercel 배포 시점에만 드러났을 오류이고, 새
docs-build잡이 잡는 대상이다. setup-cli@v1은 태그가 아니라 움직이는 브랜치였다(핀 시점 v1.7.1).landing-page/의 자체 lock 은 소유 경계 밖이라 문서에만 적었다. Vercel 프로젝트의 Node 버전은 대시보드 설정이라.nvmrc와 자동 연동되지 않는다(오너 손 목록).- 풀러
default_pool_size·max_client_conn은 API 가 null(등급 기본값)로 주어 숫자를 쓰지 않았다. Vercel Fluid compute 여부·요금제는 대시보드에서만 보여 "문서 인용, 대시보드 미확인" 으로 표기했다. - Git Bash 에서
gradlew.bat를 상대 이름으로 부르면 cmd 가 찾지 못한다(이전 세션 실패 2회의 원인) — PowerShell 이나 전체 경로로 호출한다.cap sync가 빌드 산출물을android/app/src/main/assets/public/(git 제외)에 복사하는데check-utf8이 그 파일(U+FFFD)을 잡아npm run check가 실패한다 — 산출물 폴더를 지우면 통과, CI 에는 없는 파일. - B01(#1346) 합류로 요청 본문 상한이
api/auth/_shared.jsBODY_LIMIT_BYTES한 곳이 되고 test-admin 의 별도 2048 상한이 사라져, Phase 3 대조 테스트·계약·문서를 새 원천으로 고쳤다. - main 의
coverage-inventory.json이 정렬된 형식으로 다시 저장돼 리베이스에서 충돌 — main 판을 받고 규칙 5줄을 스크립트로 재적용,--render2회·--strict통과. Bash 히어독은 역슬래시를 망가뜨려(\\.→.) 스크립트는 파일로 써서 실행했다. - Kotlin 경고 10건(
NativeBridge.kt163·164·165·182·200·201·213·242·243·244,Nothing?를String자리에) — 빌드 통과, N01 소유. - 이전 세션
d24267a7은 Phase 4 도중 사용량 한도로 끊겨 인수인계 댓글 없이 종료됐다. 미커밋 2파일을 그대로 이어받아 완주했다. - 사전 검증 누락(Phase 1 의 내 결함,
ci:local이 잡음): Playwright 웹서버가npm run build로 앱을 띄우는데 대상을 선언하지 않아, 새 규칙("대상 없으면 빌드 실패")대로면 CIbrowser-journeys·viewport-matrix와ci:local의 브라우저 단계가 전부 멈췄을 것이다. Phase 1 은 브라우저 e2e 를 돌리지 않고npm run check만 통과시켰다. 두 설정(playwright.config.mjs·playwright.viewport.config.mjs)의 webServer 에env: { BARBELIC_TARGET: … || "local" }을 넣고(Playwright 는process.env위에 덮어씀)deploymentTargets.test.mjs가 두 파일을 고정한다(24ccc66d). 다른 세션들이 4173 포트로 브라우저 e2e 를 돌리고 있어ci:local --full이 browser 단계에서 exit 3(포트 점유)으로 두 번 멈췄고, 포트가 풀리는 순간--only browser,viewport로 이어 돌렸다 — 두 줄을 §5 에 그대로 적는다. - CI 단위 shard 2/2 만 실패(Linux·Node 22.23), 로컬(Node 24·Node 22.19)은 통과: 원인은
app-config.js가 CommonJS 범위(루트 package.json 에type없음) 안의 ESM 문법 파일이라node --import tsx+ Node 22 에서 CommonJS 로 변환되고 named export 를 정적으로 연결하지 못해(does not provide an export named BARBELIC_CONFIG) 새appConfigEnv.test.mjs가 로드 단계에서 죽은 것. 파일 단위 실패라 첫 실행 로그에는fail 0으로 보였고 재실행에서not ok - tests/react/appConfigEnv.test.mjs로 드러났다. CI 와 같은 파일 분할(Linux 정렬)을 로컬에서 흉내 내고 npx 로 Node 22.23.2 를 받아 재현·수리(테스트가createRequire로 읽음, Node 22.23.2·24 모두 7/7). 앱 번들(Vite)·기존 테스트(동적 import)는 영향 없음. 구조적 원인(루트.js의 모듈 형식 모호성)은 이 트랙 범위 밖 — HQ 갱신안에 남긴다. - 랜딩 전 실측: 현재 staging 배포 번들(
barbelic-fkjwoysqy…vercel.app)에 staging ref 가 들어 있어 Vercel staging 환경의VITE_SUPABASE_*가 설정돼 있음을 확인 — 새 대상 검사가 staging 레인을 빨간불로 만들지 않는다는 근거.
5. 적용 결과
| 항목 | 결과 |
|---|---|
| 변수 없는 staging 빌드 | 성공·Production 지향 → 빌드 실패("staging 빌드에 VITE_SUPABASE_URL 이 없습니다…"). 음성 시나리오 6건 실제 vite build: 대상 없음 / staging+값 없음 / production+staging 값 / VITE_ 서비스 키 / Vercel staging 시스템 변수+값 없음 / 모르는 대상 → 전부 명시 오류 |
| 산출물 검사 | 양성 3건(local·staging·관리자 셸 production 고정) 통과, 잘못된 기대·비밀값 유입 시 실패 확인 |
| smoke 비밀 없음 | notice + success → failure. DB 비밀번호 빈 값 → 명시 실패(다른 환경 대체 없음) |
| 환경 값 복사본 대조 | 0 → 12곳(check:deployment) + 설치본·SW·Edge 버전 문자열 |
| 릴리스 식별 | 청크 파일 이름 → 커밋 SHA(__BARBELIC_RELEASE__; Actions/Vercel 빌드에서만, 로컬은 dev) |
| Node 손 기입 | 워크플로 13곳 + 캐시 키 → 0곳(.nvmrc) · Vercel CLI @latest → 정확 버전 · setup-cli 태그 → SHA 3곳 |
| 관리자 셸·docs 빌드 CI 검증 | 0회 → PR·main 푸시마다(docs-build 잡, 로컬 실측 docs npm ci 7초·빌드 1분 1초) |
| Production 세션 토큰 artifact | trace/video 30일 → 기록 안 함(결과 JSON 7일) |
| 자원 한도 | 선언 0 → 45항목(실측·코드 상수·문서 인용·목표·미측정 구분), 대조 테스트 5건 |
| Android release 빌드 | 증거 없음 → BUILD SUCCESSFUL 1분 3초, app-release.aab 6,289,676 bytes, SHA-256 9ec568f7…, 도장 production, 비밀 0건, 서명 없음 |
| iOS release 빌드 | 미실행(Windows) — R05 인계 항목·명령 명시 |
| 로컬 게이트 | npm run check 통과(단위 2,990). 검증: ci:local full(--full) · scope full(바뀐 파일 41개) · verify 통과 · 샌드박스(Postgres 17.6.1.127 = Production 핀) 마이그레이션 전체 적용·schema 스냅샷 대조·pgTAP 109파일 1,900 assert 통과(11:22Z) · e2e local 11/11 · empty 7/7 · cardio 6/6 · persistence 1/1 통과 · browser 단계는 다른 세션의 4173 포트 점유로 exit 3(2회) → 같은 샌드박스·CI=true·미리보기 4179 포트에서 test:e2e-browser 38/38(CASE-017 은 미리보기 서버에 샌드박스 Supabase env 를 넘기지 않은 첫 실행에서 410 으로 실패 → env 를 준 뒤 재실행 통과) · test:e2e-viewport 14/14 (11:50Z) |
| staging 배포(랜딩 뒤) | landing:request → landing-queue 워크플로가 머지 커밋의 deploy.yml staging 실행(database → functions → frontend[새 산출물 검사] → smoke)을 끝까지 확인해야 성공으로 끝난다 — 결과 run·merge SHA 는 이슈 #1332 Phase 5 댓글에 기록 |
| Production | 미검증 — v0.18.0 릴리스에서 deploy.yml production 레인이 같은 검사를 처음 실행한다 |
6. 이번 개선으로 향상된 것
스테이징에서 테스트한 기록이 Production 에 들어가는 일이 구조적으로 막힌다
운영자 A 가 변수를 빠뜨리면 배포가 아니라 빌드가 멈추고, 통과한 번들은 첫 줄 도장으로 자기 대상을 말한다. 배포 워크플로는 배포 직전 그 도장을 다시 읽는다.
"값이 없으면 건너뜀" 이 사라졌다
비밀값·ref·DB 비밀번호가 없으면 잡 이름과 빠진 이름을 적고 실패한다. 다른 환경 값으로 넘어가지 않는다.
환경 값·도구 버전의 원천이 각각 한 곳이다
deployment-targets.json·.nvmrc·package.json toolchain. 복사본이 어긋나면 check:deployment·ciToolchain 테스트가 PR 에서 잡는다.
자원 한도가 D08·I01·R04 가 읽을 수 있는 숫자가 됐다
현재 값과 목표·미측정을 구분한 표와 상수. 통계 worker 라운드 127초 = timeout 120초 경계에 있다는 사실이 표에 있다.
설치본×웹×서버 조합이 표로 있고 Android 빌드 증거가 있다
N01·R05·R02 가 같은 행 목록으로 호환 실험·리허설을 한다.
구조적으로 남는 것
배포 대상 원천 파일과 대조 검사, 빌드 대상 선언 규칙과 도장, 산출물 검사, toolchain 원천, 자원 계약 표와 대조 테스트, 호환 표와 설치본 버전 대조, target 표 문서.
남은 것
- iOS release 빌드 증거(R05, 오너 Mac) · Android 서명·Play App Signing 검증(R05, 오너 keystore).
- 미측정 자원(worker 동시성·Edge 본문 한도·parse 메모리)은 D08·I01·R04 가 잰다. 통계 worker 라운드 60초 이하는 D04·D08 목표.
- HQ 갱신안: 총괄 B02 행·랜딩 표 행, 릴리스 SHA 주입은 이 PR 로 해결됐음을 G04 갱신안 항목에 반영.
- 오너 손(진행 무관): Vercel 두 프로젝트 Node 버전
22.x확인, "System Environment Variables" 켜짐 확인, Production 환경VITE_SUPABASE_*명시(선택). - 릴리스 v0.18.0 머지 뒤
[v0.18.0 반영완료]+ 닫기, Production 레인의 산출물 검사 결과 기록.