−25%

Windows 연간 결제, 10월 31일까지. 요금제 보기

EQVPS
시작하기

5분 만에 시작하는 샌드박스 TypeScript SDK: npm i @eqvps/sdk부터 첫 실행까지

@eqvps/sdk를 설치하고 Node.js, Deno, Bun에서 Firecracker 샌드박스를 띄워 Python, Node, 셸 코드를 실행하고, 파일을 주고받고, 긴 작업의 출력을 실시간으로 봅니다. 모든 코드가 담긴 6단계입니다.

이 가이드는 JavaScript나 TypeScript에서 샌드박스를 실행합니다. 격리된 Firecracker microVM을 띄우고, 세 가지 언어로 코드를 실행하고, 파일을 주고받고, 긴 작업의 출력을 따라갑니다. 각 단계는 짧은 코드 조각입니다.

Node.js 18+(또는 Deno, Bun)와 EQVPS 계정이 필요합니다.

1. 토큰 받기

SDK는 일반 계정 토큰으로 인증합니다. 대시보드, API, MCP 서버에서 토큰을 받는 방법은 계정 연결에 있습니다.

export EQVPS_API_KEY="your-token"

잔액이 없는 새 계정은 처음 샌드박스를 만들 때 1달러의 샌드박스 크레딧을 받습니다. 이 가이드를 여러 번 돌려도 충분한 금액입니다.

2. SDK 설치

npm i @eqvps/sdk

예제는 ES 모듈과 최상위 await를 사용합니다. .mjs 파일로 저장하거나, .ts로 저장한 뒤 npx tsx로 실행하세요.

3. 샌드박스를 띄우고 코드 실행

import { Sandbox } from "@eqvps/sdk";

await Sandbox.with({ tariff: "small" }, async (sb) => {
  console.log(sb.id);
  const r = await sb.run("import platform; print(platform.python_version())");
  console.log(r.exit_code, r.stdout);
});

출력은 샌드박스 id(sb_와 24자), 그다음 0 3.12.x입니다. Sandbox.with는 콜백이 끝나면, 예외를 던진 경우에도 샌드박스를 삭제합니다.

기본 언어는 Python입니다. 나머지 둘은 옵션 하나면 됩니다.

await Sandbox.with({}, async (sb) => {
  const js = await sb.run("console.log(process.version)", { language: "node" });
  const sh = await sb.run("uname -r && nproc", { language: "bash" });
  console.log(js.stdout, sh.stdout);
});

실패한 스크립트는 예외를 던지지 않습니다. r.ok를 확인하세요. exit_code가 0이고 타임아웃에 걸리지 않았으면 true입니다.

4. 셸 명령과 패키지

exec는 셸 문자열이나 argv 배열을 받습니다. 샌드박스는 인터넷에 접속할 수 있으므로 npm과 pip 설치도 됩니다.

await Sandbox.with({ tariff: "small" }, async (sb) => {
  await sb.exec("mkdir -p /root/app && cd /root/app && npm init -y && npm i lodash", { timeout: 55 });
  const r = await sb.exec(["node", "-e", "console.log(require('/root/app/node_modules/lodash').VERSION)"]);
  console.log(r.stdout);
});

동기 호출은 최대 55초입니다. 그보다 오래 걸리면 백그라운드 작업으로 보냅니다(6단계).

5. 파일

await Sandbox.with({}, async (sb) => {
  await sb.upload("/root/input.json", JSON.stringify({ values: [3, 5, 8] }));
  await sb.run("import json; d = json.load(open('/root/input.json')); open('/root/out.txt', 'w').write(str(sum(d['values'])))");
  console.log(await sb.downloadText("/root/out.txt"));   // 16
});

upload는 문자열이나 Uint8Array를 받습니다. download는 Uint8Array를, downloadText는 문자열을 반환합니다. 한 번 전송할 수 있는 크기는 5 MB까지입니다.

6. 실시간 출력이 있는 긴 작업

await Sandbox.with({ tariff: "standard" }, async (sb) => {
  const task = await sb.exec("for i in 1 2 3 4 5; do echo step $i; sleep 20; done", { background: true });
  const res = await task.wait({ onOutput: (out) => process.stdout.write(out) });
  console.log(res.state, res.exit_code);   // done 0
});

작업은 바로 반환되며 55초 제한이 없습니다. wait는 기본적으로 2초마다 확인합니다(pollIntervalMs로 변경 가능). task.kill()로 멈추고, sb.task(id)로 다른 프로세스에서 다시 연결할 수 있습니다.

오류

import { Sandbox, InsufficientBalanceError, RateLimitError } from "@eqvps/sdk";

try {
  await Sandbox.with({}, async (sb) => console.log((await sb.run("print(1)")).stdout));
} catch (e) {
  if (e instanceof InsufficientBalanceError) console.log("Top up the balance");
  else if (e instanceof RateLimitError) console.log("Busy, retry in", e.retryAfter);
  else throw e;
}

서버가 Retry-After를 보내면 SDK가 429와 503을 최대 3번까지 스스로 재시도합니다. 가장 흔한 429는 한 계정에서 명령을 세 개 이상 동시에 실행할 때 생깁니다. 모든 클래스는 SDK 레퍼런스에 있습니다.

이 가이드의 비용

각 예제는 몇 초만 존재했고 최소 60초로 과금되었습니다. small에서 0.00055달러, standard에서 0.0011달러입니다. 6단계의 작업은 standard에서 약 100초 동안 돌아 약 0.002달러가 들었습니다. 전부 합쳐도 1센트에 한참 못 미칩니다. 전체 요금표는 샌드박스 한도와 과금에 있습니다.

다음 단계

자주 묻는 질문

@eqvps/sdk는 어떤 런타임을 지원하나요?

Node.js 18 이상, Deno, Bun입니다. 의존성이 없고 내장 fetch를 쓰므로 브라우저에서도 동작하지만, 계정 토큰을 프런트엔드 코드에 넣지는 마세요.

앱이 TypeScript로 되어 있어도 샌드박스에서 Python을 돌릴 수 있나요?

네. 앱의 언어와 샌드박스 안 코드의 언어는 서로 관계가 없습니다. run의 language에는 python, node, bash를 쓸 수 있고, exec는 어떤 셸 명령이든 실행합니다.

샌드박스가 반드시 삭제되게 하려면 어떻게 하나요?

Sandbox.with(options, fn)을 쓰세요. fn이 끝나거나 예외를 던지면 샌드박스를 삭제합니다. TypeScript 5.2 이상이라면 await using sb = await Sandbox.create()라고 쓸 수도 있습니다.

코드가 실패하면 run은 무엇을 반환하나요?

0이 아닌 exit_code와 stderr에 오류가 담긴 ExecResult를 반환합니다. 예외는 던지지 않습니다. 예외는 토큰이 잘못됐거나 잔액이 없는 것 같은 API 오류에서만 발생합니다.

댓글

아직 댓글이 없습니다. 첫 번째가 되세요.

댓글 남기기

댓글은 표시되기 전에 검토됩니다.