본문으로 건너뛰기

Stress 테스트 인프라

기존 e2e (테스트 가이드) 가 사건 15건 cold-start 만 검증하는 한계를 보완. 사무소가 15년 운영하면 누적되는 회귀 (페이지네이션·N+1·인덱스 누락·findNearest p95·격리 누설·UI race) 를 nightly 로 사전 차단.

2026-05-06 PR #1720~#1733 (14 PR · 20 spec) 으로 구축. 본 문서는 신규 stress spec 추가 가이드.

두 차원

차원목적시드spec 수회귀 영역
수직단일 사무소 5년치 누적cases 2k + legacy 1k5페이지네이션·N+1·인덱스·UI 인터랙션
수평50 사무소 동시 운영50 tenant × 50 cases × 5 legacy15cross-tenant 격리·ops 콘솔·운영자 mutation

실행

로컬

# 1. emulator 띄우기 (stress 환경 — functions 제외)
# stress 시드 (5000 cases batch) 가 functions trigger 폭주 + 4GB heap 한계
# OOM 을 일으킴. 로컬 Mac 에서 stress 작업 시 functions 비활성 emulator 권장.
pnpm emulator:stress

# 일반 개발 (functions 포함):
# pnpm emulator

# 2. dev 서버 (web + ops 양쪽 필요)
pnpm dev --filter=@neohollo/web # 3000
pnpm dev --filter=@neohollo/ops # 3001

# 3. stress 실행 (시드는 globalSetup 이 자동)
pnpm e2e:stress # 단일 (cases 2k + legacy 1k)
pnpm e2e:multitenant # 수평 (100 tenant × 50 cases × 5 legacy)

로컬 OOM 사고 (2026-05-06): 100 tenant × 50 cases = 5000 cases 일괄 시드가 aggregateCaseStats Cloud Function 5000 동시 trigger → Mac heap 4GB 초과 OOM. nightly (ubuntu 16GB+) 는 영향 없음. 해결: pnpm emulator:stress (functions 제외). production code 의 functions 정합은 unit test (functions/__tests__/)

  • nightly workflow 가 cover.

nightly

.github/workflows/nightly-stress.yml 가 매일 03:00 KST 자동 실행 + 수동 trigger 가능. PR 차단 아님 — 회귀 발견 시 fix PR.

gh workflow run "Nightly Stress" # 수동 trigger
gh run list --workflow="Nightly Stress" --limit 5 # 결과 확인

디렉토리 구조

scripts/
seed-stress-tenant.ts # 단일 stress (2k cases + 1k legacy)
seed-multi-tenant-stress.ts # 수평 stress (50 tenant × 50 cases × 5 legacy)
e2e/
stress/ # 단일 stress spec (5)
stress-setup.ts # globalSetup (clear + 시드)
multitenant/ # 수평 stress spec (15)
multitenant-setup.ts
helpers/
stress-auth.ts # 단일 stress owner login
multitenant-auth.ts # 50 tenant owner login
ops-auth.ts # masterAdmin cookie + clientSignInOps
playwright.stress.config.ts
playwright.multitenant.config.ts

새 spec 작성 가이드

1. 시드 chain 누락 패턴

owner 시드만 하면 spec 이 silent timeout — emulator 한계. 시드는 다음 4개를 모두 chain 해야 spec 동작:

누락증상해결
users/{uid}.consents재동의 모달이 헤딩을 덮어 timeoutseedUserConsents() (현행 CURRENT_*_VERSION "2026-04-27")
setCustomUserClaims.tenantNamesidebar 빈 paragraph{ tenantId, tenantName, role } 모두 set
embedding.vector 가 일반 number[]findNearest 가 0 반환 (vector index 인식 X)FieldValue.vector(arr) wrap
case.status 가 invalid 값dashboard·case list 의 status 필터·뱃지에서 미인식apps/web/types/case.ts:CaseStatus 일치 ("소제기"/"진행중"/"판결대기"/"집행중"/"종결")

2. ops e2e 의 client signIn

ops 의 onSnapshot 은 firestore rules 가 request.auth 검증 요구. cookie 만 inject 하면 client currentUser=null → KPI 등 데이터 fetch 가 permission denied.

apps/ops/lib/firebase/client.ts 가 dev+emulator 한정 window.__opsTestSignIn helper expose. spec 에서:

import { loginToOpsViaCookie, clientSignInOps, OPS_URL } from "../helpers/ops-auth";

await loginToOpsViaCookie(context); // SSR 가드 통과
await page.goto(`${OPS_URL}/dashboard`);
await clientSignInOps(page); // client SDK currentUser set
await page.reload({ waitUntil: "domcontentloaded" }); // onSnapshot listener 재등록

3. emulator 한계 (회피 패턴)

한계회피
findNearest vector index 자동 생성 XRAG 격리는 path-scoped UI listing 으로 cover, 진짜 vector 격리는 unit test (apps/web/lib/firebase/__tests__/find-nearest.test.ts)
aggregateCaseStats Cloud Function 비결정성KPI 정확 숫자 매칭 X. "양수만" + "Firestore onSnapshot 실패 sentinel 0건" invariant
ops Google OAuth 만 노출password REST 로 idToken → cookie inject (helpers/ops-auth.ts)

4. invariant 설계 원칙

  • 결정적 값은 정확 매칭 — 활성 Tenant = 50 (시드 보장)
  • 비결정적 값은 비교/양수 — 누적 사건 > 0, 진행중 ≤ 전체
  • sentinel 메시지 0건 — "Firestore onSnapshot 실패" 같은 production 경고 미노출
  • prefix 매칭으로 cross-tenant 누설 검출MT-007- vs MT-008- 같이 시드 시점부터 식별자 분리

5. 회귀 정책

임계값 좁히지 말고 production 코드 수정.

stress spec 이 fail 하면 임계값을 완화하는 게 아니라 production 코드를 fix (페이지네이션 추가·집계 캐시 필드·가상 스크롤·index 추가). nightly run 결과 → 회귀 발견 시 fix PR.

검증 영역 7 차원

  1. 수직 — 페이지네이션·N+1·인덱스·findNearest (단일 tenant 사건 수천건)
  2. 수평 격리cases·legacyDocuments·identity (변호사법 §26 비밀유지의무)
  3. 운영자 시점 — ops SSR 가드 + KPI sentinel
  4. 사용자 행동 — 탭 클릭·검색 디바운스·필터 복구
  5. 운영팀 master-detail — 90 카드 mount + 검색 + 카테고리 navigation
  6. ops 공용 자료 — Platform RAG (ADR 0021)
  7. ops 카드 mutation — server action chain + cross-tenant 격리

디스크 잠식 사고 (2026-05-06)

firestore-debug.log 가 47Gi 까지 누적 (cases 2k 시드 + onSnapshot stream verbose). emulator 자체 옵션으로 logging level 제어 안 됨.

예방: package.jsonclean:emulator-logs script 가 pnpm emulator 시작 시 5종 debug log truncate. 누적 0 보장.

참고

  • 인프라 PR: #1720 (단일 인프라) · #1723 (수평 인프라) · #1727 (ops client signIn helper) · #1730 (ops scenarios mount) · #1731 (emulator log fix) · #1733 (ops 카드 mutation)
  • fix PR: #1722 (consents 시드) · #1725 (tenantName + FieldValue.vector) · #1728 (CaseStatus 일치)