경계를 나누면 비용이 든다: 아키텍처 트레이드오프와 LLM 도구 호출
11 views
# 모든 경계는 비용을 청구한다 — 아키텍처 트레이드오프와 LLM 도구 호출의 딜레마
아키텍처에 은탄환은 없다. Mark Richards가 *Software Architecture Patterns*에서 지적했듯, 특정 비즈니스 문제에 들어맞는 단일한 최고의 패턴 같은 건 존재하지 않는다. 이건 패배의 인정이 아니라 설계의 출발점이다. 시스템을 설계한다는 건 무엇을 고정하고 무엇을 흐르게 둘지, 즉 불변성(Invariant)과 가변성(Variability)의 경계를 긋는 일이다. 그리고 그 경계에는 늘 대가가 따른다. 확장성을 얻으면 단순함을 잃고, 일관성을 지키려면 가용성을 양보해야 한다. 트레이드오프는 설계의 부작용이 아니라 설계 그 자체다.
이 글은 두 개의 시간대를 가로지른다. 하나는 분산 시스템이 수십 년에 걸쳐 다듬어 온 고전적 패턴들의 상충 관계, 다른 하나는 오늘날 같은 문제가 가장 첨예하게 재연되는 LLM 에이전트의 외부 도구 호출(Tool Calling) 파이프라인이다. 미리 말해두면, 인공지능 애플리케이션이 마주한 딜레마는 새로운 것이 아니다. 분산 시스템의 오래된 교훈이 새로운 문법으로 돌아온 것뿐이다.
## 8대 패턴이 그리는 상충의 지형도
패턴을 평가하려면 먼저 평가의 축이 있어야 한다. ISO/IEC 25010이 정의하는 품질 속성 — 확장성, 성능, 유지보수성, 테스트성, 배포성, 비용, 복잡성, 팀 자율성, 결함 허용도, 보안 — 이 그 좌표계다. 패턴의 우열은 절대적이지 않다. 어떤 축에서 탁월한 패턴은 다른 축에서 대가를 치른다. 다차원 비교란 결국 이 비대칭을 직시하는 작업이다.
**모놀리식과 계층화** 아키텍처는 단일 트랜잭션 경계와 ACID 보장이라는 단순함을 준다. 하나의 배포 단위 안에서 정합성이 자연스럽게 유지된다. 다만 이 통합성 때문에 부분적 스케일링이 불가능해지고, 작은 변경에도 전체를 다시 배포해야 하는 병목이 생긴다.
**마이크로서비스(MSA)** 는 그 병목을 독립 배포와 독립 확장으로 푼다. 대가는 가볍지 않다. 네트워크가 시스템의 일부가 되는 순간 이른바 '마이크로서비스 프리미엄'이 청구된다. 단일 프로세스 안에서 메서드 호출 한 번이면 끝나던 일이, 분산 트랜잭션을 제어하기 위한 Saga나 2단계 커밋(2PC) 같은 별도의 설계 부담으로 바뀐다.
**이벤트 기반(EDA)** 아키텍처는 시간적·공간적 디커플링으로 대용량 트래픽을 흡수한다. 생산자와 소비자가 서로를 알 필요가 없다. 그러나 흐름이 비동기로 흩어지면, 하나의 요청이 시스템을 어떻게 통과했는지 따라가는 분산 추적(Distributed Tracing)과 디버깅이 급격히 어려워진다.
나머지 패턴도 사정은 비슷하다. **서버리스**는 NoOps와 사용량 과금이라는 극대화된 탄력성을 주지만 Cold Start 지연과 실행 시간 제약(대표적으로 15분 한계)을 부과한다. **마이크로커널**은 플러그인 단위의 동적 확장과 결함 격리를 제공하나 코어의 수평 확장에는 물리적 한계가 있다. **공간 기반(SBA)** 아키텍처는 인-메모리 데이터 그리드(IMDG)로 마이크로초 단위 응답성을 끌어내지만 메모리 비용 폭증과 최종 일관성(Eventual Consistency)의 정합성 비용을 감수해야 한다. **피어 투 피어(P2P)** 는 단일 장애점(SPOF) 없는 검열 저항성을 얻는 대신 네트워크 홉 오버헤드와, 일관성을 보장하기 위한 극단적인 난이도를 떠안는다.
여덟 패턴이 모두 같은 구조다. 우월한 패턴이 아니라 적합한 패턴이 있을 뿐이고, 적합성이란 어떤 품질 속성에 가중치를 둘 것인가의 문제다. 그 가중치는 비즈니스가 정한다.
## LLM 도구 호출 — RPC의 귀환과 Retry의 덫
여기까지가 고전이다. 그런데 LLM 에이전트가 외부 도구를 호출하는 구조를 들여다보면 익숙한 풍경과 다시 마주친다. LLM이 추론으로 만들어낸 인수(Argument)로 외부 API나 DB 도구를 호출하는 행위는 근본적으로 원격 프로시저 호출(RPC)의 한 형태다. 네트워크 너머의 컴포넌트에 요청을 보내고 응답을 기다린다는 점에서, 에이전트 시스템은 태생부터 분산 시스템이다.
다만 한 가지가 결정적으로 다르다. 호출의 발신자가 비결정론적이라는 점이다. 전통적 RPC에서 인수는 컴파일러와 타입 시스템이 보증한다. 그러나 LLM은 인수를 추론으로 '생성'한다. 그 결과 스키마 미준수, 누락된 파라미터, 인코딩 불일치 같은 규칙 파괴형 에러가 나온다. 도구가 반환하는 400 에러는 네트워크 장애가 아니라 발신자의 추론이 규칙을 어겼다는 신호다. 까다로움은 전부 여기서 나온다.
가장 손쉬운 대응은 단순 재시도(Naïve Retry Loop)다. 에러가 나면 다시 호출한다. 그런데 이 소박한 루프는 분산 시스템이 이미 안티패턴으로 분류해 둔 것을 그대로 재현한다.
첫째, **비용 폭증**이다. 전통적 RPC 재시도는 패킷 몇 개를 다시 보내는 일이지만, LLM 재시도는 컨텍스트 윈도우 전체를 다시 토큰화해 입출력 단가를 다시 지불하는 일이다. 규모가 커지면 운영 비용(Cost at scale)은 선형을 넘어선다.
둘째, **타임아웃 캐스케이드(Timeout Cascade)** 다. 동기식 루프 안에서 재시도가 반복되면 하위 호출의 지연이 누적되고, 결국 상위 오케스트레이터의 타임아웃까지 건드린다. 도구 하나가 실패를 반복하는 동안 시스템 전체가 멈춰 서는, 분산 시스템에서 익히 경계해 온 연쇄 마비다.
셋째, **결합도 상승**이다. 재시도를 위해 LLM 프롬프트가 물리 API 스키마에 점점 더 밀착되면 도구가 독립적으로 진화하지 못한다. 스키마를 바꾸는 순간 프롬프트가 깨지는 구조는 진화 가능성(Evolutionary Path)을 봉쇄한다.
## 엄격한 검증과 관대한 수용 사이
문제의 핵심은 오래된 원칙 하나로 수렴한다. 포스텔의 법칙(Robustness Principle), "보낼 때는 엄격하게, 받을 때는 너그럽게"다. LLM의 출력을 어디까지 엄격하게 단속하고 어디까지 너그럽게 받아들일 것인가 — 이 선택이 두 개의 대립하는 설계 철학을 낳는다.
### 엄격한 검증(Strict Validation)
Pydantic 스키마 파서 같은 도구로 LLM의 출력을 엄정하게 평가하고, 미세한 오류조차 허용하지 않으면서 즉시 에러 피드백 루프를 가동하는 모델이다. 강점은 분명하다. 데이터 무결성이 보장되고, SQL Injection이나 Path Traversal 같은 악의적 파라미터가 경계에서 원천 차단된다. 보안만 놓고 보면 이보다 견고한 방어선은 없다.
대가도 분명하다. 사소한 포맷 변동에도 복원력이 깨진다. 따옴표 하나, 후행 쉼표 하나가 전체 호출을 무산시킨다. 그때마다 자가 수정(Self-Correction) 호출이 발생하고, 그 호출이 또 토큰과 레이턴시를 청구한다. 무결성을 지키려는 엄격함이 거꾸로 성능을 갉아먹는 셈이다.
### 관대한 수용(Tolerant Reader)
JSON 괄호 누락, 비필수 필드의 부재, 데이터 타입 불일치 같은 것을 파서나 미들웨어 단에서 휴리스틱하게 자동 보정(Auto-Correction)해 호출을 성사시키는 모델이다. 강점은 태스크 성공률이 올라간다는 점이다. 불필요한 피드백 루프를 막아 비용을 줄이고 레이턴시를 단축한다. 사용자 경험은 매끄럽다.
그러나 너그러움은 경계를 흐린다. 자동 보정된 인수가 유효해 보이지만 실제로는 의도와 다를 때, 예상치 못한 사이드 이펙트(Side Effect)가 발생한다. 무엇보다 보안 경계가 약해진다. 엄격한 검증이라면 막았을 악의적 입력이 관대한 파서의 호의 속에서 통과해 버릴 수 있다.
두 모델 중 어느 한쪽이 옳은 게 아니다. 무결성과 보안을 우선하면 엄격함으로, 성공률과 비용을 우선하면 너그러움으로 기운다. 앞서 본 8대 패턴의 상충 관계와 정확히 같은 구조다. 축이 달라졌을 뿐, 비대칭적 트레이드오프라는 본질은 그대로다.
그래서 진짜 설계 결정은 둘 중 하나를 고르는 게 아니라 자가 수정 루프를 어떻게 제어하느냐에 달려 있다. 루프 횟수의 한계점(Threshold)을 정하지 않은 자가 수정은 그 자체로 타임아웃 캐스케이드의 원천이다. 한 발 더 나아가, 도구 호출 파이프라인을 동기식 인-프로세스(In-process)로 둘 것인지, 아니면 비동기 메시지 기반 워크플로우(Saga나 이벤트 브로커)로 격리할 것인지가 구조의 운명을 가른다. 동기식은 단순하지만 장애를 전파하고, 비동기식은 복잡하지만 장애를 봉쇄한다. 여기서도 경계는 어김없이 비용을 청구한다.
## 진화적 아키텍처, 그리고 가역적 결정
정답은 순수한 한쪽이 아니라 도메인에 맞춘 하이브리드다. 보안이 최우선인 시스템이라면 엄격한 검증을 근간으로 삼되 비동기 격리 큐를 두어 타임아웃 전파를 차단한다. 사용자 경험이 최우선인 시스템이라면 관대한 수용으로 레이턴시를 최소화하고, 보정은 백그라운드에서 비동기로 돌린다. 어느 쪽이든 단일 패턴에 대한 순혈주의가 아니라 경계를 어디에 그을지에 대한 의식적인 선택이다.
이때 기준이 되는 게 결정의 가역성이다. 제프 베조스의 분류를 빌리면, 되돌릴 수 없는 Type 1 결정과 되돌릴 수 있는 Type 2 결정은 다르게 다뤄야 한다. 파싱 엔진과 유효성 검증 정책은 본질적으로 Type 2에 가깝다. 스키마는 변하고 검증 규칙은 진화한다. 그렇다면 이 요소들을 코어 시스템에 단단히 박아 넣을 게 아니라, 마이크로커널의 플러그인처럼 분리해 교체 가능하게 둬야 한다. 검증 정책을 바꾸는 일이 전체 재배포를 요구하지 않도록 만드는 것, 그게 가역성에 기반한 설계다.
8대 패턴의 상충, LLM 도구 호출의 400 에러, 엄격함과 너그러움의 대립은 따로 떨어진 사건이 아니라 하나의 원리가 드러난 세 장면이다. 좋은 아키텍처는 완벽한 패턴을 구현한 결과물이 아니라, 변화에 맞춰 경계를 다시 그을 수 있는 시스템이다.
완성도는 어느 한 시점의 최적해에 있지 않다. 비즈니스가 성장하고 인프라가 흔들릴 때 그어 둔 경계를 다시 그을 수 있는 복원력(Resilience), 거기에 있다. 모든 경계는 비용을 청구하지만, 그 경계를 다시 그을 자유를 남겨 둔 설계만이 시간을 견딘다.