라틴 아메리카 최대 은행에서 기술 부채(Technical Debt)를 감시하기 위해 AI 에이전트를 도입한 날
요약
라틴 아메리카 최대 은행에서 기술 부채를 관리하기 위해 MCP(Model Context Protocol) 기반의 AI 에이전트를 도입한 사례를 소개합니다. 개발자가 문서를 찾는 대신, 코드 에디터 내에서 실시간으로 정책 준수 여부를 확인할 수 있는 환경을 구축한 과정을 다룹니다.
핵심 포인트
- MCP를 활용해 AI 모델에 데이터 조회 및 API 호출 등 실질적인 도구 부여
- 정책 준수와 실제 개발 작업 사이의 간극을 에이전트로 해결
- VSCode 내 Claude를 활용한 실시간 인프라 및 정책 가이드 제공
- 대규모 개발 조직의 배포 신뢰성 및 보안 확보 방안
저는 평온한 날에도 배포(deploy)만으로 하루에 5,000건 이상이 올라오는 환경에서 일합니다. 여기에 수천 명의 개발자와 수십 개의 스쿼드(squads)를 곱해보면, 그 어떤 내부 정책도 문서로만 존재해서는 살아남을 수 없는 규모라는 것을 알 수 있습니다. 각 배포가 회복 탄력성(resilience), 보안(security), 신뢰성(reliability)을 갖추고 비용(cost)이 통제된 상태로 배포되는지 보장할 수 있는 인간 리뷰어, 위원회 또는 수동 프로세스는 존재하지 않습니다.
문제는 정책이 부족해서가 아닙니다. 우리는 좋은 문서화(documentation)와 잘 설계된 가드레일(guardrails), 프로덕션(production) 환경에서 무엇이 수용 가능하고 불가능한지에 대한 명확한 기준을 가지고 있었습니다(지금도 가지고 있습니다). 문제는 스프린트(sprint) 도중에 아무도 문서를 읽지 않는다는 것입니다. 개발자는 Terraform을 열고, 어제까지 리소스를 올려야 하는 상황에 직면합니다. 이때 정책이 아무리 훌륭하더라도, 그것은 내부 문서의 어딘가, 어떤 PDF, 혹은 그가 뒤져볼 시간이 없는 Teams 채널 어딘가에 머물러 있습니다.
"정책이 존재한다"와 "정책이 준수된다" 사이의 이 간극이 저를 다르게 생각하게 만들었습니다. 만약 개발자에게 규칙을 찾아가라고 요청하는 대신, 규칙이 개발자가 이미 작업하고 있는 바로 그 자리인 코드 에디터(code editor)로, 결정이 내려지는 바로 그 순간에 찾아오게 한다면 어떨까?
그 생각에서 이 프로젝트가 탄생했고, 현재 수천 명의 개발자가 일상에서 사용하고 있습니다. 이것은 화이트보드에 한 번에 그려낸 완벽한 아키텍처(architecture) 계획이 아니었습니다. 모든 것이 의미를 갖게 될 때까지, 그리고 개발자가 인프라(infra)에 대한 걱정을 덜고 애플리케이션의 비즈니스 로직(business logic)이라는 진정으로 중요한 것에 집중할 수 있게 될 때까지 문제를 하나씩 해결해 나간 과정이었습니다. 어떻게 구현되었는지, 그리고 그 과정의 각 구성 요소를 설명해 드리겠습니다. 이 글을 읽는 모든 사람이 하루 종일 AWS를 다루며 사는 것은 아니라는 점을 잘 알고 있기 때문입니다.
먼저, MCP란 도대체 무엇인가
아키텍처(architecture)에 들어가기에 앞서, 응용 AI(applied AI) 분야에 익숙하지 않은 분들을 위해 짧게 부연 설명을 하겠습니다.
MCP는 Model Context Protocol을 의미합니다. 실제로 이는 Copilot 내에서 실행되는 Claude와 같은 AI 모델에게 "손과 눈"을 달아주는 표준화된 방식입니다. 모델 자체만으로는 학습 과정에서 배운 내용만을 알 수 있습니다. 하지만 MCP가 연결되면 문서를 검색하거나, 데이터베이스를 조회하거나, API를 호출하는 등 실제로 무언가를 수행할 수 있게 됩니다.
이렇게 생각해보세요. 모델은 두뇌이고, 각 MCP는 그 두뇌의 손에 쥐여주는 도구입니다. 나사를 풀기 위한 드라이버나 전압을 측정하기 위한 멀티미터 같은 것이죠. 모델은 사용자의 요청에 따라 어떤 도구를 사용할지 결정합니다.
제 경우에는 Claude(팀 개발자 개개인의 VSCode 내 Copilot에서 실행 중)에게 세 가지 도구를 부여했습니다.
이유를 생략하지 않은 아키텍처 (architecture)
VSCode에서 질문이 시작되어 답변에 이르기까지의 경로를 그려보겠습니다:
VSCode (Claude를 사용하는 Copilot)
│
▼
...
"개발자가 질문을 입력한 것"과 "코드가 실행된 것" 사이에 이렇게 많은 단계가 있는 것이 과해 보일 수 있다는 점은 저도 알고 있습니다. 하지만 각 계층은 대기업이 직면한 실제 문제들을 해결합니다:
- Route 53는 아무도 IP를 외우고 싶어 하지 않기 때문에, 그리고 아무에게도 알리지 않고 도메인 뒤에 있는 것을 교체할 수 있기 때문에 존재합니다.
- Network Firewall은 오직 있어야 할 트래픽만이 그곳에 있도록 보장합니다. 누군가를 들여보내기 전에 입주자 명단을 확인하는 건물의 경비실을 생각하면 됩니다.
- API Gateway는 실제로 호출을 받는 곳이며, 통과시키기 전에 두 가지를 확인합니다: Bearer Token (해당 사용자가 인증되었음을 증명하며, 회사의 Active Directory를 통해 검증됨)과 API Key (해당 애플리케이션 — 이 경우에는 Copilot 확장 프로그램 — 이 이 엔드포인트(endpoint)를 사용할 권한이 있음을 증명함)입니다. 이는 "당신이 누구인지 안다"와 "당신이 사용하는 앱이 출입증을 가지고 있다는 것을 안다"의 차이입니다.
- ALB 앞단의 NLB는 네트워크 뒷단의 더 세부적인 요소입니다: NLB는 매우 가볍고 안정적으로 연결을 유지하며(내부 네트워크 간의 경계를 넘나들기에 좋습니다), 그 후 요청 내용을 이해하고 어떤 Lambda로 보낼지 결정하는 ALB로 제어권을 넘깁니다.
- 마지막으로 Lambda는 코드가 실제로 실행되는 곳입니다. 24시간 켜져 있는 고정된 서버 없이, 누군가 호출할 때만 깨어납니다.
약어가 아주 많다는 점은 알고 있습니다. 하지만 이 블록에서 무언가 하나를 제거한다면, 이 전체 체인은 두 가지를 동시에 보장하기 위해 존재합니다. 즉, 허가된 사람만이 응답에 도달할 수 있도록 하는 것과, 보안 계층이 가득한 기업 네트워크 구조 내부에서 오더라도 응답이 빠르게 전달되도록 하는 것입니다.
모델에게 준 세 가지 도구
도구 1 — 내부 정책의 사서
이것은 제가 설명하기 가장 좋아하는 도구인데, 매우 인간적인 문제를 해결하기 때문입니다: 지식은 존재하지만, 아무도 찾아보지 않는 어딘가에 길을 잃고 있는 상태 말입니다.
저는 우리의 기술 부채 가드레일 (guardrails) 정책에 대한 기술 명세서—무엇이 부채로 간주되는지, 각 유형이 어떻게 분류되는지, 무엇이 허용되고 무엇이 허용되지 않는지—가 저장되어 있는 일종의 "지능형 도서관"인 **지식 베이스 (Knowledge Base)**로 들어가는 관문 역할을 하는 자체 API를 구축했습니다.
이 도서관이 문서에 적힌 내용을 (단순히 순수 텍스트로만 저장하는 것이 아니라) "이해"할 수 있도록, 각 문서는 **임베딩 (embedding)**이라고 불리는 과정을 거칩니다. 이는 텍스트를 단순히 정확한 단어가 아닌, 해당 구절의 _의미_를 나타내는 숫자로 변환하는 방식입니다. 저는 이 변환을 위해 Bedrock을 통한 AWS의 임베딩 모델을 사용했으며, OpenSearch는 키워드뿐만 아니라 의미적 유사성을 통해 검색할 수 있는 방식으로 이 숫자들을 저장합니다.
실제 사례를 들면: Copilot이 "암호화되지 않은 버킷을 노출하는 것에 대한 우리 정책은 무엇인가?"를 알아야 할 때, 모델은 (우리의 내부 문서를 본 적이 없는) 모델의 일반적인 지식에 물어보는 것이 아닙니다. 대신 정보의 원천으로 직접 가서 정확한 구절을 찾아낸 뒤, 그것을 바탕으로 답변합니다. 이것은 기술적으로 **RAG (Retrieval-Augmented Generation, 검색 증강 생성)**라고 불리지만, 개념은 간단합니다. 모델이 알고 있는 것으로 "추측"하는 대신, 먼저 올바른 답변을 찾은 다음 자신의 언어로 설명하는 것입니다.
도구 2 — 스쿼드(squad)의 엑스레이
이것은 더 직접적입니다. 모든 스쿼드는 기술 부채를 축적하며, 이는 정상입니다. 문제는 그 양이 얼마인지, 그리고 얼마나 심각한지 모른다는 점입니다.
저는 스쿼드별로 열려 있는 기술 부채의 양과 각 부채의 심각도를 담은 DynamoDB (정보를 "카드" 형식으로 저장하여 조회가 빠른 데이터베이스) 테이블을 만들었습니다. 두 번째 MCP는 단순히 이 테이블을 조회합니다.
사소해 보일 수 있지만, 이는 게임의 판도를 바꿉니다. 이제 모델이 특정 스쿼드를 위해 무언가를 분석할 때, 해당 팀이 이미 심각한 부채에 빠져 있는지 아니면 상대적으로 여유로운 상태인지 알 수 있으며, 이는 권장 사항의 어조와 우선순위를 변화시킵니다.
도구 3 — 리뷰 가이드라인 (roteiro da revisão)
세 번째 도구는 어떤 데이터도 찾지 않습니다. 대신 모델에게 VSCode에 열려 있는 Terraform 코드를 정확히 어떻게 살펴봐야 하는지, 그리고 무엇을 체크해야 하는지를 알려주는 잘 짜인 지침 세트인 **프롬프트 (prompt)**를 반환합니다.
이것은 마치 제가 예전에 작성했던 "훌륭한 리뷰어의 매뉴얼"과 같으며, 이제는 누군가 리뷰를 요청할 때마다 재사용됩니다. 모델은 이 가이드라인을 읽고, 이를 따라 분석을 완료하기 위해 나머지 두 가지 도구를 호출해야 할지 스스로 결정합니다.
세 가지 도구의 결합 방식
프로그래밍을 하는 사람의 관점에서는 이 과정이 전혀 드러나지 않습니다. 사용자는 그저 대부분의 작업에 Claude Sonnet 4.6을 사용하도록 설정된 Copilot이 포함된 VSCode를 열고, "이 모듈 좀 리뷰해줘"와 같은 요청을 할 뿐입니다.
백그라운드에서는 대략 다음과 같은 일이 일어납니다:
- 모델이 분석을 어떻게 진행할지 알기 위해 도구 3(가이드라인)을 가져옵니다.
- 가이드라인에 따라, 코드에서 보고 있는 내용에 대해 정책이 실제로 무엇이라고 말하는지 확인하기 위해 도구 1을 호출합니다.
- 논리적으로 타당하다면, 해당 스쿼드(squad)가 이미 부채 한계치에 도달했는지 확인하기 위해 도구 2를 호출합니다. 이는 권장 사항의 긴급도를 변화시킬 수 있습니다.
- 개발자에게 "AI의 추측"이 아닌, 회사의 실제 문서까지 추적 가능한 수정 제안을 반환합니다.
이러한 추적 가능성(traceability)이 모든 것을 바꿉니다. 모델이 "제 생각에는 이것이 좋은 관행이 아닌 것 같습니다"라고 말하는 것이 아니라, "정책 X의 Y 섹션에 따르면 이것은 허용되지 않으며, 귀하의 스쿼드는 이미 12개의 심각한 부채를 보유하고 있으므로 지금 해결해야 할 때일 수 있습니다"라고 말하는 것입니다.
이를 구축하며 겪었던 고충
모든 과정이 순탄했던 것은 아닙니다. 힘들었던 부분도 이야기하는 것이 중요하다고 생각합니다:
- 네트워크 체인은 지연 시간(Latency)이라는 대가를 치릅니다. Lambda가 깨어나기까지 다섯 번의 홉(Hop)을 거쳐야 하며, 때로는 모델이 응답을 구성하기 위해 두세 개의 도구(Tool)를 순차적으로 호출하기도 합니다. 이 때문에 타임아웃(Timeout) 문제를 매우 주의 깊게 모니터링해야 했습니다.
- 콜드 스타트(Cold start)가 예상보다 더 번거로웠습니다. 호출되지 않은 채 오랜 시간이 지난 Lambda는 "깨어나는" 데 시간이 다소 걸립니다. 해결책은 생각보다 간단했습니다. ALB(Application Load Balancer)는 타겟의 상태를 확인하기 위해 주기적으로 헬스 체크(Health check)를 수행하는데, 이 메커니즘을 활용했습니다. 프로비저닝된 동시성(Provisioned Concurrency) 비용을 지불할 필요 없이, Lambda가 항상 "따뜻한(Warm)" 상태를 유지할 수 있도록 헬스 체크 빈도를 설정했습니다.
- 라이브러리는 누군가 관리할 때만 유용합니다. 업데이트되지 않은 문서로 구성된 RAG(검색 증강 생성)는 RAG가 아예 없는 것보다 더 나쁩니다. 현실과 맞지 않는 내용을 매우 확신에 차서 답변하기 때문입니다. 그래서 팀원 중 누군가가 주기적으로 소스 문서를 검토하는 것이 일과가 되었습니다.
- 보안은 정말 손이 많이 가는 작업입니다. 토큰과 API 키를 가지고 있는 것만으로는 충분하지 않습니다. 키 로테이션(Key rotation) 루틴을 도입해야 했고, 특히 팀원이 스쿼드(Squad)를 옮기거나 퇴사할 때 액세스 권한을 가진 사람이 누구인지 검토하는 과정이 필요했습니다.
응답이 도저히 담기지 않을 때
사용량이 본격적으로 늘어나기 시작했을 때 나타난 문제가 하나 있는데, 튜토리얼에서는 아무도 알려주지 않는 종류의 문제라 이야기할 가치가 있습니다.
스쿼드의 기술 부채(Technical Debt) 상태를 조회하는 MCP가 실패하기 시작했습니다. 항상 그런 것은 아니었고, 쿼리 결과가 너무 많이 돌아올 때만 발생했습니다. 원인을 조사해 보니 어렴풋이 알고는 있었지만 직접 겪어본 적은 없던 두 가지 기술적 제한 사항에 부딪혔습니다. Lambda는 동기식 응답에 대해 6MB의 상한선이 있고, 그 앞단에 있는 Application Load Balancer(ALB)는 1MB에서 연결을 끊어버립니다. 즉, Lambda가 오류 없이 거대한 응답을 구성할 수 있다 하더라도, ALB가 사용자에게 도달하기 전 중간에 응답을 떨어뜨려 버리는 것입니다.
정확히 그런 일이 벌어지고 있었습니다. 기술 부채(Technical Debt)가 많이 쌓인 스쿼드(squads)가 너무 큰 응답을 생성하면, 요청이 체인 중간에서 끊겨버렸고, 개발자는 원인을 이해하지 못한 채 일반적인 오류 메시지만 보게 되었습니다.
해결책은 두 가지 변화를 포함했습니다. 하나는 데이터베이스에서의 변화이고, 다른 하나는 MCP API에서의 변화입니다.
데이터베이스에서는 GSI를 사용했습니다. 데이터베이스 분야가 생소한 분들을 위해 설명하자면, DynamoDB에서는 보통 각 테이블이 하나의 방식, 즉 주로 기본 키(Primary Key)를 통한 조회에 최적화되어 있습니다. 만약 다른 기준(예: "ID를 통한 특정 부채" 대신 "심각도 순으로 정렬된 특정 스쿼드의 모든 부채")으로 동일한 데이터를 검색해야 한다면, 기본 테이블에서 직접 조회하는 것은 느려지거나 효율적인 방식으로는 아예 불가능할 수 있습니다. **GSI (Global Secondary Index, 글로벌 보조 인덱스)**는 실질적으로 동일한 데이터를 다른 검색 패턴에 최적화하여 인덱싱하는 두 번째 방법입니다. 마치 도서관의 동일한 장서에 대해 저자별로 정리된 카드와 주제별로 정리된 카드, 두 종류의 도서 카드를 가지고 있는 것과 같습니다. 저는 테이블 전체를 스캔할 필요 없이, 특정 스쿼드의 부채를 심각도 순으로 빠르게 가져올 수 있도록 구성된 GSI를 생성했습니다.
MCP API에는 제한(limit)이 있는 페이지네이션 (Pagination)을 도입했습니다. 모든 데이터를 한 번에 가져오는 대신, 이제 MCP는 기본적으로 요약된 버전, 즉 ALB의 상한선보다 훨씬 낮은 안전한 크기 제한 내의 첫 번째 결과들을 반환합니다. 만약 사용자(또는 더 많은 컨텍스트가 필요함을 인지한 모델 자체)가 더 많은 내용을 보고 싶어 한다면, 다음 페이지를 요청하는 방식으로 진행됩니다. 이는 여러분이 인지하지 못한 채 이미 사용해 온, 검색 결과가 수천 개의 항목을 한 번에 반환하는 대신 "페이지당 10개"씩 나오는 모든 페이지네이션 API와 동일한 원리입니다.
이로 인한 예상치 못한 긍정적인 부작용이 있었습니다. 오류를 해결했을 뿐만 아니라, 요약된 응답이 더 빨라졌고 모델이 처리하기에도 더 쉬워졌습니다. 대부분의 경우, 초기 요약만으로도 개발자가 스쿼드(squad)의 상황을 이해하기에 충분하며, 실제로 더 깊이 파고들어야 할 때만 페이지네이션 (pagination)이 작동합니다. 때로는 기술적 한계가 결과적으로 원래 아이디어보다 더 나은 솔루션으로 당신을 이끌기도 합니다.
마치며
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기