AI 기능이 제품 경로로 들어오면서 gateway의 역할도 단순한 API base URL 통일을 넘어섰습니다. 모델 provider 선택, streaming 변환, tool 호출, MCP 연결, PII redaction, 비용 할당, audit, fallback이 한 요청에 겹칩니다. 최근 공개된 플러그인 중심 AI gateway 사례와 개발자 논의는 “더 많은 모델을 한 endpoint에 붙이자”보다, 기능을 core에 계속 누적하지 않고 독립된 확장으로 관리하려는 흐름을 보여 줍니다. 다만 하나의 프로젝트 출시가 업계 표준을 증명하는 것은 아닙니다. 여기서 주목할 점은 AI gateway를 운영하는 팀들이 반복해서 마주하는 확장성 대 안전성의 구조적 문제입니다.
plugin architecture는 새 provider나 정책을 빨리 붙이는 장점이 있지만, 잘못 설계하면 schema를 모르는 plugin이 field를 지우고, audit plugin의 timeout이 모든 응답을 늦추고, redaction 우회가 log에 원문을 남기며, plugin 순서 변경이 권한 결정을 바꿉니다. Inference Router의 품질·비용 Gate가 어떤 모델을 고를지 다뤘다면, LLM Gateway의 Prompt Firewall·PII DLP는 경계에서 무엇을 막을지 다뤘습니다. 이 글은 그 정책들을 어떤 확장 계약과 격리 조건에서 안전하게 실행할지에 초점을 둡니다.
이 글에서 얻는 것
- AI gateway의 plugin을 provider adapter, protocol 변환, policy, 관측 네 종류로 나누는 기준을 얻습니다.
- 공통 schema가 provider 고유 기능을 지우지 않도록 capability preservation 계약을 설계합니다.
- plugin마다 권한·hook phase·latency budget·실패 정책을 명시하는 방법을 배웁니다.
- 새 extension을 production에 연결하기 전에 fixture, shadow traffic, kill switch로 검증하는 순서를 정리합니다.
핵심 개념/이슈
1) “모든 것을 plugin으로”는 설계가 아니라 경계 설정의 시작이다
AI gateway에 붙는 코드는 모두 같은 위험을 갖지 않습니다. provider adapter는 인증과 wire protocol을 이해해야 하고, protocol translator는 streaming event와 tool call state를 보존해야 합니다. policy plugin은 tenant quota·model allowlist·data classification을 판단하며, observability plugin은 비용·latency·실패 사유를 기록합니다. 이들을 같은 lifecycle과 권한으로 취급하면 작은 metrics extension도 upstream credential 또는 원문 prompt에 접근할 수 있습니다.
먼저 plugin을 기능명이 아니라 request에 미치는 영향으로 분류하세요.
| 종류 | 예시 | 요청 경로 영향 | 기본 실패 정책 |
|---|---|---|---|
| adapter | OpenAI-compatible ↔ provider wire format | 요청·응답의 의미와 capability 변경 | fail-closed |
| protocol | streaming framing, tool-call state, MCP session | event 순서와 cancellation 변경 | fail-closed |
| policy | allowlist, quota, redaction, tenant routing | 허용·거부 또는 payload 수정 | 위험도별 명시 |
| observer | metric, trace, audit mirror | 원칙적으로 결과를 변경하지 않음 | bounded fail-open |
예를 들어 비용 accounting이 잠시 실패했다고 정상 고객의 답변을 모두 막을 이유는 보통 없습니다. 반면 PII redaction이 실패했는데 원문을 외부 provider에 보낼 수 있다면 fail-open은 허용할 수 없습니다. “plugin 오류면 계속 진행” 같은 전역 기본값은 이 차이를 지웁니다. Tool Permission Manifest와 Runtime Attestation처럼, extension마다 읽을 수 있는 data class와 수행 가능한 action을 좁혀야 합니다.
2) 공통 schema는 번역기가 아니라 정보 보존 계약이다
gateway가 여러 provider를 하나의 request model로 묶는 이유는 client 코드와 운영 정책을 단순하게 만들기 위해서입니다. 그러나 공통 schema에 없는 field를 조용히 버리면 안전한 추상화가 아닙니다. reasoning 설정, JSON schema constraint, tool-call parallelism, stream usage event, cache control처럼 provider마다 지원 범위가 다른 기능은 결과 품질·비용·보안에 영향을 줍니다.
그래서 routing 전에 capability matrix를 계산하고, 선택한 provider가 요청의 필수 capability를 만족하는지 판정해야 합니다. 예를 들어 structured_output=strict, tool_parallelism=required, stream_usage=required 중 하나라도 제품 계약에 필수라면, 지원하지 않는 provider로 fallback하지 말고 명시적으로 거부하거나 사용자에게 degraded mode를 고르게 해야 합니다. 반대로 optional field라면 dropped_fields, downgrade_reason, provider_capability_version을 response metadata와 audit event에 남깁니다.
client intent
-> required / optional capability 분류
-> candidate provider capability 비교
-> preserve | explicit downgrade | reject
-> adapter + stream execution
-> receipt에 실제 provider·capability·변환 결과 기록
이 계약은 모델 변경 시 특히 중요합니다. Schema-Constrained Output과 Runtime Validator가 보여 주듯 schema를 요청에 넣었다고 결과가 자동으로 안전해지지 않습니다. gateway는 provider가 이 기능을 native로 보장하는지, emulation했는지, 아예 지원하지 않는지 구분해야 하며, validator 결과를 routing·retry 판단과 연결해야 합니다.
3) hook 순서와 stream ownership은 API 표면이다
plugin을 beforeRequest, afterResponse 정도의 이름으로 연결하면 처음에는 충분해 보입니다. 하지만 redaction 다음에 cache key를 계산할지, tenant quota를 fallback 전과 후 중 어디서 차감할지, audit이 tool result 원문을 볼 수 있는지에 따라 보안·비용·디버깅 결과가 달라집니다. 등록 순서를 configuration 파일의 우연한 배열로 두면 upgrade 때 순서가 바뀌는 것만으로도 정책이 깨질 수 있습니다.
실무에서는 최소한 다음 phase를 stable API로 둡니다: authenticate, classify, authorize, select-provider, transform-request, execute-stream, validate-result, record-receipt. 각 plugin은 허용된 phase에서만 실행되고, 같은 phase 안에서도 dependency와 conflict를 manifest에 선언합니다. redaction은 transform-request 전에 policy가 요구하는 원문 분류를 볼 수 있어야 할 수 있지만, observer가 그 원문을 다시 외부 sink로 내보내면 안 됩니다. 따라서 어떤 data representation을 소유하고 다음 단계에 넘기는가도 명시합니다.
stream은 더 까다롭습니다. 한 plugin이 chunk를 buffer하면서 provider cancel 신호를 놓치거나, tool call event를 일반 text로 바꾸면 client는 중복 실행 또는 과금 오류를 겪습니다. stream-transform plugin에는 최대 buffer 크기와 time budget을 둡니다. 예를 들어 interactive path에서 추가 동기 처리 p95 50ms, buffered data 256KiB를 넘으면 변환을 중단하고 정책에 따라 원 stream을 통과·중단하도록 정할 수 있습니다. 정확한 한계는 제품 SLO에 맞추되, 무한 buffering을 기본값으로 두지 않는 것이 핵심입니다.
4) plugin 공급망은 gateway의 공급망이다
gateway plugin은 일반 library보다 민감할 수 있습니다. prompt, tool argument, model credential, tenant policy, trace를 동시에 볼 가능성이 있기 때문입니다. community plugin을 편하게 설치하는 행위는 기능 추가이면서 권한 추가입니다. 특히 remote config를 받아 임의 URL로 보내거나, broad file/network access를 요구하거나, 실행 중 다른 plugin을 내려받는 extension은 별도 보안 검토 대상입니다.
모든 plugin에 publisher, pinned version 또는 digest, requested permission, configuration owner, 비밀값 경로, rollback version을 inventory로 남기세요. AI Inference Portability와 Exit Plan에서 provider 교체 경로를 준비하듯, 핵심 plugin도 제거했을 때 어떤 policy가 사라지는지와 대체 동작을 알아야 합니다. 큰 framework가 “sandboxed plugin”을 표방해도 egress, secret injection, host filesystem, telemetry sink 범위를 직접 확인하지 않으면 신뢰 경계는 생기지 않습니다.
실무 적용
1) plugin manifest를 배포 단위로 만든다
README에 설명만 쓰지 말고 deployable manifest에 아래 정보를 둡니다. 이 정보가 없으면 운영자는 incident 때 그 plugin이 어떤 요청을 바꿨는지, 끄면 어떤 위험이 남는지 빠르게 판단할 수 없습니다.
name: pii-redaction-v2
phase: transform-request
reads: [user_content]
writes: [sanitized_content, redaction_metadata]
network_egress: false
latency_budget_ms_p95: 30
failure_mode: fail_closed
conflicts: [raw-prompt-exporter]
rollback: pii-redaction-v1@sha256:...
manifest는 보안 문서가 아니라 runtime gate여야 합니다. network_egress: false인 plugin이 outbound call을 하려 하면 load 또는 CI canary에서 실패하게 하고, observer가 request body를 읽으려 하면 data classification permission을 추가 승인하게 만드세요. 고위험 plugin은 source repository URL이나 태그가 아니라 immutable digest로 pin하고, upgrade는 artifact diff와 representative fixture 결과를 같이 검토합니다.
2) provider 조합별 golden fixture로 기능 유실을 잡는다
새 provider를 붙일 때 200 응답만 확인하면 안 됩니다. 최소한 다음 fixture를 실제 protocol별로 고정합니다.
- text만 있는 일반 응답과 finish reason
- strict JSON schema 성공·실패 응답
- 병렬 tool call, tool error, tool call 취소
- streaming 중 client disconnect와 upstream cancellation
- usage/cost event, cache hit 표기, tenant quota 차감
- unsupported capability 요청의 명시적 reject 또는 downgrade receipt
각 fixture는 expected content만 아니라 event 순서, unknown field 보존, cancel 이후 provider request 수, 민감 data masking까지 비교해야 합니다. 예를 들어 stream cancel 뒤에도 token이 계속 발생하면 client 화면은 멈춰도 비용이 누적될 수 있습니다. Execution Receipt 형태로 선택된 provider, policy decision, plugin chain version, 실제 capability, 변환·차단 사유를 남기면 incident 때 추측을 줄일 수 있습니다.
3) observe-only에서 enforce까지 단계적으로 올린다
새 gateway extension은 아래 순서가 안전합니다.
- offline fixture: known request·response에서 schema와 policy 결과를 비교한다.
- observe-only: production metadata를 보고 결정만 기록하며 결과는 바꾸지 않는다.
- shadow: 허용된 sample에서 별도 경로를 실행해 latency·drop·capability 차이를 비교한다. 사용자 원문과 secret은 최소화하거나 masking한다.
- canary enforce: 1~5% tenant 또는 internal traffic에만 켜고, 독립 kill switch를 확인한다.
- 확대 또는 중단: gateway error rate가 기준선보다 0.5%p 이상 오르거나 interactive latency p95가 15% 이상 악화되면 확장을 멈추고 plugin만 rollback한다.
이 숫자는 출발점입니다. 중요한 것은 core gateway 또는 모든 모델 route를 한 번에 rollback해야 하는 구조를 피하는 것입니다. plugin이 맞지 않다고 판단됐을 때 policy ID 하나만 끌 수 있어야 실험과 안정성을 같이 가져갈 수 있습니다.
트레이드오프/주의점
작은 core와 많은 plugin은 서로 다른 팀의 개발 속도를 높이지만, version 조합 수를 폭발시킵니다. plugin을 너무 잘게 쪼개면 request path가 길어지고, 어떤 extension이 payload를 바꿨는지 이해하기 어려워집니다. 반대로 모든 provider 특수 처리를 core에 넣으면 upgrade가 느려지고 하나의 배포가 모든 tenant를 위험에 빠뜨립니다. 기본 원칙은 protocol의 불변성·credential lifecycle·stream cancellation 같은 safety-critical invariant는 core 또는 강하게 검증된 adapter에 두고, 자주 바뀌는 routing·policy·관측은 좁은 extension으로 분리하는 것입니다.
또한 “plugin으로 바꾸었으니 vendor lock-in이 없다”는 결론은 성급합니다. provider-specific behavior가 prompt template, eval set, tool schema, cache key, billing event에 스며들면 adapter만 교체해도 제품 행동이 달라집니다. portability는 interface 이름이 아니라 같은 fixture를 통과하고 rollback할 수 있는 증거로 판단해야 합니다.
마지막으로 gateway에 모든 보안 결정을 집중시키면 domain service의 authorization이 약해질 수 있습니다. gateway는 model·tool 호출의 공통 경계를 지키지만, 주문 취소나 데이터 export처럼 제품 고유 권한은 해당 service가 최종적으로 검증해야 합니다. gateway policy는 보완 계층이지 domain authorization의 대체물이 아닙니다.
체크리스트 또는 연습
- 모든 plugin에 phase, read/write data class, egress, latency budget, failure mode, rollback target이 있다.
- provider fallback 전에 required capability를 검사하며, optional field 유실은 downgrade receipt에 남긴다.
- plugin 순서가 config의 우연이 아니라 dependency·conflict가 있는 versioned contract다.
- interactive stream의 추가 동기 처리 budget과 max buffer를 정하고 cancellation fixture를 실행했다.
- observer 실패가 user request를 막지 않아도 되는지, policy 실패가 fail-closed여야 하는지를 각각 정했다.
- plugin artifact는 mutable tag가 아닌 검증 가능한 version 또는 digest로 관리한다.
- offline → observe-only → shadow → 1~5% canary → 확대의 순서와 독립 kill switch가 있다.
연습으로 현재 AI gateway의 확장 하나를 고르세요. 그 plugin이 읽는 데이터, 바꾸는 필드, 실패 시 사용자에게 미치는 결과, 30분 안에 끌 수 있는지를 네 줄로 적습니다. 답이 모호하다면 plugin을 더 만들기 전에 manifest와 receipt부터 설계해야 합니다. 반대로 이 네 줄이 명확하고 fixture·rollback이 준비돼 있다면, provider와 policy가 빠르게 바뀌는 환경에서도 gateway를 안정적인 플랫폼 경계로 운영할 수 있습니다.
💬 댓글