본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 31. 15:03

Rust로 구축한 프로덕션 지향 멀티 프로바이더 AI 챗봇 — 구축 방법

요약

Python 대신 Rust를 사용하여 프로덕션 환경에 적합한 멀티 프로바이더 AI 챗봇 백엔드를 구축하는 방법을 소개합니다. Claude, OpenAI, Ollama를 단일 인터페이스로 통합하며, 비동기 처리와 타입 안전성을 통해 안정적인 성능을 제공합니다.

핵심 포인트

  • Rust의 비동기 처리와 메모리 효율성을 통한 낮은 지연 시간 확보
  • Claude, OpenAI, Ollama를 지원하는 통합 인터페이스 구현
  • 타입 안전성을 활용한 견고한 API 계약 및 에러 처리
  • Web UI, CLI, Docker를 지원하는 프로덕션 지향 아키텍처

대부분의 AI 챗봇 튜토리얼은 Python을 선택합니다. FastAPI, LangChain, 그리고 빠른 requests.post — 20분이면 끝납니다. 프로토타이핑(Prototyping) 용도로는 괜찮습니다. 하지만 제가 실제로 실제 API 뒤에 배치할 무언가 — 적절한 비동기 동시성(Async concurrency), 타입이 지정된 에러(Typed errors), 그리고 GC(Garbage Collection) 일시 중단이 없는 무언가 — 를 만들고 싶었을 때, 저는 대신 Rust를 선택했습니다.

이 글은 Claude, OpenAI, 그리고 Ollama를 단일 인터페이스 뒤로 통합하고, Web UI, CLI 모드, Docker 지원이 내장된 프로덕션 지향 Rust 백엔드인 **chatbot**에 대한 기록입니다.

왜 AI 백엔드로 Rust인가?

타당한 질문입니다. LLM API 호출은 네트워크 바운드(Network-bound)인데, 왜 백엔드 언어가 중요할까요?

몇 가지 이유가 있습니다:

  • 예측 가능한 지연 시간(Latency). 부하 상황에서도 GC 일시 중단이 없다는 것은 수십 개의 동시 대화를 처리할 때 P99 응답 시간이 안정적으로 유지됨을 의미합니다.
  • 메모리 효율성. Tokio의 각 비동기 태스크(Async task)는 Python 스레드보다 훨씬 저렴합니다. 인스턴스당 더 많은 사용자를 서비스할 수 있습니다.
  • 타입 안전성(Type safety). Rust의 소유권 모델(Ownership model)은 API 계약을 명시적으로 만듭니다. 요청 간에 가변적인 대화 상태(Mutable conversation state)를 실수로 공유할 수 없습니다 — 컴파일러가 이를 강제합니다.
  • 장기적인 유지보수성. 명시적인 타입과 명시적인 에러 처리(Error handling)는 코드를 자기 문서화(Self-documenting)하게 만듭니다.

특히 LLM 앱의 경우: 네, 실제 소요 시간의 95%는 모델의 응답을 기다리는 데 사용됩니다. 하지만 나머지 5% — 라우팅(Routing), 상태 관리(State management), 프로바이더 선택, 연결 처리 — 는 모두 당신이 제어해야 할 영역입니다. Rust는 그 부분을 철통같이 만들어 줍니다.

주요 기능

  • 통합 채팅 인터페이스를 통해 Claude (Anthropic), OpenAI, 또는 Ollama (로컬)에 연결
  • 기본적으로 http://localhost:8080에서 Web UI를 제공하거나, CLI 모드로 실행 가능
  • 메모리 내에 세션별 대화 기록(Per-session conversation history) 유지
  • 선(先) 연결 흐름을 통한 런타임 프로바이더 전환(Runtime provider switching) 지원
  • 컨테이너화된 배포를 위한 Dockerfile 포함
  • OpenAI 호환 프로바이더로서 OpenRouter 지원

아키텍처 개요 (Architecture Overview)

User (Browser or CLI)
        │
        ▼
...

이 프로젝트는 src/ 디렉토리에 깔끔한 5개 모듈 레이아웃을 갖추고 있습니다:

src/
├── main.rs          # 스타트업 라우팅 + CLI 루프
├── config.rs        # Provider 열거형(enum) + 환경/런타임 설정
...

각 모듈은 정확히 하나의 책임만을 가집니다. 거대 객체(God objects)나 얽혀 있는 임포트(tangled imports)는 없습니다.

프로바이더 추상화 (The Provider Abstraction)

프로젝트의 핵심은 client.rs입니다. 프로바이더별 로직을 여기저기 흩뿌리는 대신, 모든 외부 AI 호출은 활성화된 프로바이더에 따라 요청을 전달하는 단일 ChatClient를 통해 이루어집니다.

핵심 통찰: Claude는 Anthropic 네이티브 API 형식을 사용하는 반면, OpenAI와 Ollama는 모두 OpenAI 호환 스키마(OpenAI-compatible schema)를 사용합니다. 이 두 가지 코드 경로를 분리함으로써 프로바이더 로직의 정직함을 유지합니다. 즉, 호환성이 없는 곳에서 억지로 호환되는 것처럼 속이지 않습니다.

// client.rs의 단순화된 개념
pub enum Provider {
    Claude,
...

메시지를 보낼 때, 클라이언트는 적절한 HTTP 규약(contract)을 선택합니다:

pub async fn send(&self, messages: &[Message]) -> Result<String> {
    match self.provider {
        Provider::Claude => self.send_claude(messages).await,
...

이는 향후 새로운 프로바이더(Gemini, Cohere 등)를 추가할 때, 하나의 분기(arm)와 하나의 메서드만 추가하면 된다는 것을 의미하며, 애플리케이션의 나머지 부분은 전혀 수정할 필요가 없습니다.

공유 대화 상태 (Shared Conversation State)

멀티 턴(Multi-turn) 채팅에는 지속적인 메시지 기록이 필요합니다. Rust의 비동기(async) 모델에서 요청 핸들러 간에 상태를 공유하려면 명시적인 동기화가 필요합니다. 이 프로젝트는 공유 가능한 가변 상태(shared mutable state)를 위한 Rust의 표준 패턴인 Arc<Mutex<...>>를 사용하여 이를 수행합니다:

// conversation.rs - 모든 핸들러 간에 공유됨
pub type SharedConversation = Arc<Mutex<Vec<Message>>>;

...

Arc는 Axum 핸들러 간에 대화 객체를 복제(clone)할 수 있게 해주고(각 핸들러는 자체적인 비동기 태스크에서 실행됩니다), Mutex는 한 번에 하나의 핸들러만 기록에 접근하도록 보장합니다. 타입 시스템에 의해 경합 조건(race conditions)이 방지됩니다.

듀얼 모드: 웹 UI + CLI (Dual-Mode: Web UI + CLI)

앱은 기본적으로 Web UI 모드로 실행되지만, 터미널 워크플로(terminal workflow)도 지원합니다:

# 기본값: http://localhost:8080에서 Web UI 제공
cargo run

...

이는 다양한 상황에서 유용합니다. Web UI는 데모 및 공유용으로, CLI는 스크립팅 및 다른 도구로의 파이핑(piping)용으로 사용할 수 있습니다.

빠른 시작 (Quick Start)

옵션 A: Rust 불필요

사전 빌드된 Windows 실행 파일 (v1.0.1)을 다운로드하여 실행하세요:

.\chatbot.exe
# http://localhost:8080 을 엽니다

옵션 B: 소스 코드로부터

# 1. Rust 설치
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

...

프로바이더 설정 (Provider Configuration)

.env 파일에서 프로바이더를 설정하세요:

Claude (Anthropic)

PROVIDER=claude
ANTHROPIC_API_KEY=sk-ant-...

OpenAI

PROVIDER=openai
OPENAI_API_KEY=sk-...

Ollama (로컬 — API 키 불필요)

# 먼저 모델을 가져옵니다 (pull)
ollama pull llama3.2
PROVIDER=ollama
MODEL=llama3.2

OpenRouter (OpenAI 호환 API를 통해 모든 모델에 액세스)

PROVIDER=openai
OPENAI_API_KEY=sk-or-...
BASE_URL=https://openrouter.ai/api
...

모든 환경 변수:

변수기본값설명
PROVIDERclaudeclaude, openai, ollama
...

Docker

docker build -t chatbot .
docker run -it --rm -v .env:/data/.env chatbot

호스트 네트워킹을 사용하는 Ollama의 경우:

docker run -it --rm --network host -v .env:/data/.env chatbot

Rust vs Python: 백엔드 성능 관점

이와 같은 백엔드 워크로드(동시 HTTP + JSON + 상태 관리)의 경우, Rust는 프로덕션에서 중요한 지표 전반에 걸쳐 Python보다 일관되게 우수한 성능을 보여줍니다:

지표RustPython중요성
처리량 (Throughput, req/sec)더 높음더 낮음인스턴스당 더 많은 동시 사용자 수용
...

참고: LLM 애플리케이션의 경우, 모델/API 네트워크 시간이 전체 지연 시간 (Latency)의 대부분을 차지합니다. 하지만 Rust는 동시성 동작 (Concurrency behavior), 메모리 사용량 (Memory footprint), 그리고 서버 효율성 측면에서 여전히 우위에 있으며, 이는 대규모 운영 시 비용과 신뢰성에 직접적인 영향을 미칩니다.

다음 단계 (로드맵)

  • 스트리밍 응답 (Streamed responses) — SSE를 통한 토큰 단위 스트리밍
  • 지속적인 채팅 기록 (Persistent chat history) — SQLite 또는 Postgres 백엔드
  • 메트릭 + 트레이싱 (Metrics + tracing)tracing 크레이트 (crate) + OpenTelemetry 통합
  • 통합 테스트 (Integration tests) — 프로바이더 어댑터용

시도해 보기 / 기여하기

이 프로젝트는 오픈 소스이며 MIT 라이선스를 따릅니다:

👉 github.com/MihirMohapatra/chatbot

백엔드 시스템을 위해 Rust를 탐색 중이거나, 각 프로바이더마다 반복적인 코드 (Boilerplate)를 작성하지 않고 여러 AI 프로바이더와 통신해야 하는 무언가를 구축하고 있다면, 이것은 좋은 시작점이 될 것입니다. 이슈 (Issues)와 풀 리퀘스트 (PRs)를 환영합니다.

Rust 1.80+, Tokio, Axum, reqwest, serde, 그리고 anyhow로 구축되었습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0