본문으로 건너뛰기

의뢰인 포털 메시지 알림 운영 매뉴얼

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_KEYfunctions Secret권장이메일 silent skip · 인앱은 정상
RESEND_FROM_EMAILfunctions env선택기본 noreply@pro.neohollo.com
APP_BASE_URLfunctions 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. 실패 진단 체크리스트

의뢰인이 메시지를 보냈는데 변호사가 알림을 못 받는다

  1. 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 누락된 이력 있는지.
  2. 변호사 측 NotificationBell 확인

    • 데이터는 들어갔는데 안 보임 → NotificationType union 에 client_message_received 있는지 확인 (apps/web/types/notification.ts)
    • NOTIF_META 매핑 빠짐 → fallback (case_created 아이콘) 으로 표시됨. 정상 매핑 추가.
  3. 이메일 안 옴

    • Resend dashboard (resend.com) → Logs → 해당 이메일 검색
    • bounce / spam 차단 확인
    • RESEND_API_KEY 만료 가능성

의뢰인이 보낸 메시지가 변호사 사건 메시지 탭에 안 보인다

알림 이슈가 아닌 메시지 자체 write 실패. portal-redesign Phase 2 (#94) 진단:

  1. iron-session 세션 만료 (30일 sliding window — 의뢰인이 재로그인 필요)
  2. 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

  1. portal-test 토큰 발급 (Owner 측 사건 상세 → "포털 링크" 다이얼로그)
  2. 시크릿 브라우저에서 4자리 코드로 포털 진입
  3. PortalMessagesTab 에서 메시지 전송
  4. 변호사 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 경로의 누락 메시지 재처리)