Sentry와 Betterstack을 활용해 $0로 구축하는 최소 모니터링 가이드와 론칭 전 80분 투자가 에러 대응 시간을 13시간에서 1시간으로 단축한 사례 분석
사용자가 알려주는 에러
WICHI를 론칭하고 첫 주, 에러 모니터링이 없었습니다. 기능은 잘 작동하는 것 같았고, 로컬에서 테스트도 통과했습니다.
문제를 알게 된 건 사용자 피드백이었습니다.
| # | 사용자 피드백 | 실제 원인 | 발견까지 걸린 시간 |
|---|---|---|---|
| 1 | ”결과 페이지가 안 떠요” | 특정 쿼리에서 API 타임아웃 | ~18시간 |
| 2 | ”분석이 중간에 멈춰요” | LLM 응답 파싱 실패 | ~12시간 |
| 3 | ”결제했는데 크레딧 안 올라가요” | Webhook 처리 중 예외 | ~6시간 |
| 4 | ”어제까지 됐는데 오늘 안 돼요” | 의존 서비스 API 변경 | ~24시간 |
graph TD
A["에러 발생"] --> B{"모니터링\n있나?"}
B -->|"있음"| C["즉시 알림\n(분 단위)"]
B -->|"없음"| D["사용자가 발견"]
D --> E["사용자가 연락"]
E --> F["개발자 확인"]
F --> G["원인 파악"]
C --> G
H["모니터링 없음:\n평균 15시간"] --> I["모니터링 있음:\n평균 5분"]
style D fill:#ffebee,stroke:#f44336
style C fill:#e8f5e9,stroke:#4caf50
style H fill:#ffebee,stroke:#f44336
style I fill:#e8f5e9,stroke:#4caf50
사용자가 에러를 제보하는 순간, 이미 3가지가 잘못된 겁니다: (1) 에러가 발생했고, (2) 모니터링이 없어서 몰랐고, (3) 사용자 경험이 나빠졌습니다.
모니터링의 3가지 층
모니터링은 한 가지가 아닙니다. 최소 3개 층이 필요합니다.
graph TD
subgraph L1["Layer 1: Uptime 모니터링"]
U1["서비스가 살아있는가?"]
U2["응답 시간이 정상인가?"]
U3["SSL 인증서가 유효한가?"]
end
subgraph L2["Layer 2: 에러 추적"]
E1["런타임 에러 캡처"]
E2["에러 빈도와 영향 범위"]
E3["스택 트레이스"]
end
subgraph L3["Layer 3: 로그 관리"]
L3a["구조화된 로그"]
L3b["로그 검색/필터"]
L3c["로그 보존 기간"]
end
L1 --> L2 --> L3
style L1 fill:#e8f5e9,stroke:#4caf50
style L2 fill:#e3f2fd,stroke:#2196f3
style L3 fill:#fff3e0,stroke:#ff9800
| 층 | 답하는 질문 | 도구 예시 | 우선순위 |
|---|---|---|---|
| Uptime | ”서비스가 죽었나?” | Betterstack, UptimeRobot | 1순위 |
| 에러 추적 | ”어떤 에러가 발생했나?” | Sentry, Bugsnag | 1순위 |
| 로그 관리 | ”왜 발생했나?” | Betterstack Logs, Datadog | 2순위 |
| APM | ”어디가 느린가?” | Sentry Performance, New Relic | 3순위 |
| 사용자 분석 | ”무엇을 하고 있나?” | PostHog, Mixpanel | 3순위 |
1인 SaaS에서 1순위는 Uptime + 에러 추적입니다. “서비스가 죽었나?”와 “에러가 났나?”에 답할 수 있으면 80%는 커버됩니다.
Sentry: 에러 추적
왜 Sentry인가
| 도구 | 무료 티어 | 에러 추적 | 소스맵 | 성능 | 알림 |
|---|---|---|---|---|---|
| Sentry | 5K 이벤트/월 | ✅ | ✅ | ✅ | ✅ |
| Bugsnag | 7.5K 이벤트/월 | ✅ | ✅ | ❌ | ✅ |
| Rollbar | 5K 이벤트/월 | ✅ | ✅ | ❌ | ✅ |
| LogRocket | 1K 세션/월 | ✅ | ✅ | ✅ | ✅ |
| 자체 구축 | 무료 | 수동 | 수동 | 수동 | 수동 |
Sentry를 선택한 이유:
- 무료 티어가 충분: 5,000 이벤트/월. 초기 SaaS에는 넉넉함
- SDK가 모든 프레임워크 지원: Next.js, Express, Python, React 등
- 소스맵 자동 업로드: minified 코드에서도 원본 위치 확인
- 릴리즈 추적: 어떤 배포에서 에러가 시작됐는지 확인
최소 설정
// sentry.config.js (Next.js 예시)
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV,
// 프로덕션에서만 활성화
enabled: process.env.NODE_ENV === 'production',
// 에러 샘플링 100% (초기에는 모든 에러 캡처)
sampleRate: 1.0,
// 성능 샘플링 10% (무료 티어 절약)
tracesSampleRate: 0.1,
});
에러에 맥락 추가
// 사용자 컨텍스트
Sentry.setUser({
id: user.id,
email: user.email, // 주의: GDPR 고려
});
// 추가 태그
Sentry.setTag('plan', user.plan); // free, pro, enterprise
Sentry.setTag('feature', 'analysis'); // 기능 구분
// 수동 에러 캡처
try {
await processAnalysis(query);
} catch (error) {
Sentry.captureException(error, {
extra: {
queryId: query.id,
engineCount: query.engines.length,
},
});
throw error;
}
Sentry 무료 티어 최적화
5,000 이벤트/월을 효율적으로 쓰려면:
| 전략 | 방법 | 효과 |
|---|---|---|
| 중복 제거 | Sentry가 자동으로 같은 에러 그룹화 | 이벤트 수 절약 |
| 에러 필터링 | 404, 봇 트래픽 무시 | 노이즈 제거 |
| 샘플링 | tracesSampleRate: 0.1 | 성능 이벤트 90% 절약 |
| 환경 분리 | 프로덕션만 전송 | 개발 이벤트 제외 |
// 불필요한 에러 필터링
Sentry.init({
beforeSend(event) {
// 404는 무시
if (event.exception?.values?.[0]?.type === 'NotFoundError') {
return null;
}
// 봇 트래픽 무시
if (event.request?.headers?.['user-agent']?.includes('bot')) {
return null;
}
return event;
},
});
Betterstack: Uptime + 로그
왜 Betterstack인가
| 도구 | 무료 티어 | Uptime | 로그 | 상태 페이지 | 알림 채널 |
|---|---|---|---|---|---|
| Betterstack | 5 모니터, 로그 1GB | ✅ | ✅ | ✅ | Slack, Email, SMS |
| UptimeRobot | 50 모니터 | ✅ | ❌ | ✅ | |
| Pingdom | 유료 | ✅ | ❌ | ✅ | 다양 |
| Datadog | 유료 | ✅ | ✅ | ❌ | 다양 |
Betterstack을 선택한 이유:
- Uptime + 로그가 한 곳에: 별도 도구 2개 대신 1개로 해결
- 무료 상태 페이지: 사용자에게 서비스 상태를 보여줄 수 있음
- 깔끔한 UI: 1인 개발자가 빠르게 파악 가능
- Heartbeat 모니터: cron job, 백그라운드 작업 감시
Uptime 모니터 설정
Betterstack에서 설정할 모니터:
| # | 모니터 대상 | 체크 방법 | 주기 | 알림 |
|---|---|---|---|---|
| 1 | 메인 사이트 | HTTP 200 확인 | 3분 | Slack + Email |
| 2 | API 헬스체크 | /api/health 엔드포인트 | 1분 | Slack + Email |
| 3 | 결제 Webhook | Heartbeat (주기적 ping) | 5분 | Email + SMS |
| 4 | SSL 인증서 | 만료일 확인 | 1일 | Email (30일 전) |
헬스체크 엔드포인트
// /api/health — 서비스 상태 확인용
app.get('/api/health', async (req, res) => {
const checks = {
server: 'ok',
database: 'unknown',
cache: 'unknown',
};
try {
await db.query('SELECT 1');
checks.database = 'ok';
} catch {
checks.database = 'error';
}
try {
await redis.ping();
checks.cache = 'ok';
} catch {
checks.cache = 'error';
}
const healthy = Object.values(checks).every(v => v === 'ok');
res.status(healthy ? 200 : 503).json({
status: healthy ? 'healthy' : 'degraded',
checks,
timestamp: new Date().toISOString(),
});
});
헬스체크 엔드포인트는 단순히 200을 반환하는 게 아니라, DB와 주요 의존성의 상태도 확인해야 합니다. 서버는 살아있는데 DB 연결이 끊어진 경우를 잡아내려면 이 구분이 필요합니다.
상태 페이지
Betterstack 무료 티어에서 상태 페이지를 제공합니다.
status.myapp.com
├── API ● Operational
├── Website ● Operational
├── Database ● Operational
└── Payments ● Operational
Uptime: 99.95% (last 90 days)
상태 페이지가 중요한 이유:
| 상태 페이지 없을 때 | 상태 페이지 있을 때 |
|---|---|
| 사용자: “왜 안 돼?” → 이메일/DM 폭주 | 사용자: 상태 페이지 확인 → “아 점검 중이구나” |
| 개발자: 문의 대응에 시간 소모 | 개발자: 수정에 집중 |
| 신뢰 하락: “이 서비스 불안한데?” | 신뢰 유지: “투명하게 운영하네” |
Sentry + Betterstack 조합: WICHI 적용 사례
모니터링 아키텍처
graph TD
subgraph APP["WICHI Application"]
FE["Frontend\n(Next.js)"]
BE["Backend\n(Express)"]
CRON["Cron Jobs"]
end
subgraph SENTRY["Sentry (에러 추적)"]
SE["에러 캡처"]
SP["성능 모니터링"]
SR["릴리즈 추적"]
end
subgraph BETTER["Betterstack (Uptime + 로그)"]
BU["Uptime 모니터"]
BL["로그 수집"]
BS["상태 페이지"]
end
subgraph ALERT["알림"]
SLACK["Slack"]
EMAIL["Email"]
end
FE --> SE
BE --> SE
BE --> BL
CRON --> BL
BU -->|"3분마다 체크"| BE
SE --> SLACK
BU --> SLACK
BU --> EMAIL
style SENTRY fill:#e3f2fd,stroke:#2196f3
style BETTER fill:#e8f5e9,stroke:#4caf50
역할 분담
| 상황 | 감지 주체 | 알림 내용 |
|---|---|---|
| 런타임 에러 (500) | Sentry | 에러 메시지 + 스택 트레이스 + 사용자 컨텍스트 |
| 서비스 다운 | Betterstack Uptime | ”API 응답 없음” + 다운타임 기록 |
| 백그라운드 작업 실패 | Betterstack Heartbeat | ”마지막 heartbeat 5분 전” |
| 응답 시간 저하 | Sentry Performance | ”p95 응답 시간 3초 초과” |
| SSL 만료 임박 | Betterstack | ”SSL 인증서 30일 후 만료” |
실제 에러 대응 사례
모니터링 도입 후, 같은 종류의 에러가 발생했을 때의 차이:
graph LR
subgraph BEFORE["Before: 모니터링 없음"]
B1["에러 발생\n(오전 2시)"] --> B2["사용자 발견\n(오전 10시)"]
B2 --> B3["피드백 전달\n(오전 11시)"]
B3 --> B4["원인 파악\n(오후 1시)"]
B4 --> B5["수정 배포\n(오후 3시)"]
end
subgraph AFTER["After: Sentry + Betterstack"]
A1["에러 발생\n(오전 2시)"] --> A2["Slack 알림\n(오전 2시 1분)"]
A2 --> A3["아침에 확인\n(오전 9시)"]
A3 --> A4["원인 파악\n(오전 9시 15분)\n스택 트레이스 즉시 확인"]
A4 --> A5["수정 배포\n(오전 10시)"]
end
style BEFORE fill:#ffebee,stroke:#f44336
style AFTER fill:#e8f5e9,stroke:#4caf50
| 지표 | Before | After |
|---|---|---|
| 에러 인지 시간 | ~8시간 | ~1분 (알림) |
| 원인 파악 시간 | ~2시간 | ~15분 (스택 트레이스) |
| 총 대응 시간 | ~13시간 | ~1시간 |
| 사용자 영향 시간 | ~13시간 | ~8시간 (야간 발생 시) |
무료 티어 비용 계산
1인 SaaS 초기에는 모니터링에 돈을 쓸 필요가 없습니다.
| 도구 | 무료 범위 | 초과 시 비용 | 초기 SaaS 충분? |
|---|---|---|---|
| Sentry | 5,000 이벤트/월 | $26/월 (50K) | ✅ 충분 |
| Betterstack Uptime | 5 모니터 | $24/월 (20개) | ✅ 충분 |
| Betterstack Logs | 1GB/월 | $24/월 (5GB) | ✅ 충분 |
| 합계 | $0/월 | — | — |
무료 티어를 오래 쓰는 팁
| 전략 | Sentry | Betterstack |
|---|---|---|
| 환경 분리 | 프로덕션만 전송 | 프로덕션 엔드포인트만 모니터 |
| 필터링 | 404, 봇 에러 제외 | 불필요한 로그 레벨 제외 |
| 샘플링 | 성능 이벤트 10%만 | — |
| 보존 기간 | 기본 90일 | 기본 3일 (무료), 핵심 로그만 |
론칭 전 모니터링 체크리스트
| # | 항목 | 도구 | 소요 시간 |
|---|---|---|---|
| 1 | Sentry 프로젝트 생성 + SDK 설치 | Sentry | 15분 |
| 2 | 에러 알림 → Slack 연동 | Sentry | 5분 |
| 3 | 에러 필터링 설정 (404, 봇 제외) | Sentry | 10분 |
| 4 | Uptime 모니터 설정 (사이트 + API) | Betterstack | 10분 |
| 5 | 헬스체크 엔드포인트 구현 | 직접 | 20분 |
| 6 | 상태 페이지 설정 | Betterstack | 10분 |
| 7 | Heartbeat 모니터 (cron job용) | Betterstack | 10분 |
| 합계 | ~80분 |
graph LR
A["80분 투자"] --> B["에러 인지 시간\n8시간 → 1분"]
A --> C["원인 파악 시간\n2시간 → 15분"]
A --> D["사용자 불만\n감소"]
A --> E["비용\n$0/월"]
style A fill:#e3f2fd,stroke:#2196f3
style E fill:#e8f5e9,stroke:#4caf50
80분이면 설정이 끝납니다. 모니터링 없이 론칭하면, 첫 번째 에러에서 그 80분의 100배를 쓰게 됩니다.
모니터링 → 체크리스트 항목
MMU의 534개 체크리스트 중 모니터링 관련은 38개입니다. 그 중 론칭 전 필수 항목 10개:
| # | 항목 | MMU 카테고리 |
|---|---|---|
| 1 | 에러 추적 도구가 프로덕션에 설치되어 있는가 | Monitoring |
| 2 | Uptime 모니터가 설정되어 있는가 | Monitoring |
| 3 | 에러 알림이 Slack/Email로 전달되는가 | Monitoring |
| 4 | 헬스체크 엔드포인트가 있는가 | Monitoring |
| 5 | 상태 페이지가 있는가 | Monitoring |
| 6 | 소스맵이 에러 추적 도구에 업로드되는가 | Monitoring |
| 7 | 에러에 사용자 컨텍스트가 포함되는가 | Monitoring |
| 8 | 백그라운드 작업에 heartbeat 모니터가 있는가 | Monitoring |
| 9 | SSL 인증서 만료 알림이 설정되어 있는가 | Security |
| 10 | 로그에 민감 정보(비밀번호, 토큰)가 포함되지 않는가 | Security |
이 10개는 모두 무료 도구로, 총 80분 안에 설정할 수 있습니다. “론칭 후에 하자”는 “에러가 터진 후에 하자”와 같습니다.
정리
| 핵심 | 내용 |
|---|---|
| 모니터링 없이 론칭한 결과 | 에러를 사용자 제보로 알게 됨 (평균 15시간 후) |
| 최소 조합 | Sentry (에러 추적) + Betterstack (Uptime + 로그) |
| 비용 | $0/월 (무료 티어로 충분) |
| 설정 시간 | ~80분 |
| 효과 | 에러 인지 8시간 → 1분, 원인 파악 2시간 → 15분 |
| 원칙 | 론칭 전에 모니터링이 먼저. 기능보다 먼저 |
관련 글

Solo SaaS 보안 — 최소한 이것만은 하자
1인 SaaS 빌더를 위한 필수 보안 체크리스트. OWASP Top 10 중 1인 개발에 치명적인 5가지 취약점 방어와 WICHI에서 보완한 실제 보안 설정 사례.

멀티엔진 아키텍처 — 3개 AI 엔진 병렬 수집 구조
AI 검색 엔진 간 응답 편차를 시그널로 활용하는 멀티엔진 아키텍처 설계 원칙과 병렬 수집 구조, 어댑터 패턴을 통한 확장성 확보 방법 분석

Self-Tuning Loop 직접 만들기 — 레퍼런스 구현 가이드
Self-Tuning Loop 4단계(Generate → Capture → Analyze → Evolve)를 범용 모듈로 추출. Supabase DDL, diff 캡처 유틸, 분석/진화 프롬프트 전문, 이메일/블로그 적용 예시, GitHub 레퍼런스 구현.