Deno Deploy로 배포하기
Deno Deploy로 배포하기
Hono 라우트 핸들러와 단일 엔트포인트에서 서빙되는 Vite React SPA로 Deno Deploy에 LangChain 딥 에이전트를 배포해요.
이 페이지는 Deno Deploy에 LangChain 딥 에이전트를 배포하는 예시 앱을 자세히 설명해요: 스트리밍 채팅 UI, 하위 에이전트, 스레드 기록까지, 모두 Hono 서버에서 HTTP + SSE 라우트 핸들러로 구현된 Agent Streaming Protocol을 기반으로 해요. React 프론트엔드는 Vite SPA예요(Next.js 예시에서 포팅). Deno는 단일 main.ts 엔트포인트에서 빌드된 정적 자산과 API를 제공해요.
Next.js 예시를 Deno + Hono로 포팅한 것으로, Vercel 대신 Deno Deploy에서 동일한 에이전트 스택을 실행하는 방법을 보여줘요.
출처: 문서
본문
Deno Deploy에 배포하기
-
Deno Deploy 프로젝트 만들기:
langchain-ai/deployment-cookbook을 포크하거나 클론하세요. Deno Deploy 대시보드에서 이 저장소에 연결된 새 프로젝트를 만드세요. -
빌드 설정 구성하기:
- Root Directory를
js-deno로 설정하세요. - build command를
deno task build:client로 설정하세요 (Vite SPA를dist/로 빌드). - entrypoint를
main.ts로 설정하세요. - 프로젝트 환경 변수에
OPENAI_API_KEY를 추가하세요.
- Root Directory를
-
배포: 대시보드에서 배포하세요. Deno의 빌드 환경이 빌드 명령을 실행하므로
dist/가 클라우드에서 생성되며 커밋할 필요가 없어요.
또는 내장 deno deploy CLI(Deno 2.x)를 사용하세요. deno.json의 deploy 블록이 org/app을 설정해요. 이를 자신의 것으로 바꾸거나(또는 --org/--app 플래그를 전달해 재정의할 수 있음):
cd js-deno
# First time only: create the app
deno deploy create --org <your-org> --app <your-app> --source local --region us --entrypoint main.ts
# Set your OpenAI key
deno deploy env add OPENAI_API_KEY <your-key> --org <your-org> --app <your-app>
# Build the client, deploy to production, and clean up dist/
deno task deploy
deno task deploy는 deno task build:client && deno deploy --prod를 실행한 다음 rm -rf dist를 실행해요. deno deploy --source local은 .gitignore를 뺀 작업 트리를 업로드하고 빌드 명령을 실행하지 않기 때문에 로컬에서 빌드해야 해요. 빌드 명령은 GitHub에 연결된 앱에서만 실행돼요.
CLI
--source local흐름에만 해당하는 두 가지 함정:
dist/를 gitignore하면 안 돼요. 업로더는.gitignore를 존중하므로, 방금 빌드한dist/가 업로드 시점에 보여야 하며 그렇지 않으면 모든 비-/api라우트가 404를 반환해요. 저장소 루트.gitignore는 모든dist를 무시하므로js-deno/.gitignore는!dist/와!dist/**로 다시 포함시켜요.deno task deploy흐름은 업로드 후dist/를 삭제하므로 무시되지 않음에도git status에 남지 않아요.deploy.include목록을 사용하지 마세요.include를 추가하면 빌드가 엔트포인트를src/main.ts로 해석해 실패하는 Deno Deploy 버그가 있어요. 기본.gitignore기반 업로드에 의존하세요.
선택적으로 .env.example의 변수를 추가해 LangSmith 추적을 활성화하세요.
필수 API 엔드포인트
앱은 /api/threads/... 아래에 Agent Streaming Protocol을 노출해요. 라우트 핸들러는 server/routes.ts에 있으며 js-next/app/api/threads/의 Next.js 핸들러를 미러링해요.
최소 (스트리밍 채팅)
| 메서드 | 경로 | 목적 |
|---|---|---|
POST |
/api/threads/:threadId/commands |
프로토콜 명령(run.start, …)을 받고 에이전트 런을 시작 |
POST |
/api/threads/:threadId/stream |
런에 대한 프로토콜 이벤트의 SSE 스트림 |
GET / POST |
/api/threads/:threadId/state |
체크포인트된 스레드 상태 읽기 및 부트스트랩 |
선택 (사이드바)
| 메서드 | 경로 | 목적 |
|---|---|---|
GET |
/api/threads |
체크포인터가 아는 스레드 나열 |
DELETE |
/api/threads/:threadId |
스레드의 세션과 체크포인트 삭제 |
POST |
/api/threads/:threadId/history |
페이지네이션된 체크포인트 기록 (Agent Protocol) |
요청 흐름
%%{init: {"themeVariables": {"lineColor": "#40668D", "primaryColor": "#E5F4FF", "primaryTextColor": "#030710", "primaryBorderColor": "#006DDD"}}}%%
flowchart TB
subgraph browser["Browser (Vite React SPA)"]
SP["StreamProvider"]
Adapter["HttpAgentServerAdapter"]
SP --- Adapter
end
subgraph deno["Deno.serve + Hono"]
CMD["POST /api/threads/:id/commands"]
STR["POST /api/threads/:id/stream (SSE)"]
STA["GET|POST /api/threads/:id/state"]
end
subgraph server["server/"]
SRV["registry · session · threads"]
end
subgraph agent["server/agent"]
AGT["createDeepAgent + checkpointer"]
end
Adapter -->|POST| CMD
Adapter -->|POST| STR
Adapter -->|GET / POST| STA
CMD --> SRV
STR --> SRV
STA --> SRV
SRV --> AGT
classDef process fill:#E5F4FF,stroke:#006DDD,stroke-width:2px,color:#030710
classDef trigger fill:#F6FFDB,stroke:#6E8900,stroke-width:2px,color:#2E3900
classDef output fill:#EBD0F0,stroke:#885270,stroke-width:2px,color:#441E33
class browser,deno process
class server trigger
class agent output
Deno 백엔드 작동 방식
이 예시는 단일 Deno 프로세스로 실행돼요:
main.ts:Deno.serve+ Hono 앱./api라우트를 마운트하고dist/에서 Vite 빌드 SPA를 서빙해요.server/routes.ts: Agent Streaming Protocol에 대한 Hono 라우트 정의.server/session.ts:LocalThreadSession: LangGraphStreamChannel에 프로토콜 이벤트를 버퍼링하고,matchesSubscription으로 필터링하며, 일치하는 프레임을 SSEReadableStream으로 팬아웃(fan-out)해요.server/threads.ts: LangGraph SDK 와이어 형식의 체크포인터 기반getState/updateState/getHistory헬퍼.server/registry.ts: 에이전트와 스레드 ID당 하나의 세션을 소유하는 프로세스-로컬 싱글턴.server/agent/: Next.js 예시와 동일한createDeepAgent오케스트레이터 (researcher + math-whiz 하위 에이전트, mock 도구).
Deno Deploy는 각 isolate를 자체 인메모리 MemorySaver 체크포인터로 실행해요. isolate 간 프로덕션 영속성을 위해 내구성 있는 체크포인터(Postgres, Redis 등)로 교체하세요. 라우트 핸들러와 server/threads.ts 헬퍼는 동일하게 유지돼요.
프로덕션 영속성
기본적으로 에이전트는 인메모리 MemorySaver 체크포인터(server/agent/index.ts)와 프로세스-로컬 세션 맵(server/registry.ts)을 사용해요. 이는 로컬 개발과 단일 isolate 배포에는 작동하지만, Deno Deploy(여러 isolate, 콜드 스타트)에서는 대화 상태가 인스턴스 간에 영구적이지 않아요.
server/agent/index.ts의 MemorySaver를 @langchain/langgraph-checkpoint-postgres나 @langchain/langgraph-checkpoint-redis 같은 내구성 있는 체크포인터로 교체하세요. SSE 재연결이 isolate 간에 작동하도록 공유 세션/재생 저장소도 원할 거예요.
로컬 개발
cp .env.example .env # set OPENAI_API_KEY
export $(grep -v '^#' .env | xargs) # load env for Deno
# Terminal 1 — API + static (after first client build)
deno task build:client # first time only
deno task dev
# Terminal 2 — Vite dev server with HMR (proxies /api to :8000)
cd client && pnpm install && pnpm dev
핫 리로드 개발을 위해 http://localhost:5173을 여세요. Vite dev 서버가 /api를 포트 8000의 Deno 서버로 프록시해요.
프로덕션과 유사한 로컬 실행(단일 서버, HMR 없음):
deno task build:client
deno task start
http://localhost:8000을 여세요.
프로젝트 구조
main.ts: Deno Deploy 엔트포인트(Deno.serve+ Hono).server/agent/: 하위 에이전트와 mock 도구가 있는 딥 에이전트(createDeepAgent).server/: 프로토콜 서버 로직:session.ts,threads.ts,serialize.ts,registry.ts,routes.ts.client/: Vite + React SPA (Next.js 예시와 동일한 UI).dist/: Deno가 서빙하는 Vite 빌드 출력 (deno task build:client로 생성).