이번 포스팅은 운영 중인 웹 서비스에서 처음 겪은 보안 사고의 기록입니다. 공격은 분산 스크래핑 봇넷이었습니다.
사람과 멀티 AI 에이전트가 함께 공격을 감지하고, 진행 중인 공격의 패턴을 실시간으로 분석하고, 서비스가 받는 영향을 추적했습니다. 그리고 분석으로 확정한 방어 방법 하나를 단 한 번 적용해 공격을 무력화했습니다.
마지막 장에서는 이 경험을 바탕으로 AI 에이전트를 통한 시스템 운영의 방향을 이야기해 보려 합니다.
1. 사건 요약
| 항목 | 값 | 출처 |
| 기간 | 2026.08.19 20:00 - 2026.08.20 10:00 | 자체 옵저버빌리티 스택 |
| Cloudflare 수신 | 약 197만건(평시 대비 50배), 방문자 3.6만 | Cloudflare 대시보드 및 자체 옵저버빌리티 스택 |
| 오리진 도달 | 14시간동안 약 77만건(직전 평시 24시간 대비 약 13배) | 자체 옵저버빌리티 스택 |
| 전송량 | 오리진 TX 21.6GB / RX 14.1GB | 자체 옵저버빌리티 스택 |
| 피크 요청 | 131 req/s (평시의 약 260배) | 자체 옵저버빌리티 스택 |
| 서비스 영향 | p95 열화 약 18분, 서비스 마비 약 23분(조치 직전 수치) | 자체 옵저버빌리티 스택 |
| 조치 | 엣지 클라우드 WAF 설정 - 적용 6분 내 공격 97% 방어, 품질 3분내 완전 회복 | Cloudflare 대시보드 및 자체 옵저버빌리티 스택 |
| 피해 | 데이터 손실 0, 실사용자 오검문 0, 방어 비용 0, Umami 지표 오염 | 자체 옵저버빌리티 스택 |
2. 사건 전개
2.1 배경
바이브 코딩을 통해 만든 작은 웹 서비스를 하나 운영하고 있습니다. CNCF에 등재된 Graduated/Incubating 등급의 프로젝트의 새로운 릴리스 노트를 수집하여 분석해 주는 서비스입니다. 대중적인 주제는 아니어서 개설한 지 반년이 넘어가고 있는 시점이지만 사용자가 그렇게 많지는 않습니다.
단, 최근에 Amazon Bot이나 GPT나 Google Gemini 같은 AI 크롤러들이 제 사이트를 발견하고 정보를 얻어가고 있어서 사람 사용자보다는 봇 사용자들이 조금씩 늘고있는 추세였습니다. 이에 조금 더 잘 발견되게 하기 위해 DNS for AI Discovery(DNS-AID) 설정을 하였습니다. 그리고 얼마 지나지 않아 Umami 대시보드에 방문자가 급증하는 것을 포착하게 됩니다.
2.2 인지
그런데 이게 곧 정상적이지 않은 요청이라는 것을 알게 되었습니다. 미국, 브라질, 프랑스, 아르헨티나, 싱가폴, 파키스탄, 인도 등등... 세계 각지의 수십 개국으로부터 동일한 경로로 요청이 들어오고 있었습니다. 처음에는 앞서 설정한 DNS-AID가 작동하면서 나타난 현상이 아닌가 했는데 모든 요청들의 Visit duration이 1초인 것이 봇이라 하기에도 너무 짧았습니다.

2.3 관찰 및 분석
공격이 시작된 시간은 2026년 8월 19일 20시부터 약 4시간 정도는 동시 접속 사용자가 약 20명 내외로 집계 되었으며 그 요청량도 그렇게 크지 않았습니다. 공격을 인지한 시점에 AI 에이전트를 통해 평소보다 많은 사용자 수에 대한 분석을 요청했습니다.
현재 Ratatosk에 비정상적으로 보이는 요청이 증가하고 있다.
모두 /release 페이지를 겨냥하고 있고 Visit duration도 모두 0초로 일반적인 패턴이 아니다.
공격으로 의심되는데 접속 로그를 분석해서 보고해 달라.
AI 에이전트는 이에 지금 이 요청이 비정상적인 것은 맞다고 판단해 주었습니다. 그 근거로 동일 경로에 대한 동시 다발적인 접근, 그리고 비정상 적인 사이트 상주 시간을 근거로 들었습니다. 다만, 요청이 동시에 들어왔다가 다소 소강상태를 보이고 또 들어오는 파도와 같은 형식이고, 3~6 req/s 정도의 요청은 시스템에 부하를 일으키지 않는 수준이고 대부분은 웹 요청 처리 P95에도 이상이 없어 지켜보자는 입장이었습니다.

저 역시도 이 판단에 동의해습니다. 기본적인 서버의 리소스 지표인 CPU나 메모리 사용률도 평소와 같았고 웹 서비스 품질도 문제가 없다 판단했기 때문입니다. 그렇게 약 5시간 정도를 지켜보다가 자정을 넘겨서 잠이 들었습니다. 물론 잠을 자지 않는 AI 에이전트에게 감시를 맡겨둔 상태로 말입니다.
하지만, 아침에 일어나보니 밤새 이 미적지근한 공격이 계속 이어지고 있었습니다. 공교롭게도 잠을 청하고 얼마 지나지 않아 트래픽양이 두배로 늘어났고 또 얼마 지나지 않아 두배로 늘어나 있는 상태였습니다. 감시를 하고 있었던 AI 에이전트에게 보고를 요청했고 그 답변은 잠들기 전과는 심각도가 달라져 있었습니다. 밤 사이 계속된 공격으로 리소스 사용량은 확실히 올라가 있었으며 웹 요청 처리 P95도 영향을 받고 있었습니다. 10~21 req/s의 요청이 지속되면서 미리 설정해 둔 웹 서버의 초당 요청량 임계값도 넘어서고 있어 메신저를 통해 알람도 오고 있었습니다. 공격이 멈출 기세가 없었기 때문에 빠르게 AI 에이전트를 통해 분석을 요청하였습니다. AI 에이전트는 수많은 로그를 신속하게 검색하여 패턴을 찾아내고 아래와 같이 보고했습니다.
| 특징 | 측정 |
| 소스 | 주거용 프록시망 - 수십 개국에서 고유 /24 대역 26,859개, IP 26,859개, IP당 1 ~ 4 요청 후 교체, Rate Limit 발화 우회 |
| 클리어언트 | 헤드리스 Chromium 팜 - JS 실행으로 Umami 방문자로 집게, Next.js RSC 요청 형식 정확 |
| 위장 패턴 | UA=Chrome/118, Firefox 120/121(2023년식) vs Sec-Ch-Ua=Chromium 146(최신) - 사람이 사용하는 브라우저에서는 있을 수 없는 불일치 |
| 세션 구조 | 문서 로드 1회 → RSC 탐색 평균 27건 내외 → 폐기, 트래픽 86%가 RSC fetch |
| 표적 | 특정 경로 필터 조합 전수 열거. 상세 페이지 및 AI 에이전트용 앤드포인트에는 미접촉 |
분석이 있고 얼마 후 프록시 메모리가 임계치를 초과했다는 알람이 디스코드로 왔고 급하게 메모리 한도를 올려서 프록시가 OOM Killed 되는 것을 막았습니다. 바로 옵저버빌리티를 담당하는 에이전트에게 분석 내용을 파일로 전달하였고, 이를 기반으로 본격적인 메트릭과 로그 분석을 시작하였습니다. 옵저버빌리티 쪽 에이전트 역시 동일한 결론에 도달하였으며 Cloudflare의 보안 규칙을 통해 이를 해결할 수 있다고 하며 방법을 제시하였습니다.
2.4 대응과 조치
그러는 와중에도 상황은 더욱 악화일로를 걸었습니다. 사건 경과 약 13시간 만인 2026년 8월 20일 오전 9시경 요청이 38 req/s에서 57 req/s로 점프합니다. 바로 이때 P95가 92ms에서 970ms로 올라가면서 순간 영향을 받았다가 일시 소강상태를 겪습니다. 이후 재급증하면서 P95가 2.2초로 급격히 나빠졌고 9시 30분이 조금 못된 시점에 서비스 품질이 최악으로 치닫기 시작합니다.

웹 요청이 100 req/s를 넘어가면서 P95가 8.9초까지 올라갔으며 P99는 116초, 5xx 응답은 10분간 약 3600건이 발생하게 됩니다. 웹의 메트릭 앤드포인트가 무응답으로 프로브가 실패하여 옵저버빌리티 측에서 타겟 다운 크리티컬 알람이 울리기 시작합니다. 웹서버의 CPU 코어를 응급 증설 하였으나 부분적으로 효과가 있었을 뿐 즉시 재포화가 되어 효과가 없었습니다. 요청수는 140req/s를 넘어가고 있었으며 Umami의 방문자 통계는 이전 시간대와는 비교가 안될 정도로 무섭게 치솟고 있었습니다. 아마 이때 Umami에 집계된 동시 접속자 수가 300명이 넘었던 것으로 기억합니다.
이는 도저히 하나의 인스턴스로 버틸 수 없다고 판단, 앞서 옵저버빌리티 측 AI 에이전트가 추천한 Cloudflare의 보안 규칙을 사용하기로 합니다. Managed Challenge를 공격 대상이 되는 경로에 설정하였습니다. 단, 앞서 이야기한 정상적인 AI 크롤링 봇(GPT, Amazon 봇 등)은 예외로 허용하였습니다. 규칙을 적용하자마자 약 4.3만 개의 요청이 이 규칙에 의해 무력하 되었으며 3분 만에 서비스 품질 완전 회복, 6분 내 공격 전면 차단 및 안정 상태에 돌입할 수 있었습니다. 이로서 약 14시간 동안 이어진 공격을 완전히 차단할 수 있게 되었습니다.
3. 사건 타임라인 및 분석과 교훈
3.1 사건 타임라인
| 시각(KST) | 사건 |
| 20:00 ~ 20:45 | 공격 시작 - 한번 몰아치고 소강 상태를 보이는 요청(순간 최대 18 req/s) 으로 정찰 및 캘리브레이션 추정 |
| 20:45 ~ 04: 55 | 파도 단계(약 8시간): 3~6 req/s로 몰려왔다 빠지기 반복, 시간당 400 ~ 800회 방문 |
| 04:55 ~ 09:05 | 지속 단계(약 4시간): 10~21 req/s로 쉼 없이 요청 발생 → 08:15 부터는 24~45 req/s로 계단 증가, 웹 서비스 품질은 정상 구간, RequestRate 경고 알람 발생 |
| (07:36 ~ 07:44) | 프록시 컨테이너 메모리 임계치 초과 발생 → 메모리 증설 응급 처방으로 프록시 컨테이너 OOM Killed 방지 |
| 09:06 ~ 09:17 | 웹 서비스 품질 저하 시작 - 38 → 57 req/s로 점프하면서 바로 그때 P95 92ms → 970ms로 급증 |
| 09:17 ~ 09:19 | 일시 소강 - P95 즉시 97ms로 회복, 큐잉 특성 |
| 09:19 ~ 09:21 | 재급증으로 인한 P95 응답 2.2초 |
| 09:21 ~ 09:23 | 서비스 붕괴 - 108 req/s, P95 8.9초, P99 116초, 5xx 10분간 3,667건 발생 |
| 09:23 ~ 09:27 | 웹 메트릭 앤드포인트 무응답, 옵저버빌리티 측에서 타겟 다운 경보 발생 |
| 09:27 ~ 09:47 | 웹 컨테이너 CPU 증설하였으나 부분 효과, 즉시 재포화 → Cloudflare Managed Challenge 적용 |
| 09:47 ~ 09:53 | 요청 수 131 → 4 req/s로 97% 감소, P95 50ms로 안정화, 3분이내 품질 완전 회복, 6분 내 안정권 진입 |
3.2 분석과 교훈
이번 공격을 통해 의외의 수확을 하나 할 수 있었는데, 벤치마크 테스트가 되었다는 겁니다. 웹 서비스가 올라간 인프라가 어디까지 버틸 수 있는지 이번에 확실하게 알게 되었습니다. 한계선은 요청 50 req/s 이하이며 그 이상이 되면 P95 품질 저하, 110 req/s 부근이면 서비스 붕괴 수준이라는 점입니다.
또한 각종 지표들의 거동도 확인할 수 있었습니다. cpu 사용량이나 메모리 사용량은 요청에 따라 비교적 선형적으로 증가하지만 P95는 특정 임계를 넘는 순간 급격히 나빠지는 것도 확인할 수 있었습니다. 이는 큐잉의 특성으로 대기열이 처리열을 넘어가는 순간 폭주하는 큐잉으로 인해 급격히 서비스 품질의 저하가 발생하게 됩니다. 따라서, P95가 여유가 있다 하더라도 이것을 '지금은 괜찮다'라고 판단하면 안 된다는 교훈을 얻을 수 있었습니다.
그리고 수직 확장은 이런 류의 공격에는 아무런 효과가 없다는 것 역시도 웹 서버의 CPU 증설을 통해 확인할 수 있었습니다. 이번의 경우에는 공격이라 차단을 통해 막을 수 있었으나 향후 대규모 서비스를 운영할 때에는 수직 확장이 아닌 탄력적인 수평 확장과 부하분산이 중요할 것이라는 너무나도 보편적인 진리(?)를 몸으로 느낄 수 있었습니다.
4. AI 에이전트의 활약
4.1 특화된 에이전트
이번 공격을 알아차리고 즉시 AI 에이전트와 대처할 수 있었던 이유는 바로 각 기능마다 특하된 에이전트가 있어서 가능했습니다. 거창하게 AI 에이전트를 개발한 것은 아닙니다. 다만 웹 서비스와 옵저버빌리티를 구현하면서 느낀 점은, 개발과 운영 과정에서의 여러 결정 사항들을 컨텍스트 한계가 있는 AI 에이전트가 언제든지 그 맥락을 이해하고 궤도에 오를 수 있도록 하는 것이 중요하다 것이었습니다. 그래서 프로젝트 초기에 문서를 쓰는 규칙을 설정하고 그것이 자산이 될 수 있게 계속 쌓아왔습니다.
물론 문서를 쌓기만 해서는 안됩니다. 모델의 성능이 떨어졌다는 느낌을 받을 때면 대부분 문서에 쌓인 결정이나 지시사항이 서로 모순을 일으키거나 충돌하는 경우가 대다수라 이런 부분을 한 번씩 정리하는 시간이 필요했습니다. 이를 통해 문서는 고도화하고, 메모리는 중요한 것들만 남김으로써 AI 에이전트의 성능을 원하는 상태로 유지하면서 각 서비스에 특화시킬 수 있었습니다.
4.2 에이전트 간의 협업
사람만 협업하는 것은 아닙니다. AI 에이전트 간에도 협업이 가능합니다. 저는 어느 순간엔가 두 서비스를 담당하는 에이전트끼리 서로를 인지하고 대화가 필요하다는 것을 느끼기 시작했습니다. 특히 옵저버빌리티 스택을 구성할 때가 그랬습니다. 웹에서 나오는 기본적인 자원 메트릭은 매우 표면적인 상태만을 알 수 있습니다. 각 컨테이너에서 나오는 그들만의 메트릭과 애플리케이션 자체에서도 메트릭을 생산할 수 있다면 이를 통해 좀 더 세밀한 관측이 가능할 것이라 생각했습니다.
하지만, 두 에이전트는 서로를 모르고 개별적으로 작업을 진행하고 있었기 때문에 이를 연결할 필요가 있었습니다. 그래서 처음에는 사람이 두 에이전트 간의 매개자 역할을 하였습니다. 프롬프트를 입력하고 나온 출력을 다른 에이전트에게 전달해주고, 그 응답을 다시 되돌려 주는 방식으로 작업을 진행했는데 한계가 있었습니다. 그래서 생각한 것이 문서를 서로 교환하게 하면 어떨까 였습니다. 문서는 그 내용이 남으니 사람도 그것을 추적할 수 있고, 에이전트도 문서가 많아져도 검색을 통해 쉽게 맥락을 다시 끌어올 수 있을 것이라 생각했습니다.
이 방식은 생각보다 효과가 좋았습니다. 그래서 두 에이전트가 서로를 인지하고 있던 상황에서 이번과 같은 사건이 생겼고, 서로의 특성을 알던 에이전트들이 매우 면밀하게 협업하여 문제 상황을 진단하고 합의된 해결책을 도출해 낼 수 있었습니다.
4.3 빅테크들의 움직임
이미 이런 요구를 알고 있던 빅테크들은 이에 대해 더욱 세련된 방식을 제시하고 있습니다. 2025년에 발표된 Google의 A2A 프로토콜이 그것이며, 앤트로픽에서 최근에 발표한 Cross-session messaging도 하나의 방식입니다. 하지만 이것은 제가 사용하고 있는 파일 기반의 에이전트간 통신과는 조금 다릅니다.
우선 A2A의 경우 운영자 경계를 넘는 에이전트간 통신을 그 범위로 하고 있습니다. A2A는 서로 모르는 에이전트끼리의 발견과 인증, 위임을 전제로 하고 있으며 제가 운영하는 범위에서는 이런 요소가 없습니다. Cross-session messaging은 같은 호스트 내 Unix 소켓을 통한 세션 간의 통신으로 메시지를 기반으로 하여 휘발성을 가지고 있습니다. 저의 경우에는 같은 호스트에서 돌아가는 서로 다른 세션은 아니라 이것을 사용할 수 없는 상태입니다.
어쨌든, 큰 틀에서는 서로 다른 AI 에이전트들이 어떻게 서로를 알아차리고 협업해 나갈 수 있는지에 대해 고민하며 나름대로 이를 구현해가고 있다고 보입니다.
이번 봇넷 공격 사건을 통해 운영적인 측면에서의 교훈도 얻을 수 있었지만, 그간 쌓아온 AI 에이전트 사용 방식이 문제 해결에 빛을 발하기도 하였습니다. 특히 마지막 부분에 저의 독특한 운영 방식과, 이를 발전시킬 수 있는 방향이 있는지도 한번 더 고민해 봐야겠습니다.
'Platform Engineering > System & Service' 카테고리의 다른 글
| [Linux] 리눅스 커널로 본 Containerd: 프로세스 트리와 Cgroup의 이중주 - 1편 (1) | 2025.11.30 |
|---|---|
| OPNsense로 방화벽 구축하기 [3편] (4) | 2025.03.08 |
| OPNsense로 방화벽 구축하기 [2편] (0) | 2025.02.25 |
| OPNsense로 방화벽 구축하기 [1편] (0) | 2025.02.15 |
| Linux 프로세스 관리 - 좀비 프로세스에 관하여 [3편] (1) | 2025.01.28 |




























































