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에서 동일한 에이전트 스택을 실행하는 방법을 보여줘요.

소스: 배포 쿡북js-deno.

출처: 문서

본문

Deno Deploy에 배포하기

  1. Deno Deploy 프로젝트 만들기: langchain-ai/deployment-cookbook을 포크하거나 클론하세요. Deno Deploy 대시보드에서 이 저장소에 연결된 새 프로젝트를 만드세요.

  2. 빌드 설정 구성하기:

    • Root Directoryjs-deno로 설정하세요.
    • build commanddeno task build:client로 설정하세요 (Vite SPA를 dist/로 빌드).
    • entrypointmain.ts로 설정하세요.
    • 프로젝트 환경 변수에 OPENAI_API_KEY를 추가하세요.
  3. 배포: 대시보드에서 배포하세요. Deno의 빌드 환경이 빌드 명령을 실행하므로 dist/가 클라우드에서 생성되며 커밋할 필요가 없어요.

또는 내장 deno deploy CLI(Deno 2.x)를 사용하세요. deno.jsondeploy 블록이 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 deploydeno 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: LangGraph StreamChannel에 프로토콜 이벤트를 버퍼링하고, matchesSubscription으로 필터링하며, 일치하는 프레임을 SSE ReadableStream으로 팬아웃(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.tsMemorySaver@langchain/langgraph-checkpoint-postgres@langchain/langgraph-checkpoint-redis 같은 내구성 있는 체크포인터로 교체하세요. SSE 재연결이 isolate 간에 작동하도록 공유 세션/재생 저장소도 원할 거예요.

로컬 개발

클라이언트에는 Deno 2.x와 pnpm이 필요해요.

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로 생성).

더 알아보기

더 알아보기 (Learn more)