프론트엔드 샌드박스 패턴

프론트엔드 샌드박스 패턴 (Frontend Sandbox)

코딩 에이전트는 채팅 창만으로는 부족해요. 파일 브라우저, 코드 뷰어, diff 패널까지 갖춘 IDE 같은 경험이 필요하죠. 이 패턴은 딥 에이전트를 샌드박스에 연결해 격리된 환경에서 코드를 읽고 쓰고 실행하게 한 다음, 샌드박스 파일시스템을 커스텀 API 서버로 노출해서 에이전트가 작업하는 동안 프론트엔드가 파일을 실시간으로 보여 주게 해요.

이 페이지는 세 패널 UI(파일 트리, 코드 뷰어, 채팅)와 거기에 샌드박스 파일시스템을 노출하는 커스텀 API 라우트를 다뤄요. 샌드박스 제공자·라이프사이클 스코프·파일 시딩·시크릿·배포·프로덕션의 useStream 설정에 대해서는 [Going to production] 페이지를 참고하세요.

출처: 공식문서

아키텍처

이 구성은 세 부분으로 나뉘어요.

  1. 샌드백엔드가 달린 딥 에이전트 — 에이전트가 샌드박스에서 파일시스템 도구(read_file, write_file, edit_file, delete, execute)를 자동으로 받아요.
  2. 커스텀 API 서버langgraph.jsonhttp.app 필드로 노출하는 FastAPI 앱으로, 프론트엔드가 호출할 파일 탐색 엔드포인트를 제공해요.
  3. 세 패널 프론트엔드 — 에이전트가 변경할 때 파일을 실시간으로 동기화하는 파일 트리, 코드/diff 뷰어, 채팅 패널이에요.

흐름을 그림으로 보면 이렇게 돼요.

useStream()
/sandbox/:threadId/*
read/write/execute
ls / read

IDE Frontend  ──  API Server  ──  Sandbox
createDeepAgent()

샌드박스 라이프사이클

프론트엔드를 연결하기 전에 샌드박스가 얼마나 오래 사는지, 누가 공유하는지 정해야 해요. 스레드 스코프 vs 어시스턴트 스코프 샌드박스, 비동기 그래프 팩토리 설정, TTL 동작, SDK 호출 예시는 [Sandbox lifecycle] 문서를 보세요.

이 가이드는 기본적으로 스레드 스코프 샌드백을 써요. 프론트엔드와 커스텀 API 서버가 모두 LangGraph 스레드 ID에서 샌드박스를 찾아내죠. 이렇게 하면 대화가 서로 격리되고, 스레드 ID를 저장해 두면 페이지를 새로고침해도 같은 환경에 다시 연결돼요.

Page loads
  POST /threads                     → threadId
  GET /sandbox/:threadId/tree
  threads.get(threadId)             → metadata.sandbox_id
  LangSmithSandbox.create()
  threads.update(threadId, metadata.sandbox_id)
  connect(sandbox_id)               → file tree
User sends message
  POST /threads/:threadId/runs/stream
  backend reads thread_id from config
  connect to same sandbox

멀티테넌트 앱이라면 백엔드 팩토리에서 샌드박스를 사용자나 어시스턴트 단위로 스코프하세요. LangGraph 스레드 없이 데모만 돌릴 땐 API URL에 클라이언트가 만든 세션 ID를 넘기면 되는데, 이 세션 ID는 브라우저 세션을 넘어서는 유지되지 않아요.

에이전트와 API 서버 연결하기

[Execution environment]에 설명된 대로 딥 에이전트에 샌드박스 백엔드를 설정해 주세요. 그러면 에이전트가 파일시스템 도구와 execute 도구를 자동으로 받아서, 별도 도구 설정이 필요 없어요.

이 UI를 만들면 프로덕션 설정 위에 요구사항이 하나 더 붙어요. 에이전트 그래프 바깥에서 도는 커스텀 API 서버가 필요하죠. 그래서 에이전트 백엔드와 파일 탐색 라우트가 스레드별로 같은 샌드박스를 찾아야 해요. 샌드백 ID를 스레드 메타데이터에 저장하고, 둘 사이에서 단일 조회 함수를 공유하면 됩니다.

스레드 메타데이터에서 샌드박스 찾기

from deepagents import create_deep_agent
from deepagents.backends.langsmith import LangSmithSandbox
from langgraph.config import get_config


def get_or_create_sandbox_for_thread(thread_id: str) -> LangSmithSandbox:
    if not thread_id:
        raise ValueError("thread_id is required")
    # Look up sandbox_id from thread metadata, create if missing, and seed files.
    raise NotImplementedError(
        "Implement sandbox lookup and creation for your deployment environment."
    )


def get_thread_id_from_config() -> str:
    configurable = get_config().get("configurable", {})
    thread_id = configurable.get("thread_id")
    if not thread_id:
        raise ValueError("No thread_id, agent must run on a thread")
    return thread_id


def agent():
    return create_deep_agent(
        model="google_genai:gemini-3.6-flash",
        backend=lambda _runtime: get_or_create_sandbox_for_thread(
            get_thread_id_from_config()
        ),
    )

[Going to production]의 예시와 비슷하게, 에이전트는 매 실행마다 호출되는 비동기 그래프 팩토리예요. 샌드박스 ID를 스레드 메타데이터에 저장해 두면 커스텀 http.app 라우트가 같은 getOrCreateSandboxForThread 헬퍼를 호출할 수 있어요. LangGraph SDK가 유일한 진입점인 경우에만 Going to production 문서에서처럼 provider 라벨 조회를 써요.

프로젝트 파일 시딩

에이전트가 돌기 전에 uploadFiles / upload_files로 시작 파일을 올려 두세요. 시딩 패턴, 제공자별 예시, 메모리나 스킬을 샌드박스에 동기화하는 방법은 [File transfers] 문서를 참고하세요. LangSmith 샌드박스라면 컨테이너를 만들 때 샌드박스 스냅샷에서 templateName을 넘기면 돼요.

await sandbox.execute("cd /app && npm install && npm run build")

커스텀 API 서버

두 진입점(에이전트 백엔드와 파일 탐색 라우트)이 같은 샌드박스 조회 로직을 공유해야 하므로, 스레드 메타데이터에서 샌드박스를 찾는 조회 함수를 하나만 만들고 에이전트와 http.app 라우트 양쪽에서 그걸 호출해요. 에이전트는 그래프 팩토리 안에서, API 서버 라우트는 요청을 받을 때 각각 호출하죠.

API 라우트

커스텀 API 서버는 FastAPI 앱으로, langgraph.jsonhttp.app 필드로 노출돼요. 프론트엔드가 호출하는 파일 탐색 엔드포인트는 샌드박스의 루트 경로(/sandbox/:threadId/*)를 대상으로 해요. 파일 트리 목록(tree)과 파일 읽기(read, ls) 같은 라우트가 스레드 ID로 샌드백을 찾아 해당 파일시스템을 응답하죠. 이 경로들이 에이전트 백엔드와 같은 조회 함수를 쓰기 때문에, 에이전트가 만든 파일 변경이 프론트엔드 요청에도 그대로 반영돼요.

프론트엔드

프론트엔드는 세 패널로 이루어져요.

  • 파일 트리 — 샌드박스 파일시스템을 보여 주고, 에이전트가 추가·수정·삭제하는 파일이 실시간으로 반영돼요.
  • 코드/diff 뷰어 — 파일 내용과 변경 diff를 표시해요.
  • 채팅 패널 — 사용자가 에이전트에게 지시하고 실행 결과를 보는 곳이에요.

프론트엔드는 useStream()로 에이전트 실행을 스트리밍하고, 커스텀 API 서버의 파일 탐색 라우트로 샌드박스 파일시스템을 조회해요. 에이전트가 도구(read_file, write_file, edit_file, delete, execute)로 파일을 바꿀 때마다 파일 트리와 뷰어가 그 상태를 따라가게 되는 구조예요.

더 알아보기 (Learn more)