www 루트 주소가 app으로 넘어가지 않던 문제에서 옛 주소 이동 검사 두 층까지 (2026-09-15)
- 기간: 2026-09-15 10:15 ~ 02:05 KST (세션 1개
12f86128, 오너 지시 "#1609 해결좀" → "일단 관리자 권한으로 빠르게 staging, main 머지해" → "하고 제품 배포까지 쭉") - 랜딩: PR #1611(Phase 1
04e68073· Phase 2939aa5ea· QA1 핀93cb5f6d→ staging3f434ac0) · PR #1613(main #1604 → staging 동기화, QA1 핀 충돌 해결91e0db30→ stagingfb56055a) · PR #1614[Release v0.19.1]→ maine94335aa— 마이그레이션·엣지 없음, Vercel 프런트 배포 성공(1-Production Deploy 34871745442, 태그v0.19.1=e94335aa) - 설계서: 없음(수리 건) — 분석·Phase 계획·예상 효과는 #1609 댓글
- 정본: 앱
vercel.json옛 주소 규칙(/:path(.*)→<publicOrigin>/:path) ·scripts/check-deployment-pipeline.mjsLEGACY_REDIRECT_SOURCE·deployment-targets.json production.legacyOrigins - 도구: 원인 재현에
@vercel/routing-utils6.5.0sourceToRegex/getTransformedRoutes(세션 scratchpad, 레포 밖) - 게이트:
npm run check:deployment(형태) · QA1suite/tests/react/barbelicWebMetadata.test.mjs(형태) ·deploy-steps.yml프런트 잡 "Legacy origins must redirect to the public origin"(실제 응답, Production만) · 로컬 precheck static·단위 통과(작업93cb5f6d2107+1777, 동기화91e0db302107+1778, 실패 0). DB·pgTAP·e2e 미접촉 - 버그리포트:
bug-report/bug-146-20260915.md— 이 건의 정본. 아래는 그 리포트를 잇는 짧은 기록 - 계약: 없음(기존 #1474 옛 주소 계약의 규칙 형태를 고정)
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | vercel.json 규칙 교체 + check:deployment가 루트 포함 형태만 통과 | ✅ PR #1611 (04e68073) |
| Phase 2 | 배포 프런트 잡에 옛 주소 루트·/home 308 실제 확인 단계 | ✅ PR #1611 (939aa5ea) |
| 병합·배포 | 관리자 병합 staging(3f434ac0·fb56055a) → main(e94335aa) → Production | ✅ 1-Production Deploy 34871745442 성공, 태그 v0.19.1 |
1. 배경
#1474로 v0.19.0(2026-09-15 01:05 KST)부터 앱은 app.barbelic.com, 옛 주소 www.barbelic.com은 경로를 유지한 채 308로 보내기로 했다(오너 결정 "한 번에 옮김"). 배포 직후 승격 세션의 실측에서 루트 www.barbelic.com/만 앱을 그대로 내주는 것이 드러나 #1609로 등록됐다.
2. 문제 제기
/:path* 규칙이 빈 경로 /에 맞지 않는다
Vercel이 규칙을 정규식으로 바꿀 때 /:path*는 경로 조각을 하나 이상 요구하는 형태가 된다. 라이브러리로 재현: / 불일치, /home 일치. 상세는 BUG-146 §2.
검사가 결함 있는 형태를 정답으로 요구했다
정적 검사와 QA1 테스트가 source === "/:path*"를 단언했고, 배포 뒤 검사는 정규 주소만 봤다. staging에는 옛 주소가 없어 Production 배포가 첫 실행이었다.
3. 해결 방안
원칙 (오너 결정, 2026-09-15)
- D1 "일단 관리자 권한으로 빠르게 staging, main 머지해" · "하고 제품 배포까지 쭉" — release 큐·GitHub Full CI 완주 없이 관리자 병합으로 Production까지(v0.19.0 #1606·#1608과 같은 방식). 채택.
접근
루트 전용 규칙 추가(기각: 규칙 분산, 함정 잔존) / /:path(.*)로 교체 + 검사 두 층(채택) / 검사만 추가(기각: 결함 잔존). 비교표는 BUG-146 §3.
4. 적용한 내용
Phase 1 — 규칙 교체와 정적 검사 (#1611 04e68073)
vercel.json 옛 주소 규칙 source: "/:path(.*)", destination: "https://app.barbelic.com/:path". check-deployment-pipeline.mjs는 옛 주소마다 이 형태만 통과시키고 /:path*는 "루트가 이동되지 않는다" 메시지로 실패(이유 주석).
Phase 2 — 배포 뒤 실제 이동 확인 (#1611 939aa5ea)
deploy-steps.yml 프런트 잡 "The public origin must serve this deployment" 뒤에 "Legacy origins must redirect to the public origin": legacyOrigins마다 /·/home이 publicOrigin으로 308 이동하는지 curl 확인, 없는 환경은 건너뜀.
QA1 핀
QA1 bcc7e87b(테스트가 새 형태 요구) → main #1604 핀과 합친 af159b3f로 고정(PR #1613).
주요 결정과 그 근거
- 규칙을 교체하고 검사가 형태를 고정: 다음에 옛 주소를 추가하는 사람이 같은 함정에 빠지지 않게(원칙 4 구조적 원인 해결).
- 배포 검사를 정규 주소 검사 뒤에 배치: #1607의 반영 지연 오판을 키우지 않기 위해.
- QA1 main에 합치지 않음: 앱 핀이 다른 세션 브랜치 위라 QA1 main이 조상이 아니어서, 합치면 남의 브랜치 커밋이 끌려 들어간다.
작업 중 드러난 것
- 첫 precheck가 QA1 테스트 1건으로 빨간불 — 테스트가 결함 있는 형태를 단언하고 있었다(사전 검증 누락 아님, 테스트 자체의 가정 오류).
- main이 작업 중 #1604(
5d3d6ac1,qa1.lock.json변경)로 앞서가 릴리스 PR이 충돌 → main을 staging에 동기화하는 PR(#1613)로 해결. 관리자 병합 경로에서는 큐가 이 충돌을 대신 잡아 주지 않는다. - PR 생성으로 자동 실행된 GitHub Full CI는
scope잡("Promotion PR must be open and ready for review")에서 종료 — head가 release/*가 아닌 승격 PR을 정책 검사가 거부. 검증은 돌지 않았다. check:deployment를 단독으로 돌리면 QA1이 가져오는 probe 파일이 없어 실패(#1591 이후) —npm run check/ci:precheck-local처럼 QA1 준비가 앞서는 진입점을 쓴다.release/v0.19.1을 main(v0.19.0)에서 만들었지만 큐를 거치지 않아 비어 있다(=1755f24a, ruleset이 삭제 금지). 다음 패치는 v0.19.2로 main에서 새로.
5. 적용 결과
| 항목 | 결과 |
|---|---|
https://www.barbelic.com/ | 200(앱) → 308 https://app.barbelic.com/(배포 뒤 실측, 쿼리 보존) |
https://www.barbelic.com/home | 308 app.barbelic.com/home → 동일 |
| 옛 주소 규칙 형태 검사 | 없음(결함 형태를 요구) → check:deployment·QA1 테스트가 /:path(.*)만 통과 |
| 배포 뒤 옛 주소 이동 확인 | 없음 → Production 프런트 잡 단계(run 34871745442 프런트 잡에서 첫 실행에 통과) |
staging 배포 34871126235 (3f434ac0) | database·functions·frontend·smoke 성공 |
Production 배포 34871745442 (e94335aa) | database·functions·frontend·smoke·release tag 성공(2026-09-15 02:01 KST). browser journeys는 조건부 skip(v0.19.0과 동일) |
| 미검증 | www.barbelic.com/index.html의 2단 이동(cleanUrls → / → app) 실측 안 함 |
6. 이번 개선으로 향상된 것
옛 주소 루트 진입이 정규 주소로 간다
북마크·설치 아이콘·검색 결과로 www.barbelic.com/에 들어온 사용자가 app.barbelic.com/에서 앱을 쓴다 — #1474의 "옛 출처에 기록이 남지 않게" 전제가 루트 진입에서도 성립.
구조적으로 남는 것
옛 주소 → 정규 주소 계약을 형태(정적 검사·QA1)와 실제 응답(Production 배포 프런트 잡) 두 층이 지킨다. 옛 주소 추가는 legacyOrigins 한 줄로 세 검사가 자동으로 따라온다.
7. 후속 — www는 랜딩의 정규 주소 (2026-09-15, #1619)
오너 결정(같은 날 02:2x KST): www.barbelic.com = 랜딩 정규 주소(Vercel barbelic-landing), barbelic.com → www 308, 앱 = app.barbelic.com(www.app.barbelic.com 불필요). 이 결정으로 §5의 "www → app 308" 행은 더 이상 기대 동작이 아니다.
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 앱 legacyOrigins: [](deployment-targets.json·app-config.js), vercel.json www 규칙 제거, /landing → https://www.barbelic.com/ 직행, QA1 deploymentTargets.test.mjs 완화(ca0bfa4b) | ✅ PR #1620 → staging 4a899466 → #1612의 PR #1622로 main 157bf12b · 1-Production Deploy 34875556141 attempt 2 성공(frontend "no legacy origins" 건너뜀·smoke), 태그 v0.19.2. attempt 1 frontend 실패는 #1607 반영 지연 오판 → 실패 잡 재실행 |
| Phase 2 | Supabase Production Auth Site URL → https://app.barbelic.com(관리 API) | ✅ 재조회 확인 |
| Phase 3 | 랜딩 canonical → https://www.barbelic.com/ | ✅ 랜딩 PR #4 8af96022, 배포 뒤 실측 |
| Phase 4 | 이 문서·BUG-146 갱신 | ✅ 이 절 |
작업 중 드러난 것: 결정 직후 #1612의 Production 배포 34874848468이 §4의 배포 검사에서 실패(www 200) — 검사는 설계대로 동작했고 원인은 결정 변경. 도메인 정본 docs/platform/domains.md(PR #84, 미병합)의 www 역할 서술은 이 결정으로 구식이며 그 PR 담당이 고친다.
남은 것
docs/platform/domains.md(#1474 문서 PR #84, 미병합)에 규칙 형태(/:path(.*)) 설명 반영 — 그 PR 담당.- #1607 배포 뒤 공개 주소 검사 반영 지연 — 별도 이슈.
release/v0.19.1빈 브랜치 정리(ruleset 삭제 금지) — 오너 손 또는 다음 릴리스 때 판단.