Day 43~49: 마이페이지·통계·Sentry 시작
7주차 페이스 2.5h/일 — 운영자 의식보다 자동 트리거가 강하다
📑 이 챕터에서 다룰 내용
권3 제2장 (Day 42 G3 PASSED) 에서 6주차 끝 4주 누적 77h → 7주차 페이스 추가 축소 (2.5h/일) 결정. 이번 주는 "매일 2.5h" 페이스로 마이페이지·통계·Sentry 시작합니다.
| 사전 지식 체크 | 이 장의 목적 | 완료 후 결과물 |
|---|---|---|
| Day 30~42 / G3 PASSED / 7주차 페이스 2.5h/일 / G4 통과 조건 (Sentry 7일 무장애) | Day 43 G3 휴식 + Day 44~48 마이페이지·통계·KPI·Sentry + Day 49 회고. 페이스 보호 강화 | 마이페이지 + 통계 차트 + Phase 1 KPI 어드민 + Sentry 통합 + 7일 무장애 관측 시작 |
## Phase 1.0 Day 43 — ★ G3 통과 후 휴식 ★ [계획] - 작업 X (G3 통과 의식 + R4 보호) [실행] - 작업 X - 가족·자유 시간 - BUILD.md·CLAUDE.md 노출 X (강제 단절) [E2 자동 보호 효과] - G3 통과 의식 (1일 휴식) - R4 4주 누적 77h → Day 43 휴식 → 다음 4주 시작점 - 7주차 페이스 2.5h/일 (지금까지 최저) [누적] 126h (Day 43 변동 없음)
작업 (2.5h — 페이스 추가 축소)
// lib/profile-stats.ts
export interface UserStats {
total_jupjups: number;
total_success: number;
total_failed: number;
total_in_progress: number;
avg_days_taken: number | null; // SUCCESS만
recent_jupjups: JupjupItem[]; // 최근 10건
}
export async function getUserStats(userId: string): Promise {
// Supabase RPC 호출 (단일 쿼리 + 인덱스 활용)
const { data, error } = await supabase.rpc('get_user_stats', {
user_id: userId,
});
if (error) throw error;
return data;
}
CREATE OR REPLACE FUNCTION get_user_stats(user_id UUID)
RETURNS JSONB AS $$
DECLARE
result JSONB;
BEGIN
SELECT jsonb_build_object(
'total_jupjups', (SELECT COUNT(*) FROM jupjups j WHERE j.user_id = $1),
'total_success', (SELECT COUNT(*) FROM jupjups j WHERE j.user_id = $1
AND j.result = 'SUCCESS'),
'total_failed', (SELECT COUNT(*) FROM jupjups j WHERE j.user_id = $1
AND j.result = 'FAILED'),
'total_in_progress', (SELECT COUNT(*) FROM jupjups j
WHERE j.user_id = $1 AND j.result = 'IN_PROGRESS'),
'avg_days_taken', (SELECT AVG(days_taken) FROM jupjups j
WHERE j.user_id = $1 AND j.result = 'SUCCESS'),
'recent_jupjups', (
SELECT jsonb_agg(jsonb_build_object(
'id', j.id,
'benefit_id', j.benefit_id,
'benefit_name', b.name,
'result', j.result,
'created_at', j.created_at
) ORDER BY j.created_at DESC)
FROM jupjups j JOIN benefits b ON b.id = j.benefit_id
WHERE j.user_id = $1 LIMIT 10
)
) INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql STABLE;
결정: get_user_stats RPC 함수 (서버 측 집계)
근거: 1탄 v2 새 9장 9-5 절 사례 적용. 클라이언트 집계 = N+1 쿼리 위험. RPC 단일 쿼리 = 인덱스 활용 + 1 round trip.
대안: 클라이언트 집계 (5 SELECT 호출) — 성능 문제
누적: 126h + Day 44 (2.5h) = 128.5h
요구사항:
1. components/StatsChart.tsx (90줄):
- 마이페이지: 성공률 도넛 + 결과별 막대
- 상세 페이지: benefit별 성공률 + 평균 소요 일수
2. recharts 또는 Victory Native 사용 (Expo 호환)
3. CLAUDE.md §5 [2] 톤 분기:
- 사장님 톤: '성공률 N% (사장님 N명)'
- 시민 톤: '받은 비율 N% (시민 N명)'
4. 데이터 부족 (jupjups < 3건) 시: '아직 충분한 데이터 없음' 표시
5. ⚖️ E1: 차트 옆 면책 1줄
('통계는 사용자 후기 기반, 신청 결과 보장 X')
결정: jupjups < 3건 benefit는 차트 노출 X (텍스트 fallback)
근거: 통계 신뢰성 = 표본 크기. 1~2건으로 도넛 차트 = 오해 유발 (예: 1명 성공 → 100% 성공률 잘못 인식)
대안: 모든 benefit 차트 노출 — 통계 오해 위험 = E1 변호 논리 흔들림
부작용: 신규 benefit 통계 부족 → Phase 2 데이터 누적 후 자연 해결
누적: 128.5h + Day 45 (2.5h) = 131h
요구사항:
1. src/app/admin/kpi/page.tsx (160줄):
- [1] 단계적 기여 부담 곡선:
· 부담 0 (체크박스) 도달 사용자: COUNT
· 부담 0 → 낮음 (3택) 전환율: %
· 부담 낮음 → 선택 (자유 후기) 전환율: %
- [2] LLM 분류 confidence 분포:
· auto_active 비중 / admin_queue 비중
· 검수 SLA 7일 준수율
- [3] 사용자 신뢰 메트릭:
· 신고 발생 비율 (건/100 jupjups)
· 후기 작성 비율 (8%+ 목표)
2. 자동 갱신: 5분 캐시 (Supabase realtime 미사용)
| KPI 지표 | 목표 | 시뮬 결과 | 판정 |
|---|---|---|---|
| 부담 0 → 낮음 전환율 | ≥10% | 70% | ✅ 통과 |
| 부담 낮음 → 선택 전환율 | ≥30% | 60% | ✅ 통과 |
| auto_active 비중 | 정상 분포 | 65% | ✅ 정상 |
| 검수 SLA 7일 준수율 | 100% | 100% | ✅ 통과 |
| 후기 작성 비율 | ≥8% | 80% (시뮬) | ✅ 통과 |
주의: 시뮬 데이터 기반. 실제 출시 후 (Day 73~) 사용자 데이터로 본격 검증합니다.
누적: 131h + Day 46 (2.5h) = 133.5h
Day 47 작업 (2.5h)
요구사항: 1. Expo + @sentry/react-native 설치 2. Sentry 프로젝트 생성 + DSN 받음 (.env.local) 3. src/lib/sentry.ts (50줄): - Sentry.init() — 앱 시작 시 - 에러 capture 자동 - 사용자 컨텍스트 (user_id, nickname) 자동 포함 4. supabase/functions/_shared/sentry.ts: - Edge Function 에러도 Sentry 보고 5. G4 통과 조건: '7일 무장애 관측' 시작 (Day 56 까지)
결정: Sentry DSN은 NEXT_PUBLIC_ / EXPO_PUBLIC_ 환경변수로 공개
근거: Sentry DSN = 공개 가능 키 (악용 시 Sentry 알림 다 받기만, 데이터 유출 X). CLAUDE.md §3 절대 준수 — _SECRET_ 패턴 X.
부작용: 누구나 DSN 보고 가짜 에러 보낼 수 있음 → Sentry 무료 한도 (5K 에러/월) 소비 가능. 발생 시 Sentry inbound filter 설정
누적: 133.5h + Day 47 (2.5h) = 136h
Day 48 작업 (2.5h)
에러 그룹화 룰: - LLM 분류 실패 (G-1) → 'llm-classification' 그룹 - IP rate limit 차단 → 정상 동작 (Sentry 보고 X) - DB 트리거 실패 → 'db-trigger' 그룹 - 카카오 토큰 갱신 실패 → 'auth-kakao' 그룹 Slack 통합 — Critical 에러 발생 시 즉시 Slack 알림 ★ Day 48 자정부터 7일 무장애 관측 시작 (Day 55 까지)
결정: IP rate limit 차단은 정상 동작 → Sentry 보고 X
근거: 의도된 보안 동작. 사용자 잘못된 동작에 의한 정상 reject. Sentry에 보내면 "실제 에러"와 혼선
대안: 모든 reject Sentry 보고 — 노이즈 ↑ → 진짜 에러 놓침
누적: 136h + Day 48 (2.5h) = 138.5h
## Phase 1.0 Day 49 — 7주차 회고 [E2 4 트리거 점검] ☐ 누적 60h+/4주 — 4주 누적 (4~7주차): 21+24+18+12.5 = 75.5h ⚠️ (7주차 페이스 축소로 회복 진행) ☐ 회고 부재 2주+ — No ☐ 토요일 4주 연속 — No (일관 유지) ☐ "피곤하다" 등장 — No [★ 자동 결정] - 8주차 (Day 50~56) 페이스 2.5h/일 유지 (G4 통과 의무 우선) - Day 56 G4 통과 후 추가 휴식 (Day 57 휴식) ## 7주차 회고 (Day 43~49) ### 산출물 (Day 44~48, 5일) 1. src/app/profile/index.tsx (140줄, 마이페이지) 2. lib/profile-stats.ts (60줄) 3. supabase/migrations/0005_user_stats_rpc.sql (45줄) 4. components/StatsChart.tsx (90줄, 통계 차트 + 두 톤) 5. src/app/admin/kpi/page.tsx (160줄, Phase 1 KPI) 6. lib/admin/kpi-stats.ts (70줄) 7. src/lib/sentry.ts + _shared/sentry.ts (90줄) 총: 7개 파일 + 1 SQL = ~655줄 ### Sentry 7일 무장애 관측 시작 - Day 48 자정 ~ Day 55 자정 - Critical 에러 발견 시 Slack 즉시 알림 - 7일 무장애 = G4 통과 조건 (Day 56) [누적] 138.5h (Day 49 변동 없음 — 회고만) [7주차 누적] 12.5h (Day 44~48, 페이스 축소 효과)
[권1·권2·권3 5·6주차] 126h [권3 7주차] 12.5h (페이스 추가 축소 + 휴식 1일) [누적 합계] 138.5h [4주 누적 (4~7주차)] 21+24+18+12.5 = 75.5h ⚠️ (회복 진행) [★ 자동 보호 의식 본격 입증 사이클] - 5주차 끝 73h → 6주차 페이스 25% 축소 (4h → 3h) - 6주차 끝 77h → 7주차 페이스 추가 축소 (3h → 2.5h) - 7주차 끝 75.5h → 8주차 유지 + Day 57 휴식 의무 "피곤하다" 신호 0회이지만 의식적 자동 보호 작동.
- 부담 0 → 낮음: 70% (목표 ≥10% 통과)
- 부담 낮음 → 선택: 60% (목표 ≥30% 통과)
- auto_active 비중: 65%
- 검수 SLA 준수율: 100%
- 후기 작성 비율: 80% (시뮬, 실제 출시 후 검증)
📌 권3 제3장 정리
- 핵심 한 줄: Day 43~49 = G3 휴식 + 마이페이지·통계·Phase 1 KPI + Sentry 통합 + 7일 무장애 관측 시작. 페이스 2.5h/일 (지금까지 최저).
- 7주차 산출물: 마이페이지 (140줄) + RPC 함수 (45줄) + 통계 차트 (90줄) + KPI 어드민 (160줄) + Sentry 통합 (90줄)
- ★ E2 자동 보호 의식 작동 사이클: 5주차 끝 73h → 페이스 -25% / 6주차 끝 77h → 페이스 추가 -17% / 7주차 끝 75.5h → 회복 진행. "피곤하다" 신호 0회, 자동 트리거가 보호
- Sentry 7일 무장애 관측: Day 48 ~ Day 55 / Critical 에러 → Slack 즉시 / G4 통과 조건 (Day 56)
- 누적: 138.5h / 4주 누적 75.5h
- 다음 장: 권3 제4장 — Day 50~56 (G4 통과: FCM 알림 + 공유 카드 + 1순위 커뮤니티 측정)