AI 엔드포인트가 전통적인 API 흐름을 어떻게 변화시키는가
요약
전통적인 결정론적 API와 달리, 확률적 특성을 가진 AI 엔드포인트 구축 시 고려해야 할 설계 변화를 다룹니다. 입력값 제한, 구조화된 출력 검증, 지연 시간 및 비용 관리 등 AI 기반 백엔드 개발의 핵심 차이점을 설명합니다.
핵심 포인트
- AI 모델의 확률적 특성으로 인한 출력 불확실성 대응 필요
- 단순 프록시를 넘어선 입력값 제한 및 컨텍스트 관리의 중요성
- 검증(Validation) 단계가 실행 보호를 넘어 사후 처리 단계로 확장
- 지연 시간, 재시도 로직, 멱등성 등 AI 특화 엔드포인트 설계 고려
백엔드 개발자로서 저는 수백 개의 엔드포인트를 구축해 왔기에, 전형적인 엔드포인트 흐름은 웹 애플리케이션을 생각하는 방식에 깊게 각인되어 있습니다. 하지만 AI 기반 (AI-powered) 엔드포인트를 구축하기 시작했을 때, 흥미로운 변화를 감지했습니다.
처음에 AI 엔드포인트는 모델에 연결하기 위한 몇 가지 설정만 갖춘 단순한 프록시 (proxy) 엔드포인트처럼 보였습니다:
API가 요청을 받음
↓
모델로 프롬프트 (prompt) 전송
...
그리고 클라이언트로부터 프롬프트를 직접 전달하는 것이 좋은 생각이 아니라는 것을 알게 될 때까지는 잘 작동했습니다. 엔드포인트가 완전히 다른 목적으로 오용될 수 있으며, 다른 누군가가 나의 AI 사용 크레딧을 소모하게 만들 수 있기 때문입니다.
그 후 저는 입력값에도 제한이 필요하다는 것을 깨달았습니다. 특정 작업을 위해 너무 큰 컨텍스트 (context)를 보내는 것은 더 많은 비용이 들고 예상치 못한 결과를 초래할 수 있습니다.
그래서 더 자세히 살펴보기 시작했을 때, 특히 신뢰할 수 있는 구조화된 출력 (structured output)과 예측 가능한 애플리케이션 동작이 필요했을 때, 저는 이것이 그렇게 간단하지 않다는 것을 빠르게 깨달았습니다. 검증 (Validation)은 더 이상 실행을 보호하는 역할만 하는 것이 아니라, 사후 처리 (post-processing) 단계가 되었습니다. AI 모델은 확률적 (probabilistic)입니다. 동일한 입력값이라도 모델은 서로 다른 출력을 반환하거나, 필수 정보를 누락하거나, 지침을 오해하거나, 기술적으로는 유효하지만 논리적으로는 틀린 것을 반환할 수 있습니다. 그리고 모든 토큰 (token)에는 비용이 발생하기 때문에, 단순히 요청을 재시도하며 더 나은 결과가 나오기를 바랄 수도 없습니다.
그때 저는 AI 엔드포인트가 기존의 웹 API 엔드포인트와 동일한 방식으로 설계되어야 하는지 의문을 갖기 시작했습니다.
목차
- 전통적인 웹 API 엔드포인트 흐름
- AI 기반 웹 API 엔드포인트 흐름
- 이 차이점이 변화시키는 것들
- 예측 불가능한 지연 시간 (Latency)
- 재시도 로직 (Retry Logic)
- 멱등성 (Idempotency) 및 부작용 (Side Effects)
- AI 엔드포인트 테스트
- 출력 계약 (The Output Contract)
- 관측 가능성 (Observability) 및 비용
- 요약
전통적인 웹 API 엔드포인트 흐름
전통적인 웹 API 엔드포인트는 보통 다음과 유사한 흐름을 따릅니다:
요청 검증 (validate request)
↓
비즈니스 로직 실행 (execute business logic)
...
첫 번째 단계는 요청 검증 (request validation)입니다. 우리는 들어오는 데이터를 속성 제약 조건 (property constraints), API 계약 (API contracts), 권한 부여 규칙 (authorization rules) 및 애플리케이션별 비즈니스 규칙 (application-specific business rules)에 따라 검증합니다.
두 번째 단계는 실행 (execution)입니다. 애플리케이션은 데이터를 처리하고, I/O 작업 (I/O operations)을 수행하거나 비즈니스 로직 (business logic)을 실행합니다.
마지막 단계는 결과의 표현 (representation of the result)을 반환하는 것입니다. 전통적인 엔드포인트 (endpoint)에서 실행되는 코드는 일반적으로 알려진 애플리케이션 상태 (application state) 내에서 결정론적 (deterministic)입니다. 동일한 코드가 동일한 상태에서 실행될 때, 우리는 일반적으로 동일한 결과를 얻습니다.
그리고 이것이 기본적으로 전부입니다.
전통적인 Web API 엔드포인트의 일반적인 흐름은 개별 단계가 물론 매우 복잡할 수는 있지만 상대적으로 단순합니다. 하지만 AI 모델은 정확히 같은 방식으로 작동하지 않습니다. AI 모델은 단순히 미리 정의된 명령 시퀀스 (sequence of instructions)를 실행하고 항상 동일한 결과를 생성하지는 않습니다.
AI 기반 Web API 엔드포인트 흐름 (AI-powered Web API Endpoint Flow)
AI 기반 엔드포인트의 내부 흐름은 보통 더 복잡합니다:
요청 검증 (validate request)
↓
프롬프트, 도구 및 컨텍스트 준비 (prepare prompt, tools and context)
...
우리는 여전히 들어오는 요청을 검증하는 것으로 시작합니다. 표준 속성 제약 조건 (property constraints)과 비즈니스 규칙 (business rules)은 여전히 중요하지만, AI 엔드포인트는 추가적인 제어 (controls)를 필요로 할 수도 있습니다. 애플리케이션은 요청된 주제를 제한하거나, 입력 크기를 제한하거나, 의심스러운 지침 (suspicious instructions)에 대한 제어를 적용하거나, 신뢰할 수 없는 검색된 콘텐츠 (untrusted retrieved content)를 격리하거나, 모델이 호출할 수 있는 도구 (tools)를 결정해야 할 수도 있습니다.
다음 단계는 모델 호출 (model call)을 위한 프롬프트 (prompt)와 설정 (settings)을 준비하는 것입니다. 애플리케이션은 지침 (instructions), 대화 기록 (conversation history), 검색된 컨텍스트 (retrieved context), 도구 정의 (tool definitions) 및 생성 설정 (generation settings)을 준비합니다.
그다음 모델이 출력을 생성합니다. 하지만 전통적인 비즈니스 로직의 결과와 달리, 모델 호출이 성공했다고 해서 출력이 올바르다고 자동으로 가정할 수는 없습니다. 모델 제공자 (model provider)로부터 받은 성공적인 HTTP 응답은 모델이 무언가를 생성했다는 사실만을 알려줄 뿐입니다. 그 결과가 완전한지, 안전한지, 근거가 있는지 (grounded), 혹은 유용한지조차 알려주지 않습니다.
따라서 검증(validation)은 생성 이후에 다시 나타납니다. 예를 들어, 우리는 기존의 외부 서비스로부터 받은 응답과 유사한 방식으로 JSON 스키마(JSON schema)를 검증할 수 있습니다. 하지만 스키마가 올바르더라도, 결과가 비즈니스 도메인 관점에서 논리적으로 틀릴 수 있습니다. 또한 모델은 기술적으로 스키마에서 요구하지는 않지만, 제공된 문맥(context)에 따라 반드시 포함되어야 하는 속성(properties)을 누락할 수도 있습니다.
출력이 이러한 검증을 통과하지 못할 때, 애플리케이션은 다음에 무엇을 할지 결정해야 합니다. 응답을 수정하려고 시도하거나, 모델에게 다시 생성하도록 요청하거나, 폴백 모델(fallback model)을 사용하거나, 제어된 에러를 반환하거나, 혹은 사람이 검토할 수 있도록 요청을 보낼 수도 있습니다. 하지만 모든 결정에는 각자의 비용이 따릅니다. 재시도(retry)는 더 많은 토큰을 소비하며, 사람의 개입은 시간과 비용을 모두 소모합니다.
이 모든 과정은 엔드포인트(endpoint)를 단순한 모델 호출을 위한 프록시(proxy)가 아닌, 오케스트레이션 파이프라인(orchestration pipeline)으로 변화시킵니다.
이 차이점이 변화시키는 것들
전통적인 웹 API(Web API)의 경우, 애플리케이션은 결과가 어떻게 생성되는지를 명시적으로 정의합니다. 이는 실행 경로가 코드로 제어되며, 우리가 어떤 결과를 기대할 수 있는지 알고 있음을 의미합니다.
AI 기반 웹 API(AI-powered Web API)의 경우, 애플리케이션은 결과 생성의 일부를 확률적 시스템(probabilistic system)에 위임합니다. 이는 우리가 결과가 생성되는 방식을 더 이상 완전히 제어할 수 없음을 의미합니다. 기대하는 결과를 얻기 위해서는 후처리(post-processing) 및 출력 검증(output validation)과 같은 추가적인 단계를 흐름에 추가해야 합니다.
이를 다음과 같이 단순화할 수 있습니다:
- 전통적인 웹 API: 백엔드가 규칙을 실행합니다.
- AI 기반 웹 API: 백엔드가 결과를 오케스트레이션하고 평가합니다.
하지만 이러한 차이는 검증 그 이상의 것에 영향을 미칩니다. 이는 지연 시간(latency), 재시도(retries), 멱등성(idempotency), 테스트(testing), 관찰 가능성(observability), 비용(cost) 및 출력 계약(output contracts)에 대해 우리가 생각하는 방식까지 변화시킵니다.
분명 더 많은 차이점이 존재하겠지만, 제가 진행 과정에서 발견한 것들을 살펴보겠습니다.
예측 불가능한 지연 시간 (Unpredictable Latency)
전통적인 엔드포인트의 지연 시간 (Latency)은 보통 비즈니스 로직, I/O 작업, 데이터 처리 및 이와 유사한 작업들에 의해 결정됩니다. 이러한 작업들이 항상 빠르거나 완벽하게 예측 가능한 것은 아니지만, 우리는 보통 이를 개별적으로 측정할 수 있으며 코드를 개선하거나, 데이터베이스 쿼리를 최적화하거나, 캐싱 (Caching)을 추가함으로써 가장 느린 부분을 최적화할 수 있습니다.
AI 엔드포인트는 또 다른 수준의 가변성을 도입합니다.
생성 시간 (Generation time)은 선택된 모델, 프롬프트 (Prompt) 및 컨텍스트 크기 (Context size), 출력 길이, 제공업체 부하 (Provider load) 및 기타 요인에 따라 달라질 수 있습니다. 어떤 요청은 단 한 번의 모델 호출로 끝날 수 있는 반면, 다른 요청은 여러 번의 도구 호출 (Tool calls), 검증 시도 (Validation attempts) 또는 재생성 단계 (Regeneration steps)를 필요로 할 수도 있습니다.
따라서 지연 시간은 더 이상 우리가 명시적으로 실행하는 작업들에 의해서만 결정되지 않습니다. 모델 생성 과정 중에 내려진 결정들에 의해서도 영향을 받을 수 있습니다.
이로 인해 타임아웃 (Timeouts), 취소 (Cancellation), 스트리밍 (Streaming), 비동기 처리 (Asynchronous processing) 및 지연 시간 예산 (Latency budgets)이 더욱 중요해집니다.
AI 기반 엔드포인트의 지연 시간을 개선하는 가장 쉬운 방법 중 하나는 프롬프트를 개선하는 것입니다. 프롬프트를 더 구체적으로 만들고, 불필요한 컨텍스트를 줄이며, 예상되는 출력을 제한할 수 있습니다.
또 다른 예는 모델이 전체 객체 (Full objects)를 반환할 필요가 없도록 만드는 것입니다.
예를 들어, 다음과 같이 데이터를 제공할 수 있습니다:
{
"data": [
{
...
그런 다음 모델에게 전체 객체를 반복하는 대신 선택된 ID들만 반환하도록 지시할 수 있습니다.
백엔드 (Backend)는 생성 후에 해당 ID들을 원래의 데이터로 다시 매핑할 수 있습니다. 이는 출력 토큰 (Output tokens)의 수를 줄여주며 지연 시간과 비용을 모두 개선할 수 있습니다.
핵심은 우리가 지연 시간 최적화에 접근하는 방식이 다르다는 점입니다. 전통적인 엔드포인트의 경우, 우리는 보통 코드, 데이터베이스 액세스 또는 캐싱을 최적화합니다. AI 엔드포인트의 경우, 프롬프트, 컨텍스트 크기, 모델 선택 및 생성된 출력물 또한 최적화해야 합니다.
재시도 로직 (Retry Logic)
재시도 로직 (Retry Logic)
전통적인 Web API에서는 특정 조건 하에서 I/O 작업을 재시도하는 경우가 많습니다. 예를 들어, 의존성(dependency)이 일시적으로 사용할 수 없거나, 연결이 중단되거나, 외부 서비스가 일시적인 상태 코드(transient status code)를 반환할 때 재시도를 수행할 수 있습니다. 이러한 재시도는 대개 기술적인 실패(technical failures)를 기반으로 합니다.
AI 엔드포인트는 또 다른 유형의 재시도를 도입합니다. 요청이 전송 계층(transport level)에서는 성공적으로 완료될 수 있지만, 생성된 출력물(output)은 여전히 사용할 수 없는 상태일 수 있습니다. 예를 들어, 필수 필드가 누락되거나, 예상된 스키마(schema)를 위반하거나, 제공된 컨텍스트(context)와 모순되거나, 비즈니스 규칙(business rule)을 통과하지 못할 수 있습니다.
모든 재시도는 지연 시간(latency)을 증가시키고 추가적인 토큰(tokens)을 소비하며, 이는 곧 추가 비용을 의미합니다. 다음 시도에서도 결과가 달라질 수는 있지만 여전히 부정확한 응답이 돌아올 수 있습니다.
그렇기 때문에 AI 재시도는 일반적인 네트워크 재시도와 동일하게 취급해서는 안 됩니다. 명시적인 제한이 필요하며, 토큰 및 금전적 예산, 실패 원인, 다른 시도가 도움이 될 가능성, 그리고 폴백 모델(fallback model)이나 결정론적 대안(deterministic alternative)의 가용성 등 추가적인 제약 조건도 고려해야 합니다.
Ollama를 통해 노출된 로컬 모델과 함께 사용했던 이 C# 재시도 파이프라인(retry pipeline)을 살펴보겠습니다:
private static class RetryPipeline<T>
{
public static readonly ResiliencePipeline<Result<T>> Instance = new ResiliencePipelineBuilder<Result<T>>()
...
이 파이프라인은 HttpRequestException, JsonException 또는 TaskCanceledException을 수신할 때 재시도합니다. 최대 2회의 재시도 시도를 허용하며, 2초의 초기 지연 시간을 가진 지수 백오프(exponential backoff)를 사용합니다.
지금까지는 전통적인 API 엔드포인트를 위한 전형적인 회복력 파이프라인(resilience pipeline)처럼 보입니다.
중요한 차이점은 바로 이 조건입니다:
.HandleResult(r => r.IsFailed)
이 조건은 작업이 예외(exception)를 던지지 않더라도 실패한 결과(failed result)를 반환하는 경우에도 재시도를 수행합니다.
이 파이프라인(pipeline)에 의해 실행되는 액션(action) 내부에서, 저는 AI 출력을 검증합니다. 만약 응답이 기술적으로는 유효하지만 논리적으로 틀렸다면, 실패한 결과(failed result)를 반환하고 파이프라인이 다시 시도하도록 합니다.
이러한 접근 방식은 유료 모델 제공업체를 사용할 때는 이상적이지 않을 수 있습니다. 모든 재시도가 추가적인 토큰을 소비하고 비용을 증가시키기 때문입니다. 하지만 로컬 모델이나 무료 모델의 경우, 작고 엄격하게 제한된 횟수의 재시도만으로도 충분할 수 있습니다.
때로는 재시도를 하지 않는 것이 올바른 결정일 수 있습니다. 출력을 거부하거나, 제어된 에러(controlled error)를 반환하거나, 사용자에게 더 많은 정보를 요청하는 것이 더 나을 수 있습니다.
멱등성 (Idempotency) 및 부수 효과 (Side Effects)
멱등성 (Idempotency)이란 동일한 작업을 여러 번 실행해도 한 번 실행했을 때와 동일한 효과를 갖는 것을 의미합니다.
간단한 예로 전통적인 GET 엔드포인트(endpoint)가 있습니다:
GET /api/orders/123
우리는 주문을 변경하지 않고 이 엔드포인트를 여러 번 호출할 수 있습니다. 기반 데이터가 변경되면 반환되는 표현(representation)은 달라질 수 있지만, 요청 자체는 추가적인 부수 효과 (side effects)를 발생시키지 않습니다.
AI 엔드포인트의 경우, 모델이 도구(tools)를 호출할 수 있게 되면 멱등성 (idempotency)은 더 복잡해집니다.
동일한 프롬프트 (prompt)를 여러 번 보내면 서로 다른 출력이나 약간씩 다른 문구가 생성될 수 있습니다. 읽기 전용 엔드포인트의 경우 서버 상태가 변경되지 않으므로 이는 허용될 수 있습니다.
하지만 엔드포인트가 액션 (actions)을 수행할 수 있을 때는 훨씬 더 위험해집니다. 예를 들어, 모델이 도구를 통해 주문을 생성하거나, 이메일을 보내거나, 데이터를 업데이트할 수 있습니다. 만약 나중에 출력 검증이 실패하여 전체 작업이 재시도된다면, 모델은 동일한 도구를 다시 호출하여 두 번째 주문을 생성할 수도 있습니다.
그렇기 때문에 AI 엔드포인트에 의해 트리거되는 액션들은 멱등성 키 (idempotency keys), 영구 저장된 작업 결과 (persisted operation results), 그리고 유니크 제약 조건 (unique constraints)과 같은 보호 조치를 사용해야 합니다. 동일한 작업 ID (operation ID)로 동일한 액션을 반복하면, 부수 효과를 다시 실행하는 대신 원래의 결과를 반환해야 합니다.
AI 엔드포인트 테스트하기
애플리케이션 상태(application state)와 의존성(dependencies)이 제어되는 상황에서 전통적인 엔드포인트를 테스트하는 것은 비교적 간단합니다.
데이터를 준비하고, 엔드포인트를 실행한 뒤, 상태 코드(status code), 예상 응답 값(expected response values), 업데이트된 애플리케이션 상태, 또는 외부 의존성(external dependency) 호출 여부와 같은 예상 결과를 검증(assert)합니다.
AI 엔드포인트의 경우, 정확한 출력(exact-output)을 검증하는 것은 종종 취약(fragile)합니다. 동일하게 유효한 답변이라도 매우 다양한 방식으로 작성될 수 있기 때문입니다. 또한 모델 업데이트(model update)로 인해 의미는 동일하지만 표현 방식이 바뀔 수도 있습니다. 하지만 응답 상태(response status)만 확인하거나 필드가 null이 아닌지만 체크하는 것은 검증 강도가 너무 낮습니다.
항상 정확한 응답을 비교하는 대신, 결과의 필수 속성(required properties)을 검증할 수 있습니다. 사용 사례(use case)에 따라 값이 허용된 범위 내에 있는지, 금지된 콘텐츠가 포함되지 않았는지, 도구 호출(tool calls)이 허용 목록(allowlist)을 준수하는지, 그리고 응답이 예상된 비즈니스 규칙(business rules)을 따르는지 등을 검증할 수 있습니다.
예를 들어, Assert.Equal을 사용하여 전체 응답을 검증하는 것은 취약할 수 있습니다:
var result = await endpoint.GeneratePlanAsync(request);
// 취약한 검증 (Fragile assertion)
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기