운영 로그를 저장하지 않는 AI 장애 대응 코파일럿(Incident Copilot)을 만들었습니다
요약
운영 로그의 민감한 정보를 보호하기 위해 데이터를 저장하지 않는 AI 장애 대응 코파일럿(Incident Copilot) 개발 사례를 소개합니다. 사용자의 프롬프트와 로그를 서버에 영구 저장하지 않고 브라우저 로컬에만 유지하는 아키텍처를 통해 보안 문제를 해결합니다.
핵심 포인트
- 운영 로그 내 민감 정보 유출 방지를 위한 설계 필요성 강조
- 데이터를 수집하지 않고 처리만 하는 '개인용 연습장' 방식의 아키텍처
- 채팅 히스토리를 브라우저 로컬에 저장하여 서버 보안 부담 최소화
- 장애 요약, 스택 트레이스 설명, 사후 분석 초안 작성 기능 제공
운영 로그를 저장하지 않는 AI 장애 대응 코파일럿(Incident Copilot)을 만들었습니다
모든 엔지니어는 어떤 형태로든 다음과 같은 일을 해본 적이 있습니다:
- 운영 환경(Production)에서 무언가 고장 납니다.
- 로그를 가져옵니다.
- AI 채팅 앱에 로그를 붙여넣습니다.
- 질문합니다: “여기서 무슨 일이 일어나고 있는 건가요?”
- 모델이 유용한 답변을 제공합니다.
- 모두가 이것이 정상적인 일인 척합니다.
이것은 정상이 아닙니다.
미친 짓입니다.
운영 로그는 해롭지 않은 텍스트가 아닙니다. 로그에는 고객 ID, 스택 트레이스 (Stack traces), 인증 오류 (Auth errors), 요청 경로 (Request paths), 내부 서비스 이름, 데이터베이스 필드, 인프라 세부 정보, 피처 플래그 (Feature flags), API 응답, 결제 메타데이터, 그리고 때로는 애초에 로그에 남아서는 안 되었을 비밀 정보(Secrets)가 포함될 수 있습니다.
그럼에도 불구하고 2026년의 가장 빠른 디버깅 워크플로우는 여전히 다음과 같습니다:
민감한 운영 컨텍스트를 채팅창에 붙여넣고 개인정보 보호 정책이 우호적이기를 기도한다.
저는 장애 대응(Incident response)을 데이터 유출 사고로 만들지 않으면서도 AI의 도움을 받고 싶었습니다.
그래서 저는 장애 디버깅을 위한 작은 내부 앱을 만들었는데, 주요 제품 요구 사항은 간단했습니다:
사용자의 프롬프트(Prompts) 저장을 거부하더라도 앱이 유용해야 한다.
앱: 엉망진창인 운영 장애를 위한 장애 대응 코파일럿
이 앱은 기본적으로 디버깅을 위한 AI "워룸(War room)"입니다.
로그, 트레이스 (Traces), 오류, 알림 또는 장애 노트를 붙여넣으면 다음과 같은 작업을 도와줍니다:
- 변경 사항 요약
- 발생 가능한 장애 지점 찾기
- 노이즈가 많은 로그를 테마별로 그룹화
- 스택 트레이스 (Stack traces) 설명
- 롤백(Rollback) 또는 완화(Mitigation) 단계 제안
- 엉망인 Slack 업데이트를 깔끔한 장애 노트로 변환
- 사후 분석(Postmortem) 타임라인 초안 작성
이것을 만드는 명백한 방법은 다음과 같습니다:
사용자 입력 → 백엔드 (Backend) → 데이터베이스 (Database) → LLM 제공업체 → 데이터베이스 (Database) → UI
하지만 그것은 또한 매우 공포스러운 구축 방식이기도 합니다.
왜냐하면 이제 제 앱이 누군가 붙여넣은 모든 운영 장애에 대한 영구적인 아카이브를 소유하게 되기 때문입니다.
이는 제가 관리자 권한 접근, 디버그 로그, 데이터베이스 백업, 지원 도구, 분석, 보존 정책, 삭제 흐름, 그리고 데이터 침해 영향까지 걱정해야 함을 의미합니다.
저는 그것을 원하지 않았습니다.
저는 이 앱이 SaaS 대시보드보다는 개인용 연습장 (scratchpad)에 더 가깝기를 원했습니다.
개인정보 보호 규칙: 로그를 절대 저장하지 마라
이것이 핵심 설계 규칙이 되었습니다:
장애 데이터는 수집되는 것이 아니라 처리되어야 한다.
사용자가 명시적으로 내보내기 (export)를 하지 않는 한, 앱은 지난달에 무슨 일이 일어났는지 기억할 필요가 없습니다.
검색 가능한 백엔드 히스토리 (backend history)도 필요하지 않습니다.
"분석을 위해" 원시 로그 (raw logs)를 저장할 필요도 없습니다.
나중에 어떤 기능을 만들 수도 있다는 이유로 프롬프트 (prompts)를 보관할 필요도 없습니다.
그저 사용자가 현재의 혼란스러운 상황을 논리적으로 파악할 수 있도록 도와주기만 하면 됩니다.
따라서 아키텍처는 다음과 같이 구성되었습니다:
- 채팅 히스토리 (Chat history)는 브라우저에 로컬로 저장됩니다.
- 백엔드는 원시 프롬프트 (raw prompts)를 영구 저장하지 않습니다.
- 백엔드는 원시 모델 응답 (raw model responses)을 영구 저장하지 않습니다.
- 각 요청은 일회용 (disposable)으로 취급됩니다.
- 사용자가 영구적인 기록을 원한다면 사후 분석 보고서 (postmortem)를 내보낼 수 있습니다.
- 가능한 경우 모델 호출 전에 민감한 정보에 대한 정리가 이루어집니다.
- UI는 무엇이 로컬에 있고 무엇이 추론 (inference)을 위해 전송되는지 명확하게 표시합니다.
이것이 완벽한 개인정보 보호는 아닙니다.
하지만 모든 것을 영원히 조용히 저장하는 것보다는 훨씬 더 나은 기본 설정입니다.
Icelake API의 역할
모델 계층을 위해 저는 Icelake AI API를 사용했습니다. 이 API는 제가 원하는 앱의 개인정보 보호 태세에 부합하면서도 OpenAI 호환 인터페이스를 제공했기 때문입니다.
이것이 중요했던 이유는 모델 제공업체와의 통합이 프로젝트 전체가 되는 것을 원하지 않았기 때문입니다.
저는 제품 아키텍처를 단순하게 유지하고 싶었습니다:
const response = await fetch("/api/analyze-incident", {
method: "POST",
headers: {
...
그러면 서버 엔드포인트 (endpoint)는 세 가지 작업을 수행합니다:
- 명백한 민감한 값들을 비식별화 (Redacts) 합니다.
- 최소화된 프롬프트를 모델 API로 전송합니다.
- 원시 요청이나 응답을 저장하지 않고 답변을 반환합니다.
중요한 점은 이것이 기술적으로 화려하다는 것이 아닙니다.
화려하지 않습니다.
중요한 점은 이 앱이 데이터 절제 (data restraint)를 중심으로 설계되었다는 것입니다.
대부분의 AI 앱은 다음과 같은 질문으로 시작합니다:
우리가 무엇을 수집할 수 있는가?
이 앱은 다음과 같은 질문으로 시작합니다:
우리가 무엇을 수집하지 않을 수 있는가?
그 질문 하나가 많은 것을 바꿉니다.
기본 아키텍처 (Basic architecture)
대략적인 흐름은 다음과 같습니다:
Browser
|
| 1. 사용자가 로그를 붙여넣음
...
백엔드(Backend)는 의도적으로 단순하게 설계되었습니다.
incident_messages 테이블도 없습니다.
chat_history 테이블도 없습니다.
"일단 모두 저장하고, 개인정보 보호(Privacy) 문제는 나중에 해결하자"라는 방식도 없습니다.
그저 요청(Request)이 들어오면, 요청(Request)을 내보낼 뿐입니다.
비식별화 (Redaction)는 유용하지만, 개인정보 보호 전략은 아닙니다
많은 개발자가 비식별화(Redaction)를 마법의 방패처럼 취급합니다.
하지만 그렇지 않습니다.
비식별화는 도움이 되지만, 모든 것을 잡아낼 수는 없습니다.
명백한 것들은 제거할 수 있습니다:
function redact(input: string) {
return input
.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "[EMAIL]")
...
이것이 아무것도 안 하는 것보다는 낫습니다.
하지만 충분하지 않습니다.
운영 데이터(Production data)는 무질서합니다. 민감한 값들이 항상 비밀 정보처럼 보이지는 않습니다. 내부 서비스 이름이 민감할 수 있습니다. 고객 ID(Customer ID)가 민감할 수 있습니다. 데이터베이스 필드(Database fields)가 민감할 수 있습니다. 심지어 스택 트레이스(Stack trace)조차 의도보다 더 많은 정보를 드러낼 수 있습니다.
따라서 진정한 개인정보 보호(Privacy)의 승리는 비식별화에 있지 않습니다.
진정한 개인정보 보호의 승리는 요청이 완료된 후 앱이 보유하는 데이터의 양을 줄이는 데 있습니다.
비식별화는 추론(Inference) 과정 중의 리스크를 낮춥니다.
로그를 저장하지 않는 것은 리스크를 영구적으로 낮춥니다.
분석(Analytics)에 메시지 내용이 필요하다는 핑계도 그만두었습니다
이 지점이 많은 AI 앱들이 조용히 잘못된 길로 빠지는 부분입니다.
그들은 이렇게 말합니다:
제품 분석(Product analytics)을 위해 로그가 필요합니다.
아니요, 아마 필요하지 않을 것입니다.
이 앱에서 제가 신경 쓰는 이벤트(Events)는 다음과 같습니다:
- 사용자가 분석을 실행함
- 사용자가 엄격한 비식별화(Strict redaction)를 사용함
- 사용자가 완화 계획(Mitigation plan)을 복사함
- 사용자가 사후 분석 보고서(Postmortem)를 내보냄
- 모델 응답(Model response)이 실패함
- 요청(Request) 시간이 너무 오래 걸림
저는 실제 운영 로그(Production logs)가 필요하지 않습니다.
정확한 프롬프트(Prompt)도 필요하지 않습니다.
모델의 전체 답변도 필요하지 않습니다.
제품 분석(Product analytics)은 제품이 어떻게 사용되는지를 알려주어야지, 작업의 개인적인 내용을 캡처해서는 안 됩니다.
따라서 이벤트 페이로드(Event payload)는 다음과 같은 형태에 가깝습니다:
track("incident_analysis_completed", {
redactionMode: "strict",
inputLengthBucket: "10k-50k",
...
이런 식이 아니라 말입니다:
track("incident_analysis_completed", {
rawLogs: logs,
prompt: fullPrompt,
...
두 번째 버전이 더 쉽습니다.
또한 무모하기도 합니다.
로컬 우선(Local-first) 히스토리가 UX를 개선했습니다
저는 로컬 우선(Local-first) 히스토리가 타협안이 될 것이라고 예상했습니다.
결과적으로 그것은 제품의 사용감을 더 좋게 만들었습니다.
사용자들은 멘탈 모델(Mental model)을 즉시 이해했습니다:
이것은 당신의 임시 장애 작업 공간(Incident workspace)입니다. 당신이 내보내기(Export)를 하지 않는 한, 이것은 당신의 기기에 머뭅니다.
그것이 사람들이 제품을 사용하는 방식을 바꾸었습니다.
사용자들은 지저분한 로그를 붙여넣는 데 더 주저함이 없었습니다. 자신의 생각을 소리 내어 말하듯 적는 데 더 주저함이 없었습니다. 정제된 데모 상황에서만 사용하는 대신, 실제 장애(Incident) 상황에서 제품을 사용하는 데 더 주저함이 없었습니다.
개인정보 보호(Privacy)는 단순한 컴플라이언스(Compliance) 기능이 아니었습니다.
그것은 제품을 개선했습니다.
사용자가 자신의 미완성된 디버깅(Debugging) 생각이 제 백엔드(Backend)에 영구적으로 기록되고 있다는 느낌을 받지 않았기 때문입니다.
까다로운 부분들
로컬 우선(Local-first) 방식은 공짜가 아닙니다.
몇 가지 사항들이 더 어려워졌습니다:
- 기기 간 동기화 (Cross-device sync)
- 공유 장애 룸 (Shared incident rooms)
- 장기 검색 (Long-term search)
- 지원 디버깅 (Support debugging)
- 브라우저 저장소 삭제 후 복구 (Recovery after clearing browser storage)
- 대규모 장애 발생 시 협업 (Collaboration during larger incidents)
하지만 이러한 기능들은 사용자의 명시적인 의도가 필요해야 합니다.
만약 누군가 공유 장애 룸을 만들고 싶어 한다면, 좋습니다. 그 룸을 저장하세요.
만약 누군가 사후 분석(Postmortem) 결과물을 내보내고 싶어 한다면, 좋습니다. 그 사후 분석을 저장하세요.
만 만약 누군가 클라우드 동기화(Cloud sync)를 원한다면, 좋습니다. 선택 사항(Opt-in)으로 만드세요.
하지만 이러한 예외적인 사례들을 핑계 삼아 모든 개인적인 디버깅 세션을 기본값(Default)으로 저장해서는 안 됩니다.
기본값이 중요합니다.
제가 계속해서 되새기는 원칙
AI는 마법처럼 느껴지는 제품을 만드는 것을 믿을 수 없을 정도로 쉽게 만듭니다.
또한 끔찍할 정도로 많은 양의 민감한 컨텍스트(Context)를 수집하는 제품을 만드는 것도 믿을 수 없을 정도로 쉽게 만듭니다.
위험한 부분은 그 데이터가 처음에는 무섭게 보이지 않는다는 점입니다.
그저 텍스트처럼 보일 뿐입니다.
하지만 그 텍스트는 운영 중인 서비스의 장애(Production outage), 보안 문제(Security issue), 고객 에스컬레이션(Customer escalation), 법적 문제(Legal concern), 비공개 사업 계획(Private business plan), 또는 누군가가 당신의 관리자 패널(Admin panel)에 남아 있기를 절대 원치 않을 개인적인 질문일 수도 있습니다.
그래서 저의 규칙은 이제 단순합니다:
만약 사용자가 당신의 데이터베이스에 프롬프트(prompt)가 남아 있는 것을 불편해한다면, 아마 그것은 당신의 데이터베이스에 저장되어서는 안 되는 것일지도 모릅니다.
당연한 소리처럼 들립니다.
하지만 대부분의 AI 앱들은 서비스 첫날부터 이 규칙을 위반합니다.
더 큰 교훈
다음 세대의 AI 앱들은 단순히 모델의 품질(model quality)로만 경쟁해서는 안 됩니다.
그들은 절제(restraint)로 경쟁해야 합니다.
단순히 다음과 같은 질문이 아니라 말이죠:
- 모델이 얼마나 똑똑한가?
- 응답 속도가 얼마나 빠른가?
- UX(사용자 경험)가 얼마나 좋은가?
또한 다음과 같은 질문도 포함되어야 합니다:
- 당신은 무엇을 저장하기를 거부하는가?
- 당신은 무엇을 로그(log)로 남기기를 거부하는가?
- 당신 스스로가 접근하는 것을 불가능하게 만드는 것은 무엇인가?
- 사용자가 무엇을 제어하는가?
- 세션(session)이 종료될 때 무엇이 사라지는가?
장애 대응 코파일럿(incident copilot)에게 이것은 추상적인 프라이버시 이상론이 아닙니다.
이것은 유용한 엔지니어링 도구와 운영 데이터의 부채(liability) 사이의 차이입니다.
AI 앱들은 우리의 가장 민감한 워크플로우(workflow) 바로 위에 자리 잡게 될 것입니다.
디버깅(Debugging), 법률(Legal), 금융(Finance), 의료(Health), 채용(Hiring), 전략(Strategy), 개인적 글쓰기(Personal writing), 보안(Security)까지 말이죠.
만약 우리가 "모든 것을 저장하라"는 기본 SaaS 본능을 가지고 이 모든 것을 만든다면, 우리는 업무 그 자체 위에 감시 계층(surveillance layer)을 구축하게 될 것입니다.
저는 그것을 원하지 않습니다.
저는 모든 것을 기억하지 않도록 설계되었기 때문에 바로 유용한 AI 도구를 원합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기