Claude Code × AWS CloudWatch 실전 가이드: 로그, 지표, 알람, 대시보드, 장애 회고
Claude Code로 CloudWatch 로그, 지표, 알람, 대시보드와 장애 회고를 구현하는 실전 가이드.
운영 장애에서 가장 힘든 상황은 로그가 전혀 없는 경우만이 아닙니다. 로그는 많은데 필드가 제각각이고, 알람은 너무 자주 울리며, 어디부터 확인해야 할지 모르는 상황도 똑같이 위험합니다. AWS CloudWatch에는 Logs, Metrics, Alarms, Dashboards, Logs Insights가 있지만, 설계 없이 쓰면 큰 검색창에 머무릅니다.
이 글은 Claude Code를 사용해 CloudWatch 운영을 실제 구현으로 옮기는 방법을 다룹니다. Lambda, ECS, API Gateway, ALB를 위한 구조화 JSON 로그, Logs Insights 쿼리, Metric Filter, CloudFormation/SAM 알람, Dashboard JSON, IAM 최소 권한, 장애 회고 프롬프트를 포함합니다. 목표는 AI가 프로덕션을 마음대로 바꾸는 것이 아니라, 반복적이고 실수하기 쉬운 설정과 쿼리 작성을 빠르게 만드는 것입니다.
용어를 먼저 정리합니다. 구조화 로그는 자유 문장이 아니라 JSON처럼 고정된 필드를 가진 로그입니다. 지표는 요청 수, 5xx 수, P95 지연 시간처럼 그래프로 볼 수 있는 숫자입니다. 알람은 지표가 기준을 넘을 때 알리는 규칙입니다. 런북은 장애 때 따라가는 체크리스트입니다. 최소 권한은 조사에 필요한 읽기 권한만 먼저 주는 방식입니다.
전체 구조
flowchart LR
App["Lambda / ECS / API Gateway"] --> Logs["CloudWatch Logs"]
App --> Metrics["CloudWatch Metrics"]
Logs --> Insights["Logs Insights 쿼리"]
Logs --> Filter["Metric filters"]
Metrics --> Alarms["CloudWatch Alarms"]
Metrics --> Dash["Dashboards"]
Insights --> Claude["Claude Code 장애 회고"]
Alarms --> Runbook["SNS / PagerDuty / runbook"]
Claude Code에는 로그 형식, 시간 범위, 서비스 이름, 알람 의도, 허용할 명령을 구체적으로 알려줘야 합니다. 그래야 생성된 쿼리와 IaC를 사람이 검토하기 쉬워집니다.
실제 활용 사례
첫 번째는 Lambda 배치 실패입니다. REPORT 라인만으로는 업무 원인을 알기 어렵습니다. jobId, 외부 API 이름, 재시도 횟수, 예외 이름을 JSON 로그에 넣고 Claude Code에 최근 두 시간의 실패가 특정 파트너, 특정 배포, 특정 입력에 몰렸는지 요약하게 합니다.
두 번째는 ECS API의 5xx 증가입니다. ALB의 HTTPCode_Target_5XX_Count는 문제가 발생했다는 신호일 뿐입니다. Logs Insights로 route, statusCode, durationMs를 집계하면 Claude Code가 가장 시끄러운 엔드포인트, 느린 P95, 배포 이후 새로 등장한 오류를 정리할 수 있습니다.
세 번째는 API Gateway 지연 시간입니다. Latency와 IntegrationLatency를 나누어 보면 병목이 게이트웨이 쪽인지, Lambda/ECS 백엔드 쪽인지 구분할 수 있습니다. 평균보다 P95/P99를 대시보드에 두는 것이 운영 판단에 더 유용합니다.
네 번째는 알람 피로입니다. Warning은 Slack, Critical은 PagerDuty로 보내고, 같은 원인의 여러 알람은 복합 알람으로 줄입니다. TreatMissingData도 반드시 명시합니다.
구조화 JSON 로그 출력
아래 Node.js 로거는 Lambda와 ECS에서 모두 사용할 수 있습니다. 중요한 점은 필드 이름을 항상 같게 유지하는 것입니다.
// logger.mjs
export function logEvent(level, message, fields = {}) {
const entry = {
timestamp: new Date().toISOString(),
level,
message,
service: process.env.SERVICE_NAME || "checkout-api",
env: process.env.NODE_ENV || "development",
requestId: fields.requestId || "unknown",
route: fields.route,
statusCode: fields.statusCode,
durationMs: fields.durationMs,
userId: fields.userId,
errorName: fields.error?.name,
errorMessage: fields.error?.message,
};
console.log(JSON.stringify(entry));
}
logEvent("ERROR", "payment authorization failed", {
requestId: "req-123",
route: "POST /checkout",
statusCode: 502,
durationMs: 842,
userId: "user-456",
error: new Error("upstream timeout"),
});
로컬 확인:
node logger.mjs | jq .
카드 번호, 토큰, 이메일 원문은 로그에 남기지 마세요. CloudWatch Logs의 데이터 보호 기능을 쓰더라도 애플리케이션에서 민감 정보를 내보내지 않는 설계가 먼저입니다.
Logs Insights 쿼리 만들기
Claude Code에는 로그 스키마와 질문을 함께 줍니다.
claude -p "
CloudWatch Logs Insights 쿼리를 만들어 주세요.
로그는 JSON이며 timestamp, level, message, service, route, statusCode, durationMs, requestId, userId 필드가 있습니다.
필요한 것:
1. 최근 1시간 route별 5xx Top 10
2. P95 지연 시간이 높은 route
3. requestId 기준 전체 타임라인
4. 배포 이후 증가한 errorName
쿼리만 출력하고 짧은 설명 주석을 붙여 주세요.
"
실제 쿼리 예시는 다음과 같습니다.
-- route별 5xx 수
fields @timestamp, route, statusCode, requestId
| filter statusCode >= 500
| stats count(*) as errors by route
| sort errors desc
| limit 10
-- route별 P95 지연 시간
fields route, durationMs
| filter ispresent(durationMs)
| stats pct(durationMs, 95) as p95, count(*) as requests by route
| sort p95 desc
| limit 20
-- requestId 타임라인
fields @timestamp, level, message, route, statusCode, durationMs
| filter requestId = "req-123"
| sort @timestamp asc
-- 오류 이름 집계
fields @timestamp, errorName, route
| filter level = "ERROR" and ispresent(errorName)
| stats count(*) as count by errorName, route
| sort count desc
| limit 20
비용 함정은 스캔 범위입니다. Logs Insights는 스캔한 데이터 양에 따라 과금되므로 시간 범위와 log group을 좁혀야 합니다.
Metric Filter와 SAM 알람
업무 오류를 CloudWatch 지표로 바꾸려면 Metric Filter가 편리합니다.
Resources:
CheckoutLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/checkout-api
RetentionInDays: 30
PaymentFailureMetricFilter:
Type: AWS::Logs::MetricFilter
Properties:
LogGroupName: !Ref CheckoutLogGroup
FilterPattern: '{ $.level = "ERROR" && $.message = "payment authorization failed" }'
MetricTransformations:
- MetricNamespace: MyApp/Business
MetricName: PaymentFailure
MetricValue: "1"
DefaultValue: 0
PaymentFailureAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: prod-payment-failure-critical
AlarmDescription: Ten minutes with five or more payment failures
Namespace: MyApp/Business
MetricName: PaymentFailure
Statistic: Sum
Period: 300
EvaluationPeriods: 2
DatapointsToAlarm: 2
Threshold: 5
ComparisonOperator: GreaterThanOrEqualToThreshold
TreatMissingData: notBreaching
AlarmActions:
- arn:aws:sns:ap-northeast-2:123456789012:prod-alerts
EvaluationPeriods는 평가할 기간 수이고 DatapointsToAlarm은 그중 몇 번 초과해야 알람으로 볼지입니다. 한 번만 보면 빠르지만 시끄럽고, 너무 길면 늦게 발견합니다.
Dashboard를 JSON으로 관리
대시보드는 콘솔에서만 만들면 재현성이 떨어집니다.
{
"widgets": [
{
"type": "metric",
"x": 0,
"y": 0,
"width": 12,
"height": 6,
"properties": {
"region": "ap-northeast-2",
"title": "API health: requests, 5xx, latency",
"view": "timeSeries",
"metrics": [
["AWS/ApplicationELB", "RequestCount", "LoadBalancer", "app/myapp/abc", { "stat": "Sum" }],
[".", "HTTPCode_Target_5XX_Count", ".", ".", { "stat": "Sum", "yAxis": "right" }],
["AWS/ApiGateway", "Latency", "ApiName", "checkout-api", "Stage", "prod", { "stat": "p95" }]
],
"period": 60
}
}
]
}
aws cloudwatch put-dashboard \
--dashboard-name myapp-production \
--dashboard-body file://dashboard.json
장애 회고 프롬프트와 IAM
조사 범위와 읽기 전용 명령을 제한합니다.
claude -p "
프로덕션 장애를 회고해 주세요. 사실과 가설을 분리해 주세요.
범위:
- log groups: /aws/lambda/checkout-api, /ecs/checkout-api
- window: 2026-06-02T10:00:00+09:00 to 2026-06-02T11:00:00+09:00
- recent change: checkout-api v1.42.0 deploy
허용 명령:
- aws logs start-query / get-query-results
- aws cloudwatch get-metric-data
- aws cloudwatch describe-alarms
출력: 타임라인, 영향 범위, 원인 가설 Top3, 즉시 조치, 재발 방지, 부족한 알람.
"
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:StartQuery",
"logs:GetQueryResults",
"logs:FilterLogEvents",
"cloudwatch:GetMetricData",
"cloudwatch:DescribeAlarms",
"cloudwatch:GetDashboard"
],
"Resource": "*"
}
]
}
자주 빠지는 함정
알람 피로가 가장 흔합니다. CPU 같은 내부 증상보다 5xx, P95 지연, 큐 적체, 결제 실패처럼 사용자 영향에 가까운 지표를 우선하세요.
로그 보존 기간을 설정하지 않으면 비용이 조용히 증가합니다. Lambda와 ECS 로그 그룹은 기본 30일로 시작하고, 감사 대상만 길게 보관합니다.
userId나 requestId를 metric dimension에 넣으면 고카디널리티 문제가 생깁니다. 상세 추적은 로그로, 집계는 지표로 분리합니다.
Claude Code에 원본 로그를 너무 많이 붙여 넣는 것도 문제입니다. 먼저 쿼리로 좁히고 민감 정보를 마스킹한 뒤 근거 있는 결론을 요구하세요.
다음 단계
중요 API 하나를 골라 JSON 로그, 5xx 쿼리, P95 쿼리, Critical 알람, 대시보드 한 줄을 적용하세요. 이후 Claude Code × AWS ECS/Fargate 가이드와 Claude Code × AWS IAM 가이드를 함께 보면 배포, 권한, 관측을 연결할 수 있습니다.
이 글의 실습 확인: Node logger 출력은 jq로 로컬 확인했고, SAM 조각은 log group, 리전, 계정, SNS ARN을 바꾸면 기존 템플릿에 넣을 수 있는 구조로 작성했습니다. 실제 호출 알람은 테스트 계정에서 임계값을 조정한 뒤 운영에 넣으세요.
공식 문서
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Obsidian 메모를 CLAUDE.md로 바꾸는 Claude Code 워크플로
Obsidian 작업 메모를 CLAUDE.md 운영 노트로 정리해 Claude Code 세션의 문맥 반복을 줄입니다.
Claude Code Revenue CTA Routing: 글에서 PDF, Gumroad, 상담으로 보내기
독자 의도에 따라 무료 PDF, Gumroad 상품, 상담으로 나누는 Claude Code CTA 설계입니다.
Claude Code 팀 인계 규칙: 리뷰 증거, 권한, 롤백, 수익 경로까지 넘기는 법
Claude Code 작업을 팀에 넘길 때 필요한 증거, 권한 규칙, 롤백, 무료 PDF, Gumroad, 상담 경로 체크리스트.