AI가 우리 백엔드를 부수지는 않았어요. 단지 우리에게 거짓말을 멈췄을 뿐이에요.

발행: (2026년 4월 24일 AM 10:31 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

배경

우리 내부 AI 에이전트는 이전에 UI를 클릭하면서 10분이 걸리던 작업을 한 번의 프롬프트로 완료할 수 있습니다. 사용자는 자연어로 아웃바운드 작업을 설명하고, 에이전트가 MCP와 통신하여 UI를 전혀 건드리지 않고 작업을 생성합니다. 작업은 다음과 같은 라이프사이클을 가집니다:

  • Draft(초안) 은 정지 상태이며 편집이 가능합니다.
  • Published(발행된) 작업은 실행됩니다.

초안의 핵심은 아무 일도 일어나지 않는다는 점입니다.

버그 바시

우리는 적대적인 마인드셋으로 버그 바시를 진행했습니다: 프롬프트 인젝션, 역할 탈취, 기능 탐색, “깨뜨리기”. 팀 외부보다 더 잘 가장자리를 찾아 한계점을 이해하고 싶었고, 그 과정에서 누락된 부분을 식별하고 제품의 실제 상태를 가늠하고자 했습니다.

한 시간 동안 아무 일도 없자 질문을 바꿨습니다. 깨뜨리려는 대신 단순히 작업을 수행하도록 요청했습니다:

create a job with these settings

발행 단계는 없고, 단지 생성만 요청했습니다.

작업은 draft 상태로 생성되었습니다. 계속해서 살펴보니 로그에 해당 작업에 대해 태스크가 실행됐다는 라인이 나타났습니다. 초안에서는 태스크가 실행되지 않아야 합니다; 바로 그게 상태의 의미이기 때문이죠. 처음엔 잡음이라고 생각했지만, 그 작업에서 온 실제 메일이 내 인박스에 도착했습니다—실제 수신자에게 실제로 전송된 것이었습니다. “이상한 로그”가 “프로덕션에서 실시간으로 동작”하게 된 겁니다.

조사

고우선순위 티켓이 나에게 할당되었습니다. LLM 주변에서 무언가 이상한 일이 발생하면 많은 사람들이 처음 떠올리는 생각은 AI가 하지 말아야 할 일을 했다는 것이었습니다. UI 버전은 오랜 기간 고객에게 제공돼 왔으며 사고가 없었습니다. 유일한 새로운 변수는 모델이었으므로 다음을 조사했습니다:

  • RAG 설정 오류
  • MCP 경계
  • 프롬프트 인젝션
  • 역할 및 권한
  • 도구 캡슐화

모두 깨끗해 보였습니다. 에이전트가 건드린 어떤 것도 전달을 트리거하는 큐에 접근할 수 없어야 했습니다.

한 시간 정도가 지나면서 가설이 바닥났습니다. 질문을 바꿨습니다: AI가 UI와 다르게 하는 일은 무엇인가? 두 페이로드를 비교했습니다. 한 가지 핵심 차이가 드러났습니다:

{
  "dispatch": { "primary": true, "secondary": true }
}

UI는 초안에서는 절대 이 필드를 보내지 않았습니다. 에이전트는 스키마를 충실히 읽었기 때문에 보냈습니다. 하위 시스템에서는 이 필드를 감시하던 우선순위 큐가 작업을 ‘지연된’ 것으로 판단하고 즉시 실행시켰습니다—초안 상태에서도 말이죠.

근본 원인

큐 핸들러에 초안 상태를 확인하는 방어 로직이 없었습니다. 해결 방법은 한 줄이었습니다:

// queue handler guard
if (status === 'draft') return;

이 방어 로직은 처음부터 존재했어야 했습니다. 버그는 잠자고 있었고, UI가 아닌 다른 클라이언트가 스키마를 충실히 따르는 create 엔드포인트를 호출하기만 하면 발동될 상황이었습니다. 에이전트가 최초로 이를 수행한 것이었습니다.

교훈

  • 에이전트가 버그를 만든 것이 아니라, UI의 암묵적 규칙 대신 스키마를 그대로 따른 최초의 클라이언트였을 뿐입니다.
  • 프론트엔드가 사용하는 모든 API는 백엔드가 강제하지 않는 관습에 의해 얽혀 있습니다.
  • 상태 방어는 호출자의 규율이 아니라 핸들러에 있어야 합니다.
  • 프론트엔드가 “조용히” 있을 것이라는 믿음은 계약이 아니라 내기입니다.

백엔드에 아직도 그런 내기를 하고 있는 엔드포인트가 얼마나 있을까요?

0 조회
Back to Blog

관련 글

더 보기 »