실제 출력을 만드는 것 실행하기

실제 출력을 만드는 것 실행하기 (Run something that produces real output)

애플리케이션이 명령 출력을 받는 방식을 선택하는 방법을 알아볼게요.

출처: 문서

본문

애플리케이션이 명령 출력을 받는 방식을 선택해요. 캡처된 출력(captured output)은 짧은 명령에 편리하고, 스트리밍은 완료를 기다리지 않고 진행 상황을 표시하거나 출력을 처리할 수 있게 해줘요.

첫 샌드박스에서 얻은 실행 중인 샌드박스 핸들로 시작해요. 명령은 인자 배열로 전달해요.

결과 모으기 (Collect the result)

프로세스를 실행하고 결과를 기다려요. 표준 출력, 표준 오류, 종료 코드를 확인해요. 애플리케이션 데드라인(deadline)을 사용해 명령이 요청을 무기한 붙잡지 않게 하세요.

이 방식은 출력을 대신 모아 줘요. 출력이 클 수 있거나 사용자에게 진행 상황이 필요하다면 스트리밍을 선호해요.

TypeScript

return sandbox.processes.run({ args }, { timeoutMs: 300_000 });

전체 TypeScript 예시: longrun/choose.ts

import type { Sandbox } from '@docker/sandboxes';

export async function collectOutput(sandbox: Sandbox, args: string[]) {
  return sandbox.processes.run({ args }, { timeoutMs: 300_000 });
}

출력 도착 시 스트리밍 (Stream output as it arrives)

프로세스를 시작하고, 연결하고, 출력 이벤트를 소비해요. 예시는 각 출력 청크를 콜백으로 전달하고 exit 이벤트에서 종료 코드를 반환해요.

콜백이 각 청크를 어떻게 처리할지 결정해요. 표시하거나, 파일에 추가하거나, 클라이언트로 보낼 수 있죠. 민감한 출력을 무분별하게 로그로 남기지 마세요.

항상 연결을 닫아요. exit 이벤트 전에 스트림이 끝난 것은 불완전한 관찰이지 성공의 증거가 아니에요. 중복 명령을 바로 시작하는 대신 프로세스 이름을 유지해 다시 연결하세요.

TypeScript 프로세스 핸들로 열린 연결은 출력을 소비하는 동안 일시적 연결 끊김 이후 자동으로 이어져요. 마지막으로 전달된 청크 이후부터 이어받고, 복구가 30초를 넘기면 멈춰요. 프로세스 입력은 재생되지 않아요. 연결되면 기본 수명 제한이 없지만, 여러분이 준 타임아웃이 전체 세션을 제한해요. Raw 스트림은 명시적으로 다시 연결해야 해요.

TypeScript

const process = await sandbox.processes.start(
  { args },
  { idempotencyKey: requestId },
);
const connection = await process.connect();
try {
  for await (const event of connection) {
    if (event.type === 'chunk') write(event.data ?? new Uint8Array());
    if (event.type === 'exited') return event.exitCode ?? 0;
  }
  throw new Error(
    `Output of ${process.name} ended before the command exited`,
  );
} finally {
  await connection.close();
}

전체 TypeScript 예시: longrun/stream.ts

import type { Sandbox } from '@docker/sandboxes';

export async function streamOutput(
  sandbox: Sandbox,
  args: string[],
  requestId: string,
  write: (bytes: Uint8Array) => void,
) {
  const process = await sandbox.processes.start(
    { args },
    { idempotencyKey: requestId },
  );
  const connection = await process.connect();
  try {
    for await (const event of connection) {
      if (event.type === 'chunk') write(event.data ?? new Uint8Array());
      if (event.type === 'exited') return event.exitCode ?? 0;
    }
    throw new Error(
      `Output of ${process.name} ended before the command exited`,
    );
  } finally {
    await connection.close();
  }
}

더 알아보기 (Learn more)

전체 레시피 목록은 Browse all recipes 에서 확인할 수 있어요.