SLA 준수를 추적하는 웹 앱을 설계·구현하는 방법: 지표 정의, 이벤트 수집, 계산 로직, 위반 알림, 정확한 보고 및 감사 가능성 확보 방법을 설명합니다.

SLA 준수는 서비스 수준 협약(SLA)—제공자와 고객 간의 계약—에 명시된 측정 가능한 약속을 충족하는 것을 의미합니다. 이 앱의 역할은 증거를 가지고 간단한 질문에 답하는 것입니다: 이 기간 동안, 이 고객에 대해 우리가 약속한 것을 지켰는가?
세 가지 관련 용어를 분리하면 도움이 됩니다:
대부분의 SLA 추적 웹앱은 운영 데이터에 매핑되는 소수의 지표로 시작합니다:
서로 다른 사용자는 같은 진실을 다르게 보길 원합니다:
이 제품은 추적, 증거, 보고에 관한 것입니다: 신호 수집, 합의된 규칙 적용, 감사 친화적 결과 생성. 성능을 보장하는 것이 아니라—정확하고 일관되게 측정하여 나중에 방어할 수 있게 만드는 도구입니다.
테이블을 설계하거나 코드를 작성하기 전에, 당신의 비즈니스에서 “준수”가 무엇을 의미하는지 고통스럽게 명확히 하세요. 대부분의 SLA 추적 문제는 기술 문제가 아니라 요구사항 문제입니다.
신뢰의 출처를 먼저 수집하세요:
이 규칙들을 명시적 규칙으로 문서화하세요. 규칙이 명확히 서술될 수 없다면 신뢰성 있게 계산할 수 없습니다.
SLA 수치에 영향을 줄 수 있는 실제 “사물”을 나열하세요:
또한 누가 무엇을 필요로 하는지도 식별하세요: 지원팀은 실시간 위반 위험을 원하고, 매니저는 주간 집계, 고객은 상태 페이지용 간단한 요약을 원합니다.
범위를 작게 유지하세요. 시스템이 엔드투엔드로 작동함을 증명할 최소 집합을 선택하세요. 예:
나중에 테스트할 수 있도록 한 페이지 체크리스트를 만드세요:
성공은 두 사람이 샘플 월을 수동으로 같은 방식으로 계산했을 때 당신의 앱이 정확히 동일한 결과를 내는 것입니다.
올바른 SLA 트래커는 수치가 왜 그런지를 설명할 수 있는 데이터 모델에서 시작합니다. 월별 가용성 수치를 정확한 이벤트와 사용된 규칙으로 추적할 수 없다면 고객 분쟁과 내부 불확실성과 싸우게 됩니다.
최소한 다음을 모델링하세요:
유용한 관계는: customer → service → SLA policy(플랜을 통해 연결될 수 있음). 인시던트와 이벤트는 서비스와 고객을 참조합니다.
시간 관련 버그는 잘못된 SLA 산출의 1순위 원인입니다. 다음을 저장하세요:
occurred_at을 UTC(타임존 의미 포함)로 저장received_at(시스템이 이벤트를 본 시각)source(모니터 이름, 통합, 수동)external_id(중복 방지용)payload(향후 디버깅용 원시 JSON)또한 customer.timezone(예: America/New_York 같은 IANA 문자열)은 표시와 영업시간 로직에 사용하되 이벤트 시간을 덮어쓰지 마세요.
응답 시간 SLA가 영업시간 외에 일시 중지된다면 캘린더를 명시적으로 모델링하세요:
working_hours 고객(또는 지역/서비스)별: 요일 + 시작/종료 시간holiday_calendar 지역 또는 고객에 연결된 날짜 범위와 라벨운영팀이 배포 없이 공휴일을 업데이트할 수 있도록 규칙을 데이터 기반으로 유지하세요.
원시 이벤트는 append-only 테이블에 저장하고, 계산된 결과는 별도로 저장하세요(예: sla_period_result). 각 결과 행에는 기간 경계, 입력 버전(정책 버전 + 엔진 버전), 사용된 이벤트 ID 참조를 포함해야 합니다. 이렇게 하면 재계산이 안전하고 고객이 “어떤 중단 분을 계산했나?”라고 물어볼 때 근거를 제시할 수 있습니다.
SLA 수치는 수집하는 이벤트의 신뢰성에 달려 있습니다. 목표는 단순합니다: 중단 시작, 인시던트 확인, 서비스 복구 같은 중요한 모든 변화를 일관된 타임스탬프와 계산에 필요한 맥락과 함께 캡처하세요.
대부분의 팀은 다양한 시스템에서 가져옵니다:
웹후크는 실시간 정확성과 낮은 부하 때문에 보통 최선입니다: 소스 시스템이 당신의 엔드포인트로 이벤트를 푸시합니다.
폴링은 웹후크가 없을 때 좋은 대안입니다: 앱이 마지막 커서 이후 변경사항을 주기적으로 가져옵니다. 속도 제한 처리와 정확한 “since” 로직이 필요합니다.
CSV 임포트는 백필 및 마이그레이션에 유용합니다. 이 경로를 1등 시민으로 취급하여 이력 기간을 재처리할 때 해킹이 필요 없게 하세요.
상류 페이로드가 달라도 내부적으로는 단일 이벤트 형태로 정규화하세요:
event_id(필수): 재시도 간에도 고유하고 안정적. 소스 GUID를 우선 사용하거나 결정적 해시 생성source(필수): 예: datadog, servicenow, manualevent_type(필수): 예: incident_opened, incident_acknowledged, service_down, service_upoccurred_at(필수): 이벤트가 발생한 시각(수신 시각이 아님), 타임존 포함received_at(시스템): 앱이 수신한 시각service_id(필수): SLA 관련 서비스incident_id(선택 권장): 여러 이벤트를 하나의 인시던트로 연결attributes(선택): 우선순위, 지역, 고객 세그먼트 등event_id에 고유 제약을 두어 멱등성(idempotency)을 확보하세요: 재시도는 중복을 만들지 않습니다.
다음과 같은 이벤트는 거부하거나 격리하세요:
occurred_atservice_id로 매핑되지 않은 경우(또는 명시적 “미매핑” 워크플로 요구)event_id와 중복되는 경우이러한 규율은 나중에 SLA 보고서로 다투는 일을 줄여줍니다—왜냐하면 깨끗하고 추적 가능한 입력을 가리킬 수 있기 때문입니다.
계산 엔진은 원시 이벤트를 방어할 수 있는 SLA 결과로 바꾸는 곳입니다. 핵심은 회계처럼 다루는 것입니다: 결정론적 규칙, 명확한 입력, 재생 가능한 트레일.
모든 것을 인시던트별(또는 서비스 영향별)로 단일 정렬된 스트림으로 변환하세요:
이 타임라인에서 구간을 합산해 지속시간을 계산하세요. 두 타임스탬프를 무턱대고 빼지 마세요.
TTFR을 incident_start와 first_agent_response(또는 SLA 문구에 따라 acknowledged) 사이의 경과한 “청구 대상” 시간으로 정의하세요. TTR은 incident_start와 resolved 사이의 청구 대상 시간입니다.
“청구 대상”이란 아래와 같은 구간을 제거한 시간을 의미합니다:
구현 세부: 영업시간·공휴일을 처리하는 캘린더 함수와 타임라인을 받아 청구 대상 구간을 반환하는 규칙 함수를 저장하세요.
사전에 다음 중 어떤 방식으로 계산할지 결정하세요:
부분 중단은 계약에서 가중치를 요구하지 않는 한 별도 위반 카테고리로 처리하거나, 계약 요구사항에 따라 영향 가중치를 적용하세요.
모든 계산은 재현 가능해야 합니다. 다음을 영구 저장하세요:
규칙이 변경되면 버전별로 재실행할 수 있어야 하며, 이력은 덮어쓰지 마세요—감사와 고객 분쟁 해결에 필수적입니다.
보고는 SLA 추적이 신뢰를 얻거나 의심받는 지점입니다. 앱은 어떤 기간을 측정했는지, 어떤 분이 계산 대상인지, 그리고 최종 수치가 어떻게 도출되었는지 분명히 해야 합니다.
고객이 실제로 사용하는 일반적인 보고 기간을 지원하세요:
기간을 month = 3 같은 방식으로 저장하지 말고 명시적 시작/종료 타임스탬프로 저장하여 나중에 계산을 재현할 수 있게 하세요.
혼란의 흔한 원인은 분모가 전체 기간인지, 아니면 “카운트되는(eligible)” 시간인지입니다.
기간별로 두 값을 정의하세요:
그런 다음 계산:
availability_percent = 100 * (eligible_minutes - downtime_minutes) / eligible_minutes
eligible minutes가 0일 수 있는 경우(예: 해당 기간에 영업시간이 전혀 없음)에는 사전에 규칙을 정하세요: “N/A”로 표기하거나 100%로 처리하되 일관성 있고 문서화하세요.
대부분의 SLA는 퍼센트와 이진 결과 둘 다 필요합니다.
또한 대시보드가 임계값을 넘기기 전에 경고할 수 있도록 “위반까지 남은 거리”(남은 에러 버짓)를 유지하세요.
마지막으로 원시 입력(포함/제외된 이벤트 및 조정)을 보관해 모든 보고서가 “이 수치가 왜 그런가?”에 수동 설명 없이 답할 수 있게 하세요.
계산 엔진이 완벽해도 UI가 기본 질문인 “우리가 지금 SLA를 지키고 있는가, 그리고 이유는 무엇인가?”에 즉시 답하지 못하면 실패합니다. 각 화면이 명확한 상태로 시작하고 사람들이 수치와 그 수치를 만든 원시 이벤트로 드릴다운할 수 있게 설계하세요.
개요 대시보드(운영자 및 매니저용). 현재 기간 준수, 가용성, 응답 시간 준수, 그리고 “위반 전 남은 시간” 같은 타일을 앞쪽에 배치하세요. 라벨은 명확하게(예: “이번 달 가용성”) 하세요. 다중 SLA를 지원하면 가장 나쁜 상태를 먼저 보여주고 확장할 수 있게 하세요.
고객 상세(어카운트 팀 및 고객용 보고). 고객 페이지는 해당 고객의 모든 서비스와 SLA 티어를 요약하여 간단한 합격/경고/실패 상태와 짧은 설명(예: “2개의 인시던트가 계산되었음; 18분 다운타임 반영됨”)을 보여야 합니다. /status(고객용 상태 페이지 제공 시)와 보고서 내보내기 링크를 추가하세요.
서비스 상세(심층 조사용). 이 페이지는 정확한 SLA 규칙, 계산 창, 준수 수치가 어떻게 형성되었는지의 분해를 보여줍니다. 시간에 따른 가용성 차트와 SLA에 포함된 인시던트 목록을 포함하세요.
인시던트 타임라인(감사용). 단일 인시던트 뷰는 이벤트 타임라인(감지, 확인, 완화, 해결)과 응답 및 해결 메트릭에 사용된 정확한 타임스탬프를 보여야 합니다.
화면 전반에 걸쳐 필터를 일관되게 하세요: 날짜 범위, 고객, 서비스, 티어, 심각도. 단위를 일관되게 사용하세요(분 vs 초; 소수 자리수). 사용자가 날짜 범위를 변경하면 페이지의 모든 지표를 업데이트해 불일치가 없게 하세요.
각 요약 지표는 “왜?” 경로를 제공해야 합니다:
툴팁은 최소한으로 사용해 “제외된 다운타임”이나 “영업시간” 같은 용어를 정의하고, 서비스 페이지에 정확한 규칙 문구를 표시해 사용자가 추측하지 않게 하세요.
약어 대신 평이한 언어를 사용하세요(대상 사용자에게 친숙하다면 예외). 상태는 색상과 텍스트 레이블을 결합해 모호함을 피하세요(예: “위험: 에러 버짓의 92% 사용”). SLA 규칙과 제외를 변경한 경우 /settings/audit-log 같은 경로로 연결되는 작은 "마지막 변경" 박스를 추가해 사용자가 정의 변경 시점을 확인할 수 있게 하세요.
알림은 SLA 추적 앱이 수동 보고에서 팀이 페널티를 피하도록 돕는 능동적 도구로 전환되는 지점입니다. 최고의 알림은 시기적절하고 구체적이며 실행 가능해야 합니다—단순히 “나쁨”을 알리는 것이 아니라 다음에 무엇을 해야 하는지 알려줘야 합니다.
세 가지 트리거 유형부터 시작하세요:
트리거는 고객/서비스/SLA별로 구성 가능하게 하세요. 서로 다른 계약은 서로 다른 허용치를 가집니다.
사람들이 실제로 대응하는 곳으로 알림을 보내세요:
모든 알림에는 /alerts, /customers/{id}, /services/{id} 같은 딥 링크와 인시던트/이벤트 상세 페이지 링크를 포함해 응답자가 빠르게 숫자를 검증하도록 하세요.
같은 키(고객 + 서비스 + SLA + 기간)를 가진 알림을 그룹화하고 쿨다운 윈도우 동안 반복을 억제하는 중복 제거를 구현하세요.
팀 시간대별 조용한 시간을 추가해 비치명한 “접근 중인 위반” 알림이 영업시간까지 대기하도록 하고, 심각도가 높으면 “위반 발생”이 조용한 시간을 무시하도록 하세요.
마지막으로 에스컬레이션 규칙(예: 10분 후 온콜 알림, 30분 후 매니저에게 에스컬레이션)을 지원해 알림이 한 곳에만 머무르지 않게 하세요.
SLA 데이터는 내부 성능과 고객별 권한을 노출할 수 있어 민감합니다. 접근 제어는 SLA “수학”의 일부로 다뤄야 합니다: 동일한 인시던트라도 어떤 고객의 SLA를 적용하느냐에 따라 결과가 달라질 수 있습니다.
역할은 단순하게 시작하고 필요하면 세분화하세요.
실용적인 기본은 RBAC + 테넌트 스코핑입니다:
고객별 데이터에 대해 명확히 하세요:
초기에 이메일/비밀번호로 시작하고 내부 역할에는 MFA를 요구하세요. 나중에 **SSO(SAML/OIDC)**를 추가할 수 있도록 신원(identity)과 권한(authorization)을 분리해 설계하세요. 통합용으로는 좁은 범위의 서비스 계정 API 키를 발급하고 회전(rotation)을 지원하세요.
다음에 대한 불변 감사 항목을 추가하세요:
누가, 무엇을 변경했는지(전/후), 언제, 어디서(IP/UA), 그리고 상관관계 ID를 저장하세요. 감사 로그는 검색 가능하고 내보낼 수 있게(/settings/audit-log).
SLA 트래킹 앱은 대개 고립되어 있지 않습니다. 모니터링 도구, 티켓 시스템, 내부 워크플로가 인시던트 생성, 이벤트 푸시, 보고서 조회를 자동화할 수 있는 API가 필요합니다.
버전이 명시된 기본 경로(예: /api/v1/...)를 사용해 페이로드를 진화시켜도 기존 통합이 깨지지 않도록 하세요.
대부분의 사용 사례를 커버하는 필수 엔드포인트:
POST /api/v1/events(상태 변경 수집), GET /api/v1/events(감사·디버깅)POST /api/v1/incidents, PATCH /api/v1/incidents/{id}(확인, 해결, 할당), GET /api/v1/incidentsGET /api/v1/slas, POST /api/v1/slas, PUT /api/v1/slas/{id}(계약·임계값 관리)GET /api/v1/reports/sla?service_id=...&from=...&to=...(준수 요약)POST /api/v1/alerts/subscriptions(웹후크/이메일 대상 관리), GET /api/v1/alerts(알림 기록)하나의 관례를 정하고 모든 곳에서 사용하세요. 예: limit, cursor 기반 페이징, 표준 필터(service_id, sla_id, status, from, to). 정렬도 예측 가능하게(sort=-created_at).
통합자가 처리할 수 있게 구조화된 에러를 반환하세요. 예:
{ "error": { "code": "VALIDATION_ERROR", "message": "service_id is required", "fields": { "service_id": "missing" } } }
명확한 HTTP 상태 사용(400 유효성, 401/403 인증/권한, 404 없음, 409 충돌, 429 속도 제한). 이벤트 수집에는 멱등성(Idempotency-Key)을 고려해 재시도가 인시던트를 중복 생성하지 않게 하세요.
토큰별 합리적 속도 제한을 적용하고(수집 엔드포인트는 더 엄격), 입력을 정제하고 타임스탬프/타임존을 검증하세요. 범위가 좁은 API 토큰(읽기 전용 보고 vs 인시던트 쓰기 권한)을 권장하고 누가 어떤 엔드포인트를 호출했는지 로그에 남겨 추적 가능하게 하세요(감사 로그 섹션 참조, /blog/audit-logs).
SLA 수치는 사람들이 신뢰할 때만 유용합니다. SLA 트래킹 앱의 테스트는 “페이지가 로드되는가”보다 “계산이 계약대로 정확히 작동하는가”에 집중해야 합니다. 계산 규칙을 별도의 제품 기능으로 취급해 자체 테스트 스위트를 만드세요.
결정론적 입력(인시던트 오픈, 확인, 완화, 해결 타임라인)과 명확한 SLA 규칙 세트로 계산 엔진을 단위 테스트하세요.
고정 타임스탬프를 사용하고 시간을 고정(freeze)해 테스트가 시스템 시간에 의존하지 않게 하세요. 다음과 같은 엣지 케이스를 커버하세요:
이벤트 수집 → 계산 → 보고 생성 → UI 렌더링의 전체 플로우를 검증하는 소수의 고가치 E2E 테스트를 추가하세요. 이것들은 엔진이 계산한 값과 대시보드가 보여주는 값 사이의 불일치를 잡아냅니다. 시나리오는 적되 의미 있게 유지하고 최종 수치(가용성 %, 위반 여부, 응답 시간 등)를 단언하세요.
영업시간, 공휴일, 시간대에 대한 테스트 픽스처를 만드세요. 예: “인시던트가 현지 금요일 17:55에 발생” 또는 “공휴일로 인해 응답 시간 계산이 멈춤” 같은 반복 가능한 케이스.
배포 이후에도 테스트는 계속됩니다. 작업 실패, 큐/백로그 크기, 재계산 소요 시간, 오류율에 대한 모니터링을 추가하세요. 수집 지연이나 야간 작업 실패 시 코드가 올바르더라도 보고서가 틀릴 수 있습니다.
SLA 트래킹 앱을 출시하는 것은 화려한 인프라보다 예측 가능한 운영에 관한 것입니다: 계산은 제때 실행되어야 하고, 데이터는 안전해야 하며, 보고서는 재현 가능해야 합니다.
초기에는 관리형 서비스를 사용해 정확성에 집중하세요:
환경은 최소화하세요: dev → staging → prod, 각자 DB와 시크릿을 분리.
SLA 추적은 순수한 요청/응답이 아닙니다; 예약 작업에 의존합니다.
작업은 워커 프로세스+큐나 관리형 스케줄러로 실행하세요. 작업은 멱등성을 보장하고 각 실행을 로깅해 감사 가능하게 만드세요.
데이터 유형별 보존 기간을 정의하세요: 파생된 준수 결과는 원시 이벤트보다 오래 보관하세요. 내보내기는 우선 CSV(빠르고 투명)로 제공하고 나중에 PDF 템플릿을 추가하세요. 데이터베이스가 진실의 출처임을 명확히 하세요—내보내기는 표시용이며 형식은 베스트 에포트입니다.
데이터 모델, 수집 흐름, 보고 UI를 빠르게 검증하려면 Koder.ai 같은 바이브 코딩 플랫폼을 고려할 수 있습니다. Koder.ai는 채팅을 통해 완전한 애플리케이션(웹 UI + 백엔드)을 생성하므로 다음을 빠르게 시제품으로 만들어 볼 수 있습니다:
요구사항과 계산이 입증되면(가장 어려운 부분), 소스 코드를 추출해 전통적인 빌드·운영 워크플로로 옮길 수 있습니다. 빠른 반복 중에도 스냅샷과 롤백 같은 기능을 유지하는 것이 좋습니다.
SLA 트래커는 한 가지 질문에 증거를 가지고 답합니다: 특정 고객과 기간에 대해 계약상 약속을 지켰는가?
실무적으로는 모니터링, 티켓, 수동 업데이트 같은 원시 신호를 수집하고, 고객의 규칙(영업시간, 제외 항목 등)을 적용하여 감사 가능한 합격/불합격 결과와 상세 근거를 생성하는 것을 의미합니다.
아래와 같이 구분해 사용하세요:
이들을 별도로 모델링하면 신뢰성 개선을 위한 내부 목표(SLO)를 바꾸더라도 계약상 보고(SLA)를 실수로 변경하지 않을 수 있습니다.
MVP로는 보통 1~3개의 핵심 지표를 엔드투엔드로 추적하는 것이 좋습니다:
이들은 실제 데이터 소스에 매핑하기 쉽고(기간, 달력, 제외 규칙 등) 까다로운 부분을 일찍 구현하게 합니다.
요구사항 오류는 대개 명시되지 않은 규칙에서 옵니다. 설계 전에 다음을 수집하고 문서화하세요:
규칙이 명확히 표현되지 않으면 코드로 추론하지 말고 질문해서 명확히 하세요.
신뢰할 수 있는 SLA 트래커의 최소 데이터 모델은 다음과 같은 명시적 엔터티로 시작하세요:
목표는 추적성입니다: 보고된 수치는 특정 event_id들과 특정 정책 버전으로 연결되어야 합니다.
시간을 정확하고 일관되게 저장하세요:
occurred_at은 UTC(타임존 의미 포함)로 저장received_at도 저장(앱이 이벤트를 수신한 시각)그리고 기간은 명시적 시작/종료 타임스탬프로 저장해 DST 변경 시에도 보고를 재현할 수 있게 합니다.
모든 것을 단일 내부 event 형태로 정규화하고, 안정적인 고유 ID를 부여하세요:
event_id(재시도 시에도 안정적이어야 함)지속시간을 계산할 때는 두 타임스탬프를 무턱대고 빼지 말고, 타임라인에서 합산된 구간(intervals) 으로 계산하세요.
청구 대상 시간(‘chargeable’)에서 제거할 구간을 명확히 정의하세요:
파생된 구간과 이유 코드를 영구 저장해서 무엇이 계산에 포함되었는지 설명할 수 있어야 합니다.
분모(eligible minutes)와 분자(downtime minutes)를 명확히 구분해 추적하세요:
그런 다음 다음 공식을 적용하세요:
availability_percent = 100 * (eligible_minutes - downtime_minutes) / eligible_minutes
또한 eligible minutes가 0일 때의 처리를 미리 정하고 문서화하세요(예: 처리).
UI가 한눈에 “지금 SLA를 충족 중인가, 그 이유는 무엇인가”를 답하도록 만드세요:
알림은 행동 지향적이어야 합니다: 접근 중인 위반, 위반 발생, 반복 위반 등과 관련 페이지(/customers/{id}, /services/{id} 등)로 연결하세요.
sourceevent_typeoccurred_atservice_idincident_id, attributesevent_id에 고유 제약을 걸어 중복 삽입을 방지하세요. 매핑되지 않거나 순서가 어긋나는 이벤트는 검역(quarantine)하거나 플래그를 세워 자동으로 수리하지 마세요.
N/A