시작하기
SNA의 구조를 이해하고 서버를 띄운 뒤 첫 세션에 프롬프트를 보냅니다.
빈 프로젝트에서 라이브 이벤트를 받기까지의 가장 짧은 경로입니다. 이 페이지에서는 앱 안에서 SNA 서버를 실행하고, Claude Code 또는 Codex / OpenCode 세션을 열고, WebSocket으로 토큰을 스트리밍합니다.
SNA로 에이전틱 애플리케이션 만들기
SNA는 에이전틱 애플리케이션을 만들기 위한 런타임 계층입니다. 여기서 에이전트는 단발 응답을 만드는 모델 호출이 아니라, 제품 안에서 세션 히스토리를 이어가고, 툴을 사용하고, 권한을 요청하고, 상태를 UI로 스트리밍하는 실행 주체입니다.
SNA로 만들 수 있는 것은 이런 쪽에 가깝습니다.
- 세션을 가진 제품 에이전트. 매번 한 프롬프트에 답하는 대신 프로젝트 맥락을 유지하고 여러 턴에 걸쳐 이어지는 에이전트.
- 캐시 친화적인 장기 대화. 매 턴 히스토리와 캐시 설정을 다시 조립하지 않고, 같은 SNA 세션에 계속 메시지를 보내는 구조.
- 툴을 사용하는 워크플로. 각 CLI 런타임이 이미 다루는 툴 사용 능력을 제품 기능 안으로 끌어오는 흐름.
- 사람 승인이 들어가는 애플리케이션. 권한 요청과 승인을 제품 UI 안에서 직접 다루는 에이전트 경험.
- Runtime harness 기반 에이전트. 낮은 수준의 모델 API 위에 루프를 다시 만들지 않고 Claude Code, Codex, OpenCode가 모델에 맞춰 다듬어 둔 오케스트레이션 계층을 사용하는 에이전트.
- 멀티 런타임 제품. 제품 API는 안정적으로 유지하면서 아래의 에이전트 런타임은 바꿀 수 있는 구조.
무엇을 만드는지
SNA는 앱 안에 넣는 작은 런타임 서버입니다. 세션 시작을 요청하면 서버가 에이전트 CLI를 자식 프로세스로 실행하고, 그 프로세스를 하나의 HTTP + WebSocket API로 노출합니다. 앱 입장에서는 일반 백엔드에 요청하는 모양이지만, 실제 작업은 Claude Code / Codex / OpenCode 프로세스가 처리합니다.
your app
│
│ HTTP for control (send, interrupt, kill, …)
│ WS for events (assistant_delta, tool_use, complete, …)
▼
SNA server ──spawn──► claude | codex | opencode아래 5단계를 그대로 따라가면 됩니다. 정규화 히스토리, 런타임 교체, 어트리뷰션 같은 설계 배경은 SNA가 뭔가요? 에서 다룹니다.
사전 준비
- Node.js 20+
- 런타임 CLI 하나 이상:
PATH의claude,codex,opencode중 하나 또는 SNA 시작 시 넘기는 명시적runtimePaths - 패키지 매니저. 예시는
pnpm이지만 npm / yarn / bun도 가능합니다.
1. 설치
pnpm add @sna-sdk/core@0.17.2 @sna-sdk/client@0.17.2SNA는 아직 0.x.x 버전대라 앱에서 사용할 때는 정확한 패키지 버전을
고정해서 쓰십시오. 자세한 내용은 설치 에
정리했고, 버그나 요청 사항은
GitHub 이슈로 남겨주십시오.
전체 사전 조건과 CLI 경로 오버라이드는 설치 에 정리되어 있습니다.
2. 서버 부팅
앱 안에서:
import { resolveClaudeCli, startSnaServer } from "@sna-sdk/core/node";
const claude = resolveClaudeCli();
const sna = await startSnaServer({
appId: "my-app",
port: 3099,
dbPath: "./data/sna.db",
runtimePaths: {
claudeCode: claude.path,
},
});SNA 서버는 관리되는 자식 프로세스로 fork됩니다. sna.port에는 실제
바인딩 포트가 들어갑니다. SDK 클라이언트에는 sna.connection을 넘깁니다.
sna.appId는 이 서버가 만든 session의 owner metadata로 붙습니다.
port를 0으로 지정하면 자동 할당되며, sna.stop()을 호출하면
SIGTERM으로 서버를 종료합니다.
3. 클라이언트 연결
import { SnaClient } from "@sna-sdk/client";
const client = new SnaClient(sna.connection);
client.connect();4. 세션 열고 에이전트 시작
const { sessionId } = await client.sessions.create({ label: "scratch" });
await client.agent.start(sessionId, {
provider: "claude-code",
model: "claude-sonnet-4-6",
});5. 이벤트 구독 → 메시지 전송
client.agent.onEvent(({ event }) => {
if (event.type === "assistant_delta") process.stdout.write(event.delta as string);
if (event.type === "tool_use") console.log("\n[tool]", event.message);
if (event.type === "complete") console.log("\n[done]", event.data);
});
await client.agent.subscribe(sessionId);
await client.agent.send(sessionId, "List the files in this directory.");이것이 전체 루프입니다. 토큰은 assistant_delta로 전달되고, 툴 호출은
tool_use로 전달됩니다. 턴이 끝나면 complete 이벤트와 함께 사용량과
비용 정보를 받을 수 있습니다.
단발 호출만 필요한 경우
세션이나 이벤트 구독 없이 답변 하나만 필요하다면 completion()을 사용합니다.
import { completion } from "@sna-sdk/core";
const result = await completion({
prompt: "Summarize: ...",
provider: "claude-code",
model: "claude-haiku-4-5",
onDelta: (chunk) => process.stdout.write(chunk),
});
// result.text, result.usage, result.costUsd, result.durationMs세 가지 스트리밍 방식(인프로세스 콜백, SSE, 전체 이벤트 스트림)은 Cookbook → Streaming 에서 비교합니다.