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

# 보이스 검색과 미리듣기

> 앱 안에 보이스 선택기를 만들어 보이스 라이브러리를 검색하고, 샘플 오디오를 렌더링한 뒤, 사용자가 보이스 ID를 선택하도록 합니다.

<Note>
  이 문서는 영어 원문을 기반으로 자동 번역되었습니다. 표현이 어색하거나 모호한 부분이 있을 수 있으니, 정확한 내용은 [영어 원문](/en/docs/examples/voice-search-and-preview)을 함께 확인해 주세요.
</Note>

제품에서 사용자가 보이스를 직접 선택할 수 있도록 하려면, 검색 가능한 목록과 오디오 미리듣기를 함께 보여주는 것이 좋습니다. Supertone API는 이를 직접 지원합니다. `search_voices`는 필터에 맞는 보이스를 미리 렌더링된 샘플 클립과 함께 반환하므로, 브라우저에서 바로 재생할 수 있습니다.

## Python — 필터링하고 샘플 출력하기

```python theme={"dark"}
import os
from supertone import Supertone

with Supertone(api_key=os.environ["SUPERTONE_API_KEY"]) as client:
    result = client.voices.search_voices(
        language="en,ko",
        style="happy,neutral",
        gender="female",
        age="young-adult",
        page_size=20,
    )

    for voice in result.items or []:
        sample = next(
            (s for s in (voice.samples or []) if s.language == "en"),
            None,
        )
        print(f"{voice.voice_id}\t{voice.name}\t{sample.url if sample else '(no en sample)'}")
```

## TypeScript — 선택기 렌더링하기

```typescript theme={"dark"}
import { Supertone } from "@supertone/supertone";

const client = new Supertone({ apiKey: process.env.SUPERTONE_API_KEY });

async function loadVoices() {
  return client.voices.searchVoices({
    language: "en,ko",
    style: "happy,neutral",
    gender: "female",
    age: "young-adult",
    pageSize: 20,
  });
}

const result = await loadVoices();

for (const voice of result.items ?? []) {
  const sample = voice.samples?.find((s) => s.language === "en");
  console.log(
    voice.voiceId,
    voice.name,
    sample?.url ?? "(no en sample)",
  );
}
```

## 브라우저에서 선택기 렌더링하기

웹 UI에 보이스를 보여줄 때는 백엔드에서 목록을 가져온 뒤(브라우저에서 직접 API Key로 Supertone API를 호출하지 마세요), 샘플 URL을 `<audio>` 요소로 렌더링하세요.

```html theme={"dark"}
<!-- After your backend returns the search result as JSON -->
<ul id="voice-picker"></ul>

<script>
  async function renderVoices() {
    const res = await fetch("/api/voices?language=en&style=happy");
    const { items } = await res.json();
    const ul = document.getElementById("voice-picker");

    for (const voice of items) {
      const sample = voice.samples?.find((s) => s.language === "en");
      const li = document.createElement("li");
      li.innerHTML = `
        <button data-voice-id="${voice.voiceId}">${voice.name}</button>
        ${sample ? `<audio controls src="${sample.url}"></audio>` : ""}
      `;
      ul.appendChild(li);
    }
  }

  renderVoices();
</script>
```

팁: `samples` 필드는 `(language, style, model)` 조합을 키로 사용합니다. 앱이 실제로 사용하는 조합과 일치하는 샘플을 골라야, 미리듣기와 운영 환경의 출력이 일치합니다.

## 페이지네이션

`search_voices`는 최대 `page_size`개의 보이스(기본값 20, 최대 100)와, 더 많은 결과가 있다면 `next_page_token`을 함께 반환합니다. 다음 페이지를 가져오려면 이 토큰을 다시 전달하세요.

<Tabs>
  <Tab title="Python">
    ```python theme={"dark"}
    page = client.voices.search_voices(language="en", page_size=50)
    while True:
        for voice in page.items or []:
            handle(voice)
        if not page.next_page_token:
            break
        page = client.voices.search_voices(
            language="en",
            page_size=50,
            next_page_token=page.next_page_token,
        )
    ```
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"dark"}
    let page = await client.voices.searchVoices({ language: "en", pageSize: 50 });
    while (true) {
      for (const voice of page.items ?? []) {
        handle(voice);
      }
      if (!page.nextPageToken) break;
      page = await client.voices.searchVoices({
        language: "en",
        pageSize: 50,
        nextPageToken: page.nextPageToken,
      });
    }
    ```
  </Tab>
</Tabs>

## 팁

* **여러 값은 OR로 결합됩니다.** `language=ko,en`은 둘 중 **하나라도** 지원하는 보이스를 반환합니다. AND 방식으로 좁히고 싶다면 여러 필터를 함께 사용하세요.
* **`sort` 파라미터는 없습니다.** 결과는 기본 순서로 반환됩니다. 별도의 정렬이 필요하다면 클라이언트 측에서 정렬하세요.
* **응답을 캐시하세요.** 보이스 라이브러리는 자주 바뀌지 않습니다. 백엔드에서 목록을 캐시해 두면(15\~60분 정도가 적당합니다) 호출 수를 줄이고 선택기의 지연시간도 개선할 수 있습니다.

## 관련 문서

<CardGroup cols={2}>
  <Card title="보이스" icon="users" href="/ko/docs/core-concepts/voices">
    보이스 ID와 보이스 객체에 대한 개념적 개요입니다.
  </Card>

  <Card title="Search voices" icon="magnifying-glass" href="/ko/api-reference/endpoints/search-voices">
    전체 필터 파라미터 레퍼런스입니다.
  </Card>
</CardGroup>
