> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreweave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 동기화 대 비동기 패턴

> CoreWeave 샌드박스 Python SDK에서 동기화 및 비동기 패턴 중에서 선택하세요.

이 예시에서는 `CWSANDBOX_API_KEY`에 설정된 CoreWeave API 액세스 토큰을 사용합니다. Forge API 키 인증은 [시작하기](/ko/products/sandboxes/get-started)를 참조하세요. CKS 요구 사항은 [배치 모드](/ko/products/sandboxes/placement)를 참조하세요.

이 가이드에서는 CoreWeave 샌드박스 Python SDK(`cwsandbox`)에서 동기화 및 비동기 패턴 중에서 선택하는 방법을 도와주며, 각 일반적인 오퍼레이션의 동기화 및 비동기 형식을 보여줍니다.

SDK는 내부적으로 단일 비동기 구현을 가지고 있습니다. 동기화/비동기 유연성은 결과를 소비하는 방법에서 비롯됩니다: 동기화에는 `.result()`를 사용하고 비동기에는 `await`를 사용하세요.

<h2 id="quick-decision-guide">
  빠른 결정 가이드
</h2>

| 사용 사례 | 패턴 | 이유 |
| - | - | - |
| 대부분의 오퍼레이션 | Sync | 코드가 더 단순하고 asyncio 보일러플레이트가 필요 없음 |
| 병렬 실행 | Sync | 오퍼레이션이 기본적으로 non-blocking으로 설계됨 |
| Jupyter 노트북 | Sync | `nest_asyncio`가 필요 없음 |
| Async 코드베이스 | Async | 기존 async 코드와 통합됨 |

**기본 원칙**: 단순하게 작성하려면 sync 패턴(`.result()`)을 사용하세요. 이미
async 코드베이스에서 작업 중인 경우에만 `await`으로 전환하세요.

<h2 id="core-concept-operationref">
  핵심 개념: OperationRef
</h2>

대부분의 SDK 메서드는 `.result()` 호출과 await가 모두 가능한 래퍼인 `OperationRef[T]`를 반환합니다.
두 방식 모두 동일한 예외를 발생시킵니다.

```python theme={"system"}
ref = sandbox.read_file("/output/data.txt")

# Sync: 값을 블로킹
data = ref.result()

# Async: 대신 await
data = await ref
```

`Sandbox.exec()`는 `OperationRef[ProcessResult]`를 확장한 `Process`를 스트림과
stdin을 지원하도록 반환합니다.

<Warning>
  비동기 컨텍스트에서는 절대로 `.result()`를 사용하지 마세요. `.result()`를 호출하면 스레드가 차단됩니다. 비동기 컨텍스트에서는 이벤트 루프가 차단되어
  애플리케이션에 교착 상태가 발생할 수 있습니다. 대신 `await`를 사용하세요.
</Warning>

<h2 id="auto-start-behavior">
  자동 시작 동작
</h2>

`session.sandbox()`는 시작되지 않은 샌드박스를 반환합니다. 샌드박스는 이를 필요로 하는 첫 번째 오퍼레이션에서 자동으로 시작됩니다:

**자동 시작을 트리거합니다**: `exec()`, `read_file()`, `write_file()`, `wait()`, `wait_until_complete()`

**자동 시작하지 않습니다**: `get_status()` (`SandboxNotRunningError` 예외 발생), `stop()` (시작된 적이 없으면 no-op)

<h2 id="operations">
  오퍼레이션
</h2>

<h3 id="create-sandboxes">
  샌드박스 생성
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    from cwsandbox import Sandbox

    # 컨텍스트 매니저 (권장)
    with Sandbox.run() as sb:
        result = sb.exec(["echo", "hello"]).result()
    # 종료 시 자동 중단
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    from cwsandbox import Sandbox

    # RUNNING 상태로 전환하기 위해 생성 + await, 그 후 정리용 async 컨텍스트 매니저
    sb = Sandbox()
    async with sb as sb:
        result = await sb.exec(["echo", "hello"])
    # 종료 시 자동 중단
    ```
  </Tab>
</Tabs>

<Note>
  `Sandbox.run()`은 내부적으로 `start().result()`를 호출하며, 이는 이벤트 루프를 차단합니다.
  비동기 코드에서는 `Sandbox(...)`로 생성한 후 `async with` 또는 `await sandbox`를 사용하여
  `RUNNING` 상태에 도달하세요.
</Note>

<h3 id="start">
  start()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sb = Sandbox()
    sb.start().result()  # backend가 수락할 때까지 블록
    print(sb.sandbox_id)
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    sb = Sandbox()
    await sb.start()  # backend가 수락할 때까지 대기
    print(sb.sandbox_id)
    ```
  </Tab>
</Tabs>

<h3 id="exec">
  exec()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    result = sb.exec(["echo", "hello"]).result()
    print(result.stdout)  # "hello\n"

    # 0이 아닌 종료 코드에서 예외 발생
    result = sb.exec(["python", "-c", "exit(1)"], check=True).result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    result = await sb.exec(["echo", "hello"])
    print(result.stdout)  # "hello\n"

    # 0이 아닌 종료 코드에서 예외 발생
    result = await sb.exec(["python", "-c", "exit(1)"], check=True)
    ```
  </Tab>
</Tabs>

<h3 id="read_file">
  read\_file()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    data = sb.read_file("/output/result.txt").result()
    print(data.decode())
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    data = await sb.read_file("/output/result.txt")
    print(data.decode())
    ```
  </Tab>
</Tabs>

<h3 id="write_file">
  write\_file()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sb.write_file("/input/data.txt", b"content").result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    await sb.write_file("/input/data.txt", b"content")
    ```
  </Tab>
</Tabs>

<h3 id="stop">
  stop()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sb.stop().result()

    # 이미 삭제된 경우 무시
    sb.stop(missing_ok=True).result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    await sb.stop()

    # 이미 삭제된 경우 무시
    await sb.stop(missing_ok=True)
    ```
  </Tab>
</Tabs>

<h3 id="wait">
  wait()
</h3>

`wait()`는 샌드박스가 `RUNNING` 상태에 도달할 때까지 실행을 차단합니다. 실행 차단이 목적이므로
동기 방식으로만 사용할 수 있습니다. 비동기 코드에서는 `await sandbox`가 같은 역할을 합니다.

```python theme={"system"}
# 동기: RUNNING 상태가 될 때까지 차단
sb = Sandbox.run().wait()
result = sb.exec(["echo", "ready"]).result()
```

```python theme={"system"}
# 비동기: 샌드박스를 직접 await
sb = Sandbox()
await sb  # RUNNING 상태가 될 때까지 대기
result = await sb.exec(["echo", "ready"])
```

<h3 id="wait_until_complete">
  wait\_until\_complete()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sb = Sandbox.run("python", "-c", "print('done')")
    sb.wait_until_complete().result()
    print(f"Exit code: {sb.returncode}")

    # 예외를 발생시키지 않고 외부에서 종료된 샌드박스 처리
    sb.wait_until_complete(raise_on_termination=False).result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    sb = Sandbox(command="python", args=["-c", "print('done')"])
    await sb.wait_until_complete()
    print(f"Exit code: {sb.returncode}")

    # 예외를 발생시키지 않고 외부에서 종료된 샌드박스 처리
    await sb.wait_until_complete(raise_on_termination=False)
    ```
  </Tab>
</Tabs>

<h3 id="get_status">
  get\_status()
</h3>

`get_status()`는 동기 방식으로만 사용할 수 있습니다. API에서 현재 상태를 조회합니다.

```python theme={"system"}
status = sb.get_status()
print(f"Sandbox is {status}")  # e.g. SandboxStatus.RUNNING
```

<h3 id="sandboxlist">
  Sandbox.list()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sandboxes = Sandbox.list(tags=["my-job"]).result()
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    sandboxes = await Sandbox.list(tags=["my-job"])
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>
</Tabs>

<h3 id="sandboxfrom_id">
  Sandbox.from\_id()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sb = Sandbox.from_id("sandbox-abc123").result()
    result = sb.exec(["echo", "reconnected"]).result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    sb = await Sandbox.from_id("sandbox-abc123")
    result = await sb.exec(["echo", "reconnected"])
    ```
  </Tab>
</Tabs>

<h3 id="sandboxdelete">
  Sandbox.delete()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    Sandbox.delete("sandbox-abc123").result()

    # 이미 삭제된 경우 무시
    Sandbox.delete("sandbox-abc123", missing_ok=True).result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    await Sandbox.delete("sandbox-abc123")

    # 이미 삭제된 경우 무시
    await Sandbox.delete("sandbox-abc123", missing_ok=True)
    ```
  </Tab>
</Tabs>

<h3 id="sessionlist">
  session.list()
</h3>

<Tabs>
  <Tab title="동기">
    ```python theme={"system"}
    sandboxes = session.list().result()
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>

  <Tab title="비동기">
    ```python theme={"system"}
    sandboxes = await session.list()
    for sb in sandboxes:
        print(f"{sb.sandbox_id}: {sb.status}")
    ```
  </Tab>
</Tabs>

<h3 id="sessionfrom_id">
  session.from\_id()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    sb = session.from_id("sandbox-abc123").result()
    result = sb.exec(["echo", "adopted"]).result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    sb = await session.from_id("sandbox-abc123")
    result = await sb.exec(["echo", "adopted"])
    ```
  </Tab>
</Tabs>

<h3 id="stream-stdout">
  스트림 stdout
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    process = sb.exec(["python", "-c", "import time; [print(i) or time.sleep(0.1) for i in range(5)]"])
    for line in process.stdout:
        print(line, end="")
    result = process.result()
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    process = sb.exec(["python", "-c", "import time; [print(i) or time.sleep(0.1) for i in range(5)]"])
    async for line in process.stdout:
        print(line, end="")
    result = await process
    ```
  </Tab>
</Tabs>

<h3 id="stream-stdin">
  stdin 스트림
</h3>

`stdin=True`로 stdin을 활성화합니다. 원시 바이트를 쓸 때는 `write()`를, 텍스트 줄을 쓸 때는 `writeline()`을 사용하고,
EOF를 알리려면 `close()`를 호출하세요.

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    process = sb.exec(["cat"], stdin=True)
    process.stdin.write(b"hello ").result()
    process.stdin.writeline("world").result()
    process.stdin.close().result()  # 프로세스에 EOF를 알립니다
    result = process.result()
    print(result.stdout)  # "hello world\n"
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    process = sb.exec(["cat"], stdin=True)
    await process.stdin.write(b"hello ")
    await process.stdin.writeline("world")
    await process.stdin.close()  # 프로세스에 EOF를 알립니다
    result = await process
    print(result.stdout)  # "hello world\n"
    ```
  </Tab>
</Tabs>

<h3 id="cwsandboxresults">
  cwsandbox.results()
</h3>

`cwsandbox.results()`는 동기 방식으로만 사용할 수 있는 배치 헬퍼입니다. 하나 이상의 `OperationRef` 객체에 `.result()`를 호출합니다.

```python theme={"system"}
import cwsandbox

# 단일 ref
data = cwsandbox.results(sandbox.read_file("/path"))

# 여러 Ref
all_data = cwsandbox.results([sb.read_file(f) for f in files])
```

<h3 id="cwsandboxwait">
  cwsandbox.wait()
</h3>

`cwsandbox.wait()`는 동기 방식으로만 사용할 수 있습니다. `Sandbox`, `OperationRef` 또는 `Process`
객체 시퀀스를 기다리고 `(done, pending)`을 반환합니다. 샌드박스는 완료 시점이 아니라
`RUNNING` 상태에 도달할 때 대기가 해제됩니다.

```python theme={"system"}
import cwsandbox

# 모든 샌드박스가 RUNNING 상태가 될 때까지 대기
sandboxes = [Sandbox.run() for _ in range(5)]
done, pending = cwsandbox.wait(sandboxes)

# 처음 2개의 오퍼레이션이 완료될 때까지 대기
refs = [sb.read_file(f) for f in files]
done, pending = cwsandbox.wait(refs, num_returns=2)

# timeout을 설정하여 대기
done, pending = cwsandbox.wait(procs, timeout=30.0)
```

<h3 id="sessionfunction">
  @session.function()
</h3>

<Tabs>
  <Tab title="Sync">
    ```python theme={"system"}
    with Session(defaults) as session:
        @session.function()
        def compute(x: int, y: int) -> int:
            return x + y

        result = compute.remote(2, 3).result()
        print(result)  # 5

        # 병렬 맵
        refs = compute.map([(1, 2), (3, 4), (5, 6)])
        results = [r.result() for r in refs]  # [3, 7, 11]
    ```
  </Tab>

  <Tab title="Async">
    ```python theme={"system"}
    async with Session(defaults) as session:
        @session.function()
        def compute(x: int, y: int) -> int:
            return x + y

        result = await compute.remote(2, 3)
        print(result)  # 5

        # 병렬 맵
        refs = compute.map([(1, 2), (3, 4), (5, 6)])
        results = [await r for r in refs]  # [3, 7, 11]
    ```
  </Tab>
</Tabs>

<h2 id="parallel-execution">
  병렬 실행
</h2>

sync API는 오퍼레이션이 설계상 **non-blocking**이기 때문에 병렬 실행을 지원합니다.
`exec()`, `read_file()`, `write_file()` 같은 메서드는 즉시 반환됩니다. `.result()`를 호출할 때만
블록됩니다.

`Sandbox.run()`은 backend가 start request를 수락할 때까지 블록합니다(내부적으로 `start().result()`를
호출합니다). 병렬 시작을 위해 `session.sandbox()`(첫 오퍼레이션 시 자동 시작)를 사용하거나
`start()` Ref를 수집하세요:

```python theme={"system"}
from cwsandbox import Sandbox, Session, SandboxDefaults

# 옵션 1: session.sandbox() - 샌드박스가 첫 exec()에서 자동 시작됩니다
with Session(SandboxDefaults()) as session:
    sandboxes = [session.sandbox() for _ in range(3)]
    processes = [sb.exec(["echo", f"sb-{i}"]) for i, sb in enumerate(sandboxes)]
    results = [p.result() for p in processes]

# 옵션 2: Sandbox() + start() Ref 수집
sandboxes = [Sandbox() for _ in range(3)]
start_refs = [sb.start() for sb in sandboxes]
for ref in start_refs:
    ref.result()  # 모든 시작이 병렬로 진행됩니다

processes = [sb.exec(["echo", f"sb-{i}"]) for i, sb in enumerate(sandboxes)]
results = [p.result() for p in processes]
for sb in sandboxes:
    sb.stop().result()
```

<h2 id="jupyter-notebooks">
  Jupyter 노트북
</h2>

SDK가 데몬 스레드에서 자체 백그라운드 이벤트
루프를 실행하므로 sync API는 `nest_asyncio` 없이도 Jupyter에서 작동합니다.

```python theme={"system"}
# 셀 1 - 샌드박스 생성
from cwsandbox import Sandbox
sandbox = Sandbox.run()
sandbox.wait()  # RUNNING 상태가 될 때까지 대기

# 셀 2 - 명령어 실행
result = sandbox.exec(["python", "-c", "print(1+1)"]).result()
print(result.stdout)

# 셀 3 - 정리
sandbox.stop().result()
```

Jupyter에는 이벤트 루프가 기본으로 내장되어 있으므로, Jupyter에서 async를 사용할 때는 `await`를 바로 쓸 수 있습니다.

```python theme={"system"}
# Jupyter에서는 asyncio.run() 없이도 동작합니다
sandboxes = await Sandbox.list(tags=["notebook"])
```

<h2 id="error-handling">
  오류 처리
</h2>

동기화 경로와 비동기 경로는 모두 동일한 예외를 발생시킵니다. `.result()`는 기본 오퍼레이션에서 발생한 모든 예외를
다시 발생시키며, `await`도 마찬가지입니다. 전체 예외 계층 구조와 복구 패턴은 [일반적인 샌드박스 SDK 오류 메시지는 무엇을 의미하나요?](/support/sandboxes/articles/what-do-common-sandbox-sdk-errors-mean)를
참조하세요.
