본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 11. 1. · 13 Views
Monitoring 디자인 패턴 완벽 가이드
시스템의 상태와 성능을 실시간으로 추적하고 관리하는 Monitoring 디자인 패턴을 학습합니다. 메트릭 수집, 헬스 체크, 로깅, 알림 등 프로덕션 환경에서 필수적인 모니터링 기법을 다룹니다.
들어가며
이 글에서는 Monitoring 디자인 패턴 완벽 가이드에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Health_Check_패턴
- Metrics_Collector_패턴
- Request_Monitoring_Middleware
- Circuit_Breaker_with_Monitoring
- Alert_Manager_패턴
- Distributed_Tracing_패턴
- Performance_Monitor_패턴
- Resource_Monitor_패턴
- Error_Tracking_패턴
- Query_Performance_Monitor
- Aggregated_Metrics_패턴
- SLA_Monitor_패턴
1. Health Check 패턴
개요
서비스의 상태를 주기적으로 확인하여 정상 작동 여부를 판단하는 패턴입니다. 데이터베이스, 외부 API 등 의존성 체크를 포함합니다.
코드 예제
class HealthChecker {
async check(): Promise<{ status: string; checks: any[] }> {
const checks = await Promise.all([
this.checkDatabase(),
this.checkRedis(),
this.checkExternalAPI()
]);
const status = checks.every(c => c.healthy) ? 'healthy' : 'unhealthy';
return { status, checks };
}
private async checkDatabase() {
try {
await db.query('SELECT 1');
return { name: 'database', healthy: true };
} catch (error) {
return { name: 'database', healthy: false, error: error.message };
}
}
private async checkRedis() {
return { name: 'redis', healthy: await redis.ping() };
}
private async checkExternalAPI() {
const start = Date.now();
const healthy = Date.now() - start < 5000;
return { name: 'external-api', healthy, responseTime: Date.now() - start };
}
}
설명
각 의존성의 상태를 비동기로 체크하고, 모든 체크가 성공해야 healthy 상태를 반환합니다. 타임아웃과 에러 핸들링을 포함합니다.
2. Metrics Collector 패턴
개요
애플리케이션의 메트릭(요청 수, 응답 시간, 에러율 등)을 수집하고 집계하는 패턴입니다. Prometheus 스타일의 메트릭 수집을 구현합니다.
코드 예제
class MetricsCollector {
private metrics = new Map<string, number>();
increment(name: string, value = 1): void {
this.metrics.set(name, (this.metrics.get(name) || 0) + value);
}
histogram(name: string, value: number): void {
const key = `${name}_sum`;
const countKey = `${name}_count`;
this.metrics.set(key, (this.metrics.get(key) || 0) + value);
this.increment(countKey);
}
gauge(name: string, value: number): void {
this.metrics.set(name, value);
}
getMetrics(): Record<string, number> {
return Object.fromEntries(this.metrics);
}
}
const metrics = new MetricsCollector();
metrics.increment('http_requests_total', 1);
metrics.histogram('http_request_duration_ms', 245);
설명
Counter, Histogram, Gauge 타입의 메트릭을 수집하며, Map을 사용해 메트릭을 메모리에 저장하고 조회할 수 있습니다.
3. Request Monitoring Middleware
개요
HTTP 요청의 성능과 에러를 자동으로 추적하는 미들웨어 패턴입니다. 응답 시간, 상태 코드, 에러율을 기록합니다.
코드 예제
function monitoringMiddleware(metrics: MetricsCollector) {
return async (req: Request, res: Response, next: NextFunction) => {
const start = Date.now();
const path = req.route?.path || req.path;
res.on('finish', () => {
const duration = Date.now() - start;
const status = res.statusCode;
metrics.increment(`http_requests_total{method="${req.method}",path="${path}",status="${status}"}`);
metrics.histogram(`http_request_duration_ms{path="${path}"}`, duration);
if (status >= 500) {
metrics.increment(`http_errors_total{path="${path}"}`);
}
});
next();
};
}
설명
Express 미들웨어로 모든 HTTP 요청을 자동 추적하며, 응답 완료 시점에 메트릭을 기록합니다. 라벨을 사용해 세분화된 메트릭을 수집합니다.
4. Circuit Breaker with Monitoring
개요
외부 서비스 호출 실패를 모니터링하고 자동으로 차단하는 Circuit Breaker 패턴입니다. 실패율을 추적하고 임계값 초과 시 요청을 차단합니다.
코드 예제
class MonitoredCircuitBreaker {
private failures = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
private nextAttempt = 0;
async execute<T>(fn: () => Promise<T>, metrics: MetricsCollector): Promise<T> {
if (this.state === 'OPEN' && Date.now() < this.nextAttempt) {
metrics.increment('circuit_breaker_rejected');
throw new Error('Circuit breaker is OPEN');
}
try {
const result = await fn();
this.onSuccess(metrics);
return result;
} catch (error) {
this.onFailure(metrics);
throw error;
}
}
private onSuccess(metrics: MetricsCollector) {
this.failures = 0;
this.state = 'CLOSED';
metrics.gauge('circuit_breaker_state', 0);
}
private onFailure(metrics: MetricsCollector) {
this.failures++;
metrics.increment('circuit_breaker_failures');
if (this.failures >= 5) {
this.state = 'OPEN';
this.nextAttempt = Date.now() + 60000;
metrics.gauge('circuit_breaker_state', 1);
}
}
}
설명
연속 실패 횟수를 추적하고 임계값 초과 시 OPEN 상태로 전환합니다. 각 상태 변화와 실패를 메트릭으로 기록하여 모니터링합니다.
5. Alert Manager 패턴
개요
메트릭 임계값을 모니터링하고 조건 충족 시 알림을 발송하는 패턴입니다. 다양한 알림 채널(이메일, Slack 등)을 지원합니다.
코드 예제
interface AlertRule {
name: string;
condition: (metrics: Record<string, number>) => boolean;
message: string;
}
class AlertManager {
private rules: AlertRule[] = [];
private alertHistory = new Map<string, number>();
addRule(rule: AlertRule) {
this.rules.push(rule);
}
async evaluate(metrics: Record<string, number>) {
for (const rule of this.rules) {
if (rule.condition(metrics)) {
await this.sendAlert(rule);
}
}
}
private async sendAlert(rule: AlertRule) {
const lastAlert = this.alertHistory.get(rule.name) || 0;
if (Date.now() - lastAlert < 300000) return; // 5분 쿨다운
await this.notifySlack(rule.message);
this.alertHistory.set(rule.name, Date.now());
}
private async notifySlack(message: string) {
// Slack 웹훅 호출
}
}
const alertManager = new AlertManager();
alertManager.addRule({
name: 'high_error_rate',
condition: (m) => (m.http_errors_total / m.http_requests_total) > 0.05,
message: 'Error rate exceeded 5%'
});
설명
알림 규칙을 정의하고 메트릭을 주기적으로 평가하여 조건 충족 시 알림을 발송합니다. 쿨다운 기능으로 알림 스팸을 방지합니다.
6. Distributed Tracing 패턴
개요
마이크로서비스 환경에서 요청의 전체 흐름을 추적하는 분산 트레이싱 패턴입니다. Trace ID와 Span ID를 사용해 요청 경로를 추적합니다.
코드 예제
class Span {
constructor(
public traceId: string,
public spanId: string,
public parentSpanId: string | null,
public operationName: string,
public startTime: number,
public tags: Record<string, any> = {}
) {}
finish() {
const duration = Date.now() - this.startTime;
console.log({
traceId: this.traceId,
spanId: this.spanId,
parentSpanId: this.parentSpanId,
operation: this.operationName,
duration,
tags: this.tags
});
}
}
class Tracer {
startSpan(operationName: string, parentSpan?: Span): Span {
const traceId = parentSpan?.traceId || this.generateId();
const spanId = this.generateId();
return new Span(traceId, spanId, parentSpan?.spanId || null, operationName, Date.now());
}
private generateId() {
return Math.random().toString(36).substr(2, 9);
}
}
설명
각 작업을 Span으로 표현하고 Trace ID로 연결하여 전체 요청 경로를 추적합니다. 부모-자식 관계로 호출 스택을 재구성할 수 있습니다.
7. Performance Monitor 패턴
개요
함수나 메서드의 실행 시간을 자동으로 측정하고 기록하는 데코레이터 패턴입니다. 성능 병목 지점을 식별할 수 있습니다.
코드 예제
function Monitor(metricName: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args: any[]) {
const start = Date.now();
const span = tracer.startSpan(metricName);
try {
const result = await originalMethod.apply(this, args);
const duration = Date.now() - start;
metrics.histogram(`${metricName}_duration_ms`, duration);
metrics.increment(`${metricName}_success`);
span.finish();
return result;
} catch (error) {
metrics.increment(`${metricName}_error`);
span.tags.error = true;
span.finish();
throw error;
}
};
return descriptor;
};
}
class UserService {
@Monitor('user_service_get_user')
async getUser(id: string) {
return await db.users.findById(id);
}
}
설명
데코레이터로 메서드를 자동으로 감싸서 실행 시간, 성공/실패를 추적합니다. 트레이싱과 메트릭 수집을 동시에 수행합니다.
8. Resource Monitor 패턴
개요
CPU, 메모리, 디스크 등 시스템 리소스 사용량을 주기적으로 수집하는 패턴입니다. Node.js의 process 모듈을 활용합니다.
코드 예제
class ResourceMonitor {
private interval: NodeJS.Timeout | null = null;
start(metrics: MetricsCollector, intervalMs = 10000) {
this.interval = setInterval(() => {
const memUsage = process.memoryUsage();
const cpuUsage = process.cpuUsage();
metrics.gauge('process_memory_heap_used_bytes', memUsage.heapUsed);
metrics.gauge('process_memory_heap_total_bytes', memUsage.heapTotal);
metrics.gauge('process_memory_rss_bytes', memUsage.rss);
metrics.gauge('process_cpu_user_seconds_total', cpuUsage.user / 1000000);
metrics.gauge('process_cpu_system_seconds_total', cpuUsage.system / 1000000);
metrics.gauge('process_uptime_seconds', process.uptime());
}, intervalMs);
}
stop() {
if (this.interval) clearInterval(this.interval);
}
}
const resourceMonitor = new ResourceMonitor();
resourceMonitor.start(metrics, 10000);
설명
10초마다 메모리와 CPU 사용량을 수집하여 메트릭으로 기록합니다. 메모리 누수나 CPU 과부하를 조기에 감지할 수 있습니다.
9. Error Tracking 패턴
개요
애플리케이션 에러를 수집하고 분류하여 추적하는 패턴입니다. 에러 타입별 발생 빈도와 스택 트레이스를 기록합니다.
코드 예제
interface ErrorEvent {
type: string;
message: string;
stack?: string;
timestamp: number;
context?: Record<string, any>;
}
class ErrorTracker {
private errors: ErrorEvent[] = [];
track(error: Error, context?: Record<string, any>) {
const event: ErrorEvent = {
type: error.name,
message: error.message,
stack: error.stack,
timestamp: Date.now(),
context
};
this.errors.push(event);
metrics.increment(`errors_total{type="${error.name}"}`);
if (this.errors.length > 1000) {
this.errors = this.errors.slice(-1000); // 최근 1000개만 유지
}
}
getErrorsByType(type: string): ErrorEvent[] {
return this.errors.filter(e => e.type === type);
}
getRecentErrors(count = 10): ErrorEvent[] {
return this.errors.slice(-count);
}
}
const errorTracker = new ErrorTracker();
try {
throw new Error('Database connection failed');
} catch (error) {
errorTracker.track(error, { userId: '123', endpoint: '/api/users' });
}
설명
에러 발생 시 타입별로 분류하고 컨텍스트 정보와 함께 저장합니다. 메트릭으로도 기록하여 에러율 추이를 모니터링할 수 있습니다.
10. Query Performance Monitor
개요
데이터베이스 쿼리 성능을 추적하고 느린 쿼리를 감지하는 패턴입니다. 쿼리 실행 시간과 빈도를 기록합니다.
코드 예제
class QueryMonitor {
private slowQueryThreshold = 1000; // 1초
async monitor<T>(queryName: string, queryFn: () => Promise<T>): Promise<T> {
const start = Date.now();
const span = tracer.startSpan(`db.query.${queryName}`);
try {
const result = await queryFn();
const duration = Date.now() - start;
metrics.histogram(`db_query_duration_ms{query="${queryName}"}`, duration);
metrics.increment(`db_query_total{query="${queryName}"}`);
if (duration > this.slowQueryThreshold) {
console.warn(`Slow query detected: ${queryName} took ${duration}ms`);
metrics.increment(`db_slow_queries_total{query="${queryName}"}`);
}
span.tags.duration = duration;
span.finish();
return result;
} catch (error) {
metrics.increment(`db_query_errors_total{query="${queryName}"}`);
span.tags.error = true;
span.finish();
throw error;
}
}
}
const queryMonitor = new QueryMonitor();
const users = await queryMonitor.monitor('find_users', () => db.users.find({ active: true }));
설명
쿼리를 래핑하여 실행 시간을 측정하고, 임계값을 초과하는 느린 쿼리를 자동으로 감지합니다. 쿼리별 성능 통계를 수집할 수 있습니다.
11. Aggregated Metrics 패턴
개요
시계열 메트릭을 시간 단위로 집계하여 저장하는 패턴입니다. 분 단위, 시간 단위 통계를 생성합니다.
코드 예제
class MetricsAggregator {
private buckets = new Map<string, { count: number; sum: number; min: number; max: number }>();
record(metricName: string, value: number, timestamp = Date.now()) {
const minute = Math.floor(timestamp / 60000) * 60000;
const key = `${metricName}:${minute}`;
const bucket = this.buckets.get(key) || { count: 0, sum: 0, min: Infinity, max: -Infinity };
bucket.count++;
bucket.sum += value;
bucket.min = Math.min(bucket.min, value);
bucket.max = Math.max(bucket.max, value);
this.buckets.set(key, bucket);
}
getStats(metricName: string, minute: number) {
const key = `${metricName}:${minute}`;
const bucket = this.buckets.get(key);
if (!bucket) return null;
return {
count: bucket.count,
avg: bucket.sum / bucket.count,
min: bucket.min,
max: bucket.max,
sum: bucket.sum
};
}
}
설명
메트릭을 분 단위로 집계하여 평균, 최소값, 최대값 등의 통계를 계산합니다. 시계열 데이터를 효율적으로 저장하고 조회할 수 있습니다.
12. SLA Monitor 패턴
개요
서비스 수준 목표(SLO)를 모니터링하고 SLA 준수 여부를 추적하는 패턴입니다. 가용성, 응답 시간 등의 SLI를 측정합니다.
코드 예제
interface SLO {
name: string;
target: number; // 예: 99.9% 가용성
window: number; // 측정 기간 (밀리초)
}
class SLAMonitor {
private slos: SLO[] = [];
private measurements = new Map<string, boolean[]>();
addSLO(slo: SLO) {
this.slos.push(slo);
this.measurements.set(slo.name, []);
}
recordSuccess(sloName: string, success: boolean) {
const measurements = this.measurements.get(sloName) || [];
measurements.push(success);
const maxMeasurements = 10000;
if (measurements.length > maxMeasurements) {
measurements.shift();
}
this.measurements.set(sloName, measurements);
}
getCompliance(sloName: string): number {
const slo = this.slos.find(s => s.name === sloName);
const measurements = this.measurements.get(sloName) || [];
if (!slo || measurements.length === 0) return 0;
const successCount = measurements.filter(m => m).length;
const compliance = (successCount / measurements.length) * 100;
metrics.gauge(`sla_compliance_percent{slo="${sloName}"}`, compliance);
return compliance;
}
}
const slaMonitor = new SLAMonitor();
slaMonitor.addSLO({ name: 'api_availability', target: 99.9, window: 86400000 });
slaMonitor.recordSuccess('api_availability', true);
설명
SLO별로 성공/실패를 기록하고 준수율을 계산합니다. 최근 측정값을 기반으로 SLA 목표 달성 여부를 실시간으로 모니터링합니다.
마치며
이번 글에서는 Monitoring 디자인 패턴 완벽 가이드에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#TypeScript #Monitoring #HealthCheck #Metrics #Observability
댓글 (0)
함께 보면 좋은 카드 뉴스
마이크로서비스 배포 완벽 가이드
Kubernetes를 활용한 마이크로서비스 배포의 핵심 개념부터 실전 운영까지, 초급 개발자도 쉽게 따라할 수 있는 완벽 가이드입니다. 실무에서 바로 적용 가능한 배포 전략과 노하우를 담았습니다.
Grafana 대시보드 완벽 가이드
실시간 모니터링의 핵심, Grafana 대시보드를 처음부터 끝까지 배워봅니다. Prometheus 연동부터 알람 설정까지, 초급 개발자도 쉽게 따라할 수 있는 실전 가이드입니다.
Prometheus 메트릭 수집 완벽 가이드
Spring Boot 애플리케이션의 메트릭을 Prometheus로 수집하고 모니터링하는 방법을 배웁니다. Actuator 설정부터 PromQL 쿼리까지 실무에 필요한 모든 내용을 다룹니다.
스프링 관찰 가능성 완벽 가이드
Spring Boot 3.x의 Observation API를 활용한 애플리케이션 모니터링과 추적 방법을 초급 개발자 눈높이에서 쉽게 설명합니다. 실무에서 바로 적용할 수 있는 메트릭 수집과 분산 추적 기법을 다룹니다.
Zipkin으로 추적 시각화 완벽 가이드
마이크로서비스 환경에서 분산 추적을 시각화하는 Zipkin의 핵심 개념과 활용 방법을 초급자도 쉽게 이해할 수 있도록 실무 스토리로 풀어낸 가이드입니다. Docker 실행부터 UI 분석까지 단계별로 배웁니다.