본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 16. 06:22

AI 에이전트에게 앱 쓰기 권한 부여하기: RobinReach의 MCP 도구를 위해 구축한 가드레일 (Guardrails)

요약

AI 에이전트에게 앱 쓰기 권한을 부여할 때 발생할 수 있는 보안 문제를 해결하기 위해, 프롬프트 기반의 지침 대신 API/인증 계층에서 브랜드 격리를 구현하는 방법을 설명합니다. LLM의 환각이나 실수에 의존하지 않고 서버 측에서 권한 범위를 검증하여 데이터 보안을 보장하는 가드레일 구축 전략을 다룹니다.

핵심 포인트

  • 프롬프트 기반 가드레일은 LLM의 환각이나 문맥 혼동에 취약함
  • 보안을 위해 API 및 인증 계층에서 데이터 접근 범위를 지정해야 함
  • 에이전트가 권한 없는 데이터에 접근하는 것 자체를 기술적으로 차단
  • MCP 도구 활용 시 브랜드 격리를 위한 서버 측 검증의 중요성

몇 달 전 저는 Rails에서 프로덕션용 MCP 서버 구축하기, 즉 Claude 및 기타 에이전트들이 호출할 수 있도록 RobinReach의 API를 일련의 MCP 도구로 노출하는 배관 작업에 대해 작성했습니다.

그 포스트는 AI 에이전트를 귀하의 앱에 연결하는 것에 관한 것이었습니다. 이번 포스트는 더 어려운 문제, 즉 연결된 이후 실제로 클라이언트의 Instagram에 게시물을 올리거나, 클라이언트를 대신해 댓글에 답글을 달거나, 일주일 치 콘텐츠를 예약하는 등의 작업을 수행할 때 어떤 일이 발생하는지에 관한 것입니다. 에이전트가 쓰기 권한 (Write Access)을 갖는 순간, "데모에서는 잘 작동해요"라는 말은 더 이상 충분하지 않게 됩니다.

모든 사용자(그리고 우리 고객의 모든 고객)가 결국 던지게 되는 단 하나의 질문은 다음과 같은 형태입니다: "이것이 실수로 건드려서는 안 될 것을 건드릴 수 있나요?" 구체적으로, 여러 클라이언트의 여러 브랜드를 관리하는 플랫폼에서, 브랜드 A를 작업 중인 AI 에이전트가 브랜드 B를 보거나 브랜드 B에 게시물을 올릴 수 있습니까?

대답은 '아니오'이며, 제가 집중하고 싶은 부분은 그 이유입니다. 왜냐하면 이것은 일반적인 "AI에게 하지 말라고 말하는" 방식과는 다른 종류의 가드레일 (Guardrail)이기 때문입니다.

브랜드 격리 (Brand isolation)는 에이전트가 따르는 규칙이 아닙니다. 그것은 에이전트가 넘겨다볼 수 없는 벽입니다.

이를 구축하는 쉬운 방법은 다음과 같습니다: 에이전트에게 전체 계정에 대한 한 세트의 자격 증명 (Credentials)을 부여하고, 사용자가 액세스할 수 있는 모든 브랜드를 나열한 다음, "항상 어떤 브랜드를 작업 중인지 확인하고 잘못된 브랜드에 대해서는 절대 행동하지 마세요"와 같은 지침을 추가하는 것입니다.

그 방식은 기술적으로 작동합니다, 작동하지 않게 될 때까지는 말이죠. LLM (Large Language Models)은 실수를 합니다. 긴 대화 과정에서 문맥 (Context)을 혼동하거나, 세 메시지 전의 ID를 재사용하거나, 때때로 그냥 환각 (Hallucination)을 일으키기도 합니다. 만약 "에이전트가 브랜드 A에 게시함"과 "에이전트가 브랜드 B에 게시함" 사이를 막아주는 유일한 것이 주의하라고 말하는 프롬프트 (Prompt) 안의 문장 하나뿐이라면, 그것은 가드레일이 아닙니다. 그것은 희망 사항일 뿐입니다.

그래서 우리는 다르게 구축했습니다. 에이전트가 통신하는 커넥터 (Connector)는 프롬프트 계층 (Prompt layer)이 아니라 API/인증 계층 (API/auth layer)에서 범위 (Scope)가 지정됩니다. 통합 (Integration)이 설정될 때, 해당 연결을 위해 발급된 자격 증명 (Credentials)은 특정 계정 및 사용자가 실제로 접근 권한을 가진 특정 브랜드 세트에 결합됩니다. 에이전트가 수행하는 모든 도구 호출 (Tool call)은 데이터베이스 행 (Database row)에 닿기 전, 서버에서 해당 범위에 대해 검증됩니다.

이것이 실무에서 의미하는 바는 다음과 같습니다:

  • 에이전트는 자신의 범위에 없는 브랜드의 목록을 나열하거나, 읽거나, 쓸 수 없습니다. "하지 말라고 지시받은 것"이 아니라, "할 수 없는 것"입니다. 다른 브랜드의 데이터 요청이 권한 오류 (Authorization error) 이외의 것을 반환하는 코드 경로 (Code path)는 존재하지 않습니다.
  • 계정에 브랜드가 하나만 있는 경우, brand_id는 에이전트가 고려할 필요조차 없는 요소이며, 범위 (Scope)가 이를 조용히 처리합니다.
  • 계정에 여러 브랜드가 있는 경우, 에이전트는 모든 쓰기 작업 (Write action) 시 명시적으로 brand_id를 선택하여 전달해야 하며, 해당 brand_id는 첫 번째 호출뿐만 아니라 모든 개별 호출마다 커넥터의 허용된 범위와 대조하여 확인됩니다.

이것이 매우 중요한 이유는 보증의 주체를 "AI가 품행을 잘 지킨다"에서 "인프라가 나쁜 결과를 불가능하게 만든다"로 옮기기 때문입니다. 고객에게 말할 때 이 두 문장은 매우 다릅니다. 하나는 행동에 대한 약속이고, 다른 하나는 아키텍처 (Architecture)에 대한 진술입니다. AI 에이전트가 여러 테넌트 (Tenant), 고객 또는 브랜드에 접근할 수 있는 무언가를 구축하고 있다면, 이것이 제가 프롬프트 지침 (Prompt instructions)을 단 한 줄이라도 쓰기 전에 가장 먼저 긋는 경계선입니다.

두 번째 계층: 어디서가 아니라, 에이전트가 하려는 내용 자체를 검증하기

범위 지정 (Scoping)은 "이것이 올바른 브랜드인가"를 해결합니다. 하지만 "이것이 올바른 콘텐츠인가"는 해결하지 못합니다. 그 지점에서 검증 (Validation)이 등장하며, 우리는 이를 엄격하고 별개의 단계로 취급합니다.

validate_postcreate_post 이전에 반드시 수행되어야 하는 필수 호출이며, 에이전트는 이를 절대 건너뛰어서는 안 된다고 지시받습니다. 우리는 검증 (Validation) 단계를 생성 (Create) 단계 자체에 통합하는 것이 더 간단함에도 불구하고, 의도적으로 분리했습니다. 이렇게 단계를 나누면 "일단 실행하고 결과 보기" 방식이 아니라 "초안 작성, 확인, 수정"의 루프 (Loop)를 강제할 수 있습니다.

검증 항목:

  • 플랫폼별 글자 수 제한 (Twitter 280자, Threads 500자, Instagram 2200자 등). 해시태그, 이모지, 줄 바꿈, 축약되지 않은 URL을 모두 포함하여 계산합니다. LLM은 스스로 글자 수를 세는 데 매우 취약하기 때문입니다.
  • 플랫폼별 필수 필드 (예: Pinterest의 제목 또는 Reel의 미디어).
  • 콘텐츠가 전송될 플랫폼에 실제로 적합한 내용인지 여부.

무언가 실패할 경우, 에이전트는 무엇이 잘못되었는지 정확히 설명하는 구조화된 응답 (Structured response)을 받게 되며, 실제 소셜 계정에 접근하기 전에 콘텐츠를 수정할 수 있습니다. 실제로 이 방식은 가장 흔한 실패 사례를 압도적인 차이로 잡아냅니다. 즉, 에이전트가 LinkedIn용으로 훌륭한 게시물을 하나 작성한 뒤, 이를 트윗으로 그대로 사용하는 순진한 실수를 범하는 경우입니다. 이는 글자 수가 너무 길 뿐만 아니라 톤 (Tone) 또한 맞지 않습니다.

기타 가드레일 (Guardrails) 요약

전체적인 그림을 완성하기 위해, 각각 한 문장 정도로 설명할 가치가 있는 몇 가지 사항이 더 있습니다.

기본적으로 초안 작성. 에이전트가 능동적으로 생성하는 모든 것은 예약되거나 게시되지 않고 초안 (Draft) 상태로 저장됩니다. 예약 또는 게시 작업은 사용자가 실제로 해당 결과를 요청할 때만 발생합니다. 이를 통해 에이전트는 "게시한다/안 한다"의 이분법적 선택 대신, "제가 만든 것입니다, 확인해 보세요"라는 안전한 상태를 가질 수 있습니다.

타겟 오디언스를 고려한 예약. 무언가를 예약하기 전에, 에이전트는 스스로 "그럴듯해 보이는" 시간을 선택하는 대신 해당 브랜드와 플랫폼에 대해 오디언스가 실제로 가장 활발하게 활동하는 시간을 가져옵니다. 그대로 내버려 두면, LLM은 해당 브랜드의 팔로워가 온라인 상태이기 때문이 아니라 훈련 데이터에 흔히 등장한다는 이유로 오전 9시나 정오와 같이 의심스러울 정도로 딱 떨어지는 숫자를 선택하는 경향이 있습니다.

피드백을 통해 학습하는 보이스 (Voice). 사용자가 생성된 콘텐츠를 수정하거나 거부할 때마다, 해당 수정 사항은 저장되어 다음번에 자동으로 적용됩니다. 에이전트는 이를 조용히 적용하도록 지시받으므로, 사용자가 매 세션마다 선호도를 다시 설명할 필요 없이 출력물이 마치 브랜드의 목소리처럼 자연스럽게 들리게 됩니다.

댓글은 자동 처리되지 않고 노출됩니다. 에이전트는 댓글을 읽고 답장할 수 있지만, 누가 어떤 게시물에 무엇을 말했는지 항상 사용자에게 보여주며, 응답 초안을 작성하기 전에 불만 사항처럼 보이는 내용은 모두 플래그(flag)를 지정합니다. 실제 고객에게 브랜드로서 답장하는 것은 리스크가 매우 크기 때문에, 반드시 인간이 루프 내에 머물러야(human-in-the-loop) 합니다.

대화에 가공되지 않은 API 정보가 유출되지 않습니다. 도구 이름, JSON, 내부 ID 등 그 어떤 것도 사용자에게 전달되지 않습니다. 모든 것은 내부 식별자가 포함된 페이로드(payload) 대신, "귀하의 Facebook 페이지인 Acme Co에 3개의 새로운 댓글이 있습니다"와 같이 평이한 언어로 번역됩니다. 이는 단순한 미적 요소처럼 보일 수 있지만, 실제로는 신뢰를 위한 가드레일 (guardrail)입니다. 기술적 지식이 없는 사용자가 가공되지 않은 에러나 ID를 보는 순간, 자신이 "소셜 미디어 매니저"와 대화하고 있다는 환상은 깨지며, 도구에 어떤 권한이라도 부여하는 것에 대해 더 조심스러워지게 됩니다.

유사한 것을 구축하고 있다면 고려해야 할 패턴

이 모든 과정에서 관통하는 주제는 동일합니다. 에이전트를 더 똑똑하게 만들려고 애쓰지 마세요. 대신 잘못된 행동을 하는 것이 올바른 행동을 하는 것보다 구조적으로 더 어렵게 만들고, 실수의 비용이 가장 높은 곳에 가장 강력한 경계(boundary)를 설정하세요.

저희의 경우, 이는 인증 계층 (auth layer)에서 강제되는 브랜드 및 테넌트 격리(tenant isolation)를 의미했습니다. 이곳에서 에이전트는 잘못된 것을 요청할 수 있는 기술적 능력조차 갖지 못하며, 콘텐츠 검증은 별도의 필수 단계로 강제되어 실수가 실제 게시되기 전에 포착됩니다. 보이스, 스케줄링, 댓글 처리 등 그 외의 모든 기능은 이 두 가지 토대 위에 구축됩니다.

MCP는 LLM에게 앱의 열쇠를 넘겨주는 것을 매우 쉽게 만듭니다. 흥미로운 엔지니어링 작업은 그 열쇠 중 일부가 모든 문을 열 수 없도록 보장하는 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0