의뢰인 포털 메시지 알림 운영 매뉴얼
ADR 0025 (Phase 3 PR-1 · PR-3 · PR-4) 의 운영 측면. 의뢰인 → 변호사 메시지 도착 시 자동 알림 (이메일 + 인앱) 의 상태 확인 · 실패 진단 · 환경변수 점검.
1. 데이터 흐름 한눈에
의뢰인 포털 (PortalMessagesTab)
↓ sendPortalMessageAction
Firestore: tenants/{tid}/cases/{cid}/messages/{mid}.create()
↓ Firestore onCreate trigger (region: asia-northeast3)
Cloud Function: onPortalMessageCreated
├─ filter: from === "client" 만 처리
├─ tenant ownerUid → members/{uid} email 조회
├─ Firestore: tenants/{tid}/notifications/{nid}.add (인앱)
└─ Resend API: POST /emails (이메일)
↓ (변호사 측 수신)
Email + NotificationBell 카드
↓ 카드 클릭 (notificationDeeplink)
/cases/{caseId}?tab=messages
2. 환경변수 점검 (production)
| 변수 | 위치 | 필수 | 미설정 시 거동 |
|---|---|---|---|
RESEND_API_KEY | functions Secret | 권장 | 이메일 silent skip · 인앱은 정상 |
RESEND_FROM_EMAIL | functions env | 선택 | 기본 noreply@pro.neohollo.com |
APP_BASE_URL | functions env | 선택 | 기본 https://pro.neohollo.com (이메일 deeplink base) |
점검 명령:
firebase functions:secrets:access RESEND_API_KEY
firebase functions:config:get # APP_BASE_URL · RESEND_FROM_EMAIL
3. 실패 진단 체크리스트
의뢰인이 메시지를 보냈는데 변호사가 알림을 못 받는다
-
Cloud Function 로그 확인 (Firebase Console → Functions → onPortalMessageCreated → Logs)
[portal-message] 데이터 없음→ message write 자체가 실패. portal write path 점검.[portal-message] case ${caseId} 없음→ race condition 가능. 재현 잡아 신고.[portal-message] tenant ${tid} ownerUid 없음→ tenant 문서 데이터 결손. ops 콘솔에서 확인.[portal-message] tenant ${tid} owner email 없음 — 인앱만 발송→members/{uid}.email비어있음. owner 가입 시 email 누락된 이력 있는지.
-
변호사 측 NotificationBell 확인
- 데이터는 들어갔는데 안 보임 →
NotificationTypeunion 에client_message_received있는지 확인 (apps/web/types/notification.ts) - NOTIF_META 매핑 빠짐 → fallback (case_created 아이콘) 으로 표시됨. 정상 매핑 추가.
- 데이터는 들어갔는데 안 보임 →
-
이메일 안 옴
- Resend dashboard (resend.com) → Logs → 해당 이메일 검색
- bounce / spam 차단 확인
RESEND_API_KEY만료 가능성
의뢰인이 보낸 메시지가 변호사 사건 메시지 탭에 안 보인다
알림 이슈가 아닌 메시지 자체 write 실패. portal-redesign Phase 2 (#94) 진단:
- iron-session 세션 만료 (30일 sliding window — 의뢰인이 재로그인 필요)
- portalToken 이 revoked 상태 (4자리 코드 오입력 누적 → 자동 잠금)
4. 운영 모니터링 지표
알림 발화 카운트
Cloud Functions Console → onPortalMessageCreated → Metrics:
- Invocations / day
- Error rate (목표 < 1%)
- p95 execution time (목표 < 3000ms — Resend 호출 포함)
Resend 도달률
- Resend dashboard → Analytics → Delivered / Opened
- bounce ratio 5% 초과 시 from email reputation 점검
NotificationBell unread 누적
- 변호사가 알림을 자주 무시하면 (unreadNotifs.length > 30 등) UX 개선 필요
- 알림 too noisy → 의뢰인 메시지 전송 빈도 조정 또는 batch 알림 후속
5. 재현 / 테스트
로컬 (Firebase Emulator)
# 1. emulator 시작
firebase emulators:start --only functions,firestore
# 2. 메시지 직접 write (firestore-emulator REST 또는 콘솔)
POST tenants/test-tid/cases/test-cid/messages
body: { from: "client", text: "테스트 메시지", senderName: "테스트 의뢰인", createdAt: serverTimestamp }
# 3. Functions 로그에 [portal-message] 호출 흐름 확인
# 4. Firestore tenants/test-tid/notifications 에 새 문서 생성됐는지 확인
Production smoke test
- portal-test 토큰 발급 (Owner 측 사건 상세 → "포털 링크" 다이얼로그)
- 시크릿 브라우저에서 4자리 코드로 포털 진입
- PortalMessagesTab 에서 메시지 전송
- 변호사 dashboard NotificationBell + 이메일 확인 (수초 이내 도달)
6. 알려진 한계 (ADR 0025 명시)
- 이메일 도달률: Resend 무료 티어 + 환경변수 미설정 시 silent skip
- 인앱 알림 mark-as-read: NotificationBell 카드 클릭 시 자동 (PR-3 통합 완료)
- 의뢰인 측 외부 알림 미구현: 변호사 답신을 의뢰인이 즉시 인지하려면 SOLAPI 필요 → ADR 0026 예정
- 알림 소실 시 자동 재시도 없음: Cloud Function 자체 throw 시만 재시도. silent log 경로는 재시도 없음. 운영 알림 누락이 의심되면 수동 재발송 (트리거를 다시 호출하는 대체 path 부재 — backfill 스크립트 별도 필요 시 작성).
7. 향후 작업
- ADR 0026: SOLAPI SMS / 카카오톡 알림톡 (의뢰인 측 외부 알림)
- batch 알림 (의뢰인이 1분 안에 여러 메시지 → 1 통합 이메일)
- ADR 0027: PWA + FCM (변호사 모바일 push)
- 알림 발화 백필 스크립트 (silent log 경로의 누락 메시지 재처리)