본문으로 건너뛰기

배포 & 릴리스

너홀로프로는 main 브랜치 = production 입니다. merge 후 수동 rollout 으로 배포합니다 — App Hosting rolloutPolicy.disabled=true 라 main push 만으로는 자동 배포되지 않으며, 배포자가 Firebase Console / CLI 로 rollout 을 트리거해야 한다 (2026-06-07 정정: 과거 "자동 배포" 표기는 실제 backend 설정과 불일치였다). 별도의 자동 태깅·GitHub Release 는 사용하지 않습니다 (2026-05-01 폐기) — 배포 단위는 main 의 commit SHA, 롤백은 App Hosting 의 SHA 단위 rollback.

main 직접 push 금지

feature 브랜치에서 PR 을 거쳐야만 main 에 반영됩니다. 긴급 hotfix 도 PR + 리뷰 경로를 따릅니다.

1. 배포 토폴로지

3개 앱이 각각 다른 Firebase 서비스로 배포됩니다.

방식백엔드 ID / 호스팅 사이트URL
apps/webFirebase App Hosting (Next.js, Cloud Run)neohollo-pro (asia-east1)https://pro.neohollo.com
apps/opsFirebase App Hosting (Next.js, Cloud Run)neohollo-pro-admin (asia-east1)https://admin-pro.neohollo.com
apps/docsFirebase Hosting (정적 빌드)docs-pro-neohollohttps://docs-pro.neohollo.com
functions/Cloud Functions v2codebase default

Firestore + Storage 는 단일 공유. Firestore 위치 asia-northeast3. (2026-06-11 정정 — ops 백엔드 ID 는 neohollo-admin 이 아니라 neohollo-pro-admin, App Hosting 두 백엔드 모두 asia-east1. App Hosting Admin API 실측.)

2. 릴리스·버전 정책

별도 자동 태깅·릴리스 없음. package.jsonversion 필드는 더 이상 자동 갱신되지 않으며, 배포 단위·롤백 기준은 main 의 commit SHA 입니다.

  • 배포 단위: main 의 각 commit SHA (Firebase App Hosting rollout 1:1 매핑)
  • 변경 이력: git log + GitHub PR 목록
  • 롤백: §4 "롤백 (App Hosting)" — Cloud Run revision 단위, main revert 없이도 가능
  • 수동 GitHub Release 가 필요한 경우 (예: 외부 공지용 milestone): gh release create vX.Y.Z 로 직접 발행

3. 품질 게이트

GitHub Actions 자동 trigger 는 사용하지 않습니다 (2026-05-18 — 전 워크플로 workflow_dispatch 단독. 솔로 dev 환경에서 Actions billing 차단 시 가짜 FAILURE 로 보호막이 0 이 되는 사고 방지). 품질 게이트 SSoT 는 로컬 pre-push hook:

pnpm test && pnpm lint # pre-push hook 이 자동 실행
pnpm --filter=@neohollo/web build # next build 회귀 (ADR 0030 T1) — 큰 변경 시 수동

E2E (Playwright) 는 게이트에 포함되지 않음 — 로컬 에뮬레이터 의존. UI 대규모 변경 시 로컬 pnpm e2e 통과 후 PR. nightly 회귀는 local launchd, production smoke 는 배포 직후 manual gh workflow run.

4. App Hosting 배포 (apps/web · apps/ops)

Firebase App Hosting 은 자동 rollout 이 비활성(rolloutPolicy.disabled=true)이라, main merge 후 수동으로 rollout 을 트리거해야 배포됩니다 (Firebase Console > App Hosting > backend 에서 새 rollout 생성, 또는 CLI). 별도 GitHub Actions deploy 단계 없음 (App Hosting backend 자체가 GitHub 통합).

  • apps/web/apphosting.yamlbuildCommand: pnpm run build, minInstances: 0, Firebase Admin 시크릿 3종 + Resend 주입
  • apps/ops/apphosting.yamlbuildCommand: pnpm --filter=@neohollo/ops... build, 서버 시크릿 없음 (client SDK 만)

배포 진행·현황 확인

가장 빠른 경로는 ops 콘솔 /health 의 "App Hosting 배포 현황" 패널 (#2751) — 백엔드별 서빙 build 의 커밋 sha (GitHub 링크) · 커밋/빌드 시각 (KST) 과 "최신 build 미서빙" 드리프트 (P1) 를 한 화면에 표시한다.

CLI:

firebase apphosting:rollouts:list --backend=neohollo-pro
firebase apphosting:rollouts:list --backend=neohollo-pro-admin

Firebase Console → App Hosting → 백엔드 선택 → 최근 rollout 상태(BUILDING · DEPLOYING · SUCCEEDED · FAILED) 확인.

롤백 (App Hosting)

  1. Firebase Console → App Hosting → 해당 backend
  2. Rollouts 탭에서 이전 성공 rollout 선택
  3. "Rollback" — Cloud Run 트래픽을 이전 리비전으로 100% 이동
  4. 완료 후 root cause 분석 → 수정 PR → 새 rollout 으로 전진 복구
main revert 없이 롤백 가능

App Hosting 롤백은 Cloud Run 레벨에서 트래픽만 전환하므로 main 브랜치의 상태와 분리됩니다. 즉 git revert 없이도 즉시 복구 가능. 급한 장애는 먼저 롤백 → 그 다음 원인 분석·정식 revert PR 순.

5. Cloud Functions 배포 (수동)

Cloud Functions 는 자동 배포 대상이 아닙니다. App Hosting 배포와 별도로 수동 실행.

# functions/ 의존성 설치 (별도 npm workspace)
cd functions
npm install

# 빌드·린트 (firebase.json predeploy 가 재실행)
npm run lint
npm run build

# 배포
cd ..
firebase deploy --only functions

혹은 특정 function 만:

firebase deploy --only functions:onCaseClosedLegacyPipeline

현재 배포된 Functions

파일역할
cleanup.ts주기적 cleanup (Scheduled)
email.tsResend 발송 헬퍼
hearings-stats.ts기일 통계 집계
legacy-pipeline.ts사건 종결 시 OCR + DLP + Vertex AI Search 인덱싱
legacy-retry.ts파이프라인 재시도
plan-downgrade.ts트라이얼 만료·grace 경과 시 자동 다운그레이드 (Scheduled)
plan-trial-reminders.ts트라이얼 만료 리마인더 (Scheduled)
reminders.ts매일 09:00 KST 기일 알림 (Scheduled)
stats.tsaggregateCaseStats (사건 통계 갱신)

Cloud Functions 긴급 비활성화

특정 function 이 사고를 일으키면:

# 함수 자체 삭제
firebase functions:delete <functionName>

# 또는 소스에서 export 제거 → 재배포 (선택적)
# functions/src/index.ts 에서 해당 export 를 주석 처리

Scheduled function 은 Google Cloud Console → Cloud Scheduler 에서 일시 정지 도 가능 (삭제 대신).

6. Firestore Rules · 인덱스 배포

firebase deploy --only firestore:rules
firebase deploy --only firestore:indexes
firebase deploy --only storage # storage.rules

Rules 배포는 즉시 반영 — 잘못된 rule 이 올라가면 바로 접근 차단. 반드시 로컬 에뮬레이터에서 검증 후 배포.

firebase emulators:start --only firestore
# → http://localhost:4000 (에뮬레이터 UI) 에서 Rules Playground 로 쿼리 검증

7. docs 사이트 배포 (apps/docs)

pnpm build --filter=@neohollo/docs # apps/docs/build/ 정적 출력
firebase deploy --only hosting:docs-pro-neohollo

Firebase Hosting 은 main push 시 자동 배포되지 않음 (App Hosting 과 다름). 문서 수정 PR 이 main 에 merge 된 후 수동 배포 가 현재 절차.

향후 자동화

docs 는 정적이라 GitHub Actions 에서 firebase deploy --only hosting:docs-pro-neohollo 를 main push trigger 로 추가하는 것이 자연스러운 개선. 아직 미구현.

8. 환경 분리 전략 (현재 상태)

현재는 단일 Firebase 프로젝트에서 프로덕션을 운영합니다. 스테이징 분리는 미구현.

로컬 개발은 Firebase 에뮬레이터로 대체:

에뮬레이터포트
Auth9099
Firestore8080
Functions5001
Storage9199
Hosting (docs)5000
App Hosting (ops)5003
Emulator UI4000 (기본)
firebase emulators:start

Playwright E2E 는 localhost:5002 를 가리킵니다 (테스트 가이드 §3).

9. 긴급 대응 절차

증상1차 대응2차 대응
프로덕션 web 500 에러App Hosting 이전 rollout 으로 롤백git revert + PR 로 정식 복구
Cloud Function 오동작firebase functions:delete 또는 Scheduler 일시정지수정 후 firebase deploy --only functions:<name>
Firestore Rules 실수 배포이전 rules 파일로 firebase deploy --only firestore:rulesrules E2E 테스트 추가
데이터 오염즉시 해당 Function 비활성화Firestore backup 에서 복구 (GCP Console)
비정상 Gemini 요금config/appMetadata.features.ai.* 플래그 OFF (Firestore 직접 편집)kill switch PR 병합
인프라 킬 스위치

config/appMetadata.features.infraHardening.* 플래그는 Firestore 문서 1개 편집으로 원격 롤백이 가능하도록 설계되어 있습니다. 배포 없이 즉시 적용. 새 보안 기능은 이 네임스페이스에 플래그로 감싸세요 (CLAUDE.md 참조).

10. 배포 전 체크리스트

  • Conventional Commits 프리픽스 적절
  • 로컬 게이트 통과 (pre-push hook: test · lint / 큰 변경은 web build 까지)
  • AI / 플랜 / Firestore 스키마 수정 시 metadata 4곳 + Firestore 동기화 (CLAUDE.md §"앱 메타데이터" 참조)
  • 신규 Cloud Function 은 수동 firebase deploy --only functions 별도 수행
  • CSP 도메인 추가 시 csp-headers.ts + 해당 테스트 업데이트
  • Firestore Rules 수정 시 에뮬레이터 검증
  • docs 수정 포함 시 로컬 pnpm build --filter=@neohollo/docs broken link 0건

11. 배포 후 체크리스트 (2026-06-11 사고 후속)

merge ≠ deploy 환경의 함정 두 가지를 rollout 직후 반드시 확인한다 (회고 참조):

  • 서빙 커밋 확인 — ops /health "App Hosting 배포 현황" 조회: 서빙 build 의 커밋 sha 가 의도한 커밋인지. rollout 직후 머지된 커밋은 포함되지 않는다 (2026-06-11 같은 날 2회 재발한 패턴 — rollout 트리거 시점의 main 이 빌드 대상). 직후 머지가 있으면 재롤아웃 여부 판단
  • 열린 탭 skew 인지 — 배포 전에 열려 있던 모든 탭의 Server Action 은 404 (failed-to-find-server-action). 사용자에게는 #2749 가 한국어 "새 버전이 배포되어…" 안내 + 새로고침 버튼을 띄우지만, 본인 작업 탭도 새로고침 필요. 근본 완화는 NEXT_SERVER_ACTIONS_ENCRYPTION_KEY 고정 (후속 작업 — 키 고정 시 변경 없는 액션의 ID 가 빌드 간 유지)
  • 직전 작업이 특정 기능 fix 였다면 production 에서 해당 경로 1회 실측 (예: 초대 fix → 재발송 1회로 stuck 초대 복구)
  • 큰 기능 배포 후 5-10분 내 Sentry · Firebase Console 오류 모니터링