Ch12. 모니터링과 장애 대응
Vercel Analytics, 에러 트래킹, 성능 모니터링, 인시던트 플레이북
모니터링 스택 개요
Vercel Analytics와 Speed Insights
// app/layout.tsx
import { Analytics } from '@vercel/analytics/next';
import { SpeedInsights } from '@vercel/speed-insights/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
);
}Speed Insights는 실제 사용자의 Core Web Vitals를 수집합니다:
| 지표 | 설명 | Good | 개선 필요 | 나쁨 |
|---|---|---|---|---|
| LCP | 최대 콘텐츠 페인트 | ≤2.5s | ≤4.0s | >4.0s |
| INP | 다음 페인트까지 상호작용 | ≤200ms | ≤500ms | >500ms |
| CLS | 누적 레이아웃 이동 | ≤0.1 | ≤0.25 | >0.25 |
| FCP | 첫 콘텐츠 페인트 | ≤1.8s | ≤3.0s | >3.0s |
| TTFB | 첫 바이트까지 시간 | ≤800ms | ≤1.8s | >1.8s |
Sentry 에러 트래킹
npx @sentry/wizard@latest -i nextjs// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 0.1, // 프로덕션: 10% 샘플링
replaysSessionSampleRate: 0, // 세션 리플레이 비활성화
replaysOnErrorSampleRate: 1, // 에러 발생 시에만 리플레이 100%
environment: process.env.VERCEL_ENV ?? 'development',
beforeSend(event) {
// PII 제거
if (event.user) {
delete event.user.ip_address;
delete event.user.email;
}
return event;
},
});모노레포에서는 Sentry 프로젝트를 앱별로 분리합니다. DSN만 앱별 환경 변수로 다르게 설정하면 됩니다.
구조화 로깅
// packages/utils/src/logger.ts
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
type LogEntry = {
level: LogLevel;
message: string;
timestamp: string;
service: string;
[key: string]: unknown;
};
export function createLogger(service: string) {
return {
info: (message: string, meta?: Record<string, unknown>) =>
log('info', service, message, meta),
warn: (message: string, meta?: Record<string, unknown>) =>
log('warn', service, message, meta),
error: (message: string, meta?: Record<string, unknown>) =>
log('error', service, message, meta),
};
}
function log(level: LogLevel, service: string, message: string, meta?: Record<string, unknown>) {
const entry: LogEntry = {
level,
message,
timestamp: new Date().toISOString(),
service,
...meta,
};
console[level](JSON.stringify(entry));
}// apps/api/src/routes/users.ts
import { createLogger } from '@acme/utils/logger';
const logger = createLogger('api');
logger.info('사용자 생성', { userId: user.id, plan: 'pro' });
logger.error('결제 실패', { userId: user.id, errorCode: 'CARD_DECLINED' });JSON 형식으로 출력하면 Vercel Logs, Datadog, CloudWatch 등에서 구조화 검색이 가능합니다.
인시던트 대응 플레이북
| 심각도 | 기준 | 응답 시간 | 에스컬레이션 |
|---|---|---|---|
| P0 (Critical) | 서비스 전체 다운 | 15분 | 즉시 팀 전체 |
| P1 (High) | 핵심 기능 장애 | 30분 | 담당 팀 리드 |
| P2 (Medium) | 비핵심 기능 장애 | 4시간 | 담당 개발자 |
| P3 (Low) | UI 버그, 성능 저하 | 24시간 | 백로그 |
## 인시던트 대응 체크리스트
### 1. 감지 및 확인
- [ ] Sentry/Vercel 알림 확인
- [ ] 영향 범위 파악 (앱, 사용자 수)
- [ ] 심각도 분류 (P0-P3)
### 2. 초기 대응
- [ ] 인시던트 채널 생성 (#incident-YYYYMMDD)
- [ ] 담당자 지정 (Incident Commander)
- [ ] 상태 페이지 업데이트
### 3. 복구
- [ ] 원인 파악
- [ ] 핫픽스 또는 롤백 결정
- [ ] 복구 확인 후 모니터링 강화
### 4. 후속 조치
- [ ] 포스트모템 작성 (5 Whys)
- [ ] 재발 방지 액션 아이템 등록
- [ ] 모니터링/알림 개선Vercel 롤백
# 직전 배포로 즉시 롤백
vercel rollback
# 특정 배포로 롤백
vercel rollback [deployment-url]Vercel의 Instant Rollback은 이전 빌드 아티팩트를 그대로 재활성화하므로, 재빌드 없이 즉시 복구됩니다.
참고 문서
- Vercel: Analytics (영어)
- Vercel: Speed Insights (영어)
- Sentry: Next.js Guide (영어)
- web.dev: Core Web Vitals (영어)