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

# Google Sheets

<Info>
  [Google Sheets](https://sheets.google.com/)와 [Apps Script](https://script.google.com/)를 결합하면 스프레드시트에서 바로 텍스트 음성 변환 생성을 자동화할 수 있습니다. 배치 처리, 콘텐츠 자동화, 팀 워크플로우에 완벽합니다!
</Info>

## 활용 방법

타입캐스트 API와 Google Sheets를 사용하면 다음을 할 수 있습니다:

- **배치 TTS 생성** - 한 번의 클릭으로 여러 텍스트를 음성으로 변환
- **워크플로우 자동화** - 데이터를 처리하고 자동으로 오디오 생성
- **코딩 불필요** - 간단한 사용자 정의 메뉴 인터페이스 사용
- **팀 협업** - 팀원들과 시트를 공유하여 협업 오디오 제작
- **Drive에 자동 저장** - 생성된 오디오 파일이 Google Drive에 자동 저장

---

## 사전 준비 사항

시작하기 전에 다음을 준비하세요:

1. **Google 계정** - Google Sheets 액세스
2. **타입캐스트 API 키** - [여기서 받기](https://studio.typecast.ai/developers/api/)

---

## 설정 가이드

### 단계 1: 스프레드시트 만들기

1. [Google Sheets](https://sheets.google.com/) 열기
2. 새 빈 스프레드시트 만들기
3. **첫 번째 행**에 헤더로 열 설정:
   - **A열**: `Text` - 음성으로 변환할 텍스트
   - **B열**: `Voice` - Voice ID (예: `tc_66aca22c7d31e45ff05ff418`) 또는 [음성 라이브러리](https://studio.typecast.ai/developers/api/voices)의 음성 이름
   - **C열**: `Language` - 언어 코드 (`eng`, `kor`, `jpn`, `cmn` 등)
   - **D열**: `Audio URL` - 비워두기 (자동으로 채워짐)
4. **두 번째 행**부터 데이터 추가 (1행은 헤더용)

<Frame caption="TTS 생성 준비가 된 샘플 데이터가 있는 스프레드시트">
  <img
    src="/images/02-sheet-with-data.webp"
    alt="Text, Voice, Language, Audio URL 열이 있는 Google Sheet"
  />
</Frame>

<Tip>
  **보이스 이름** (`Arin` 같은) 또는 **voice ID** (`tc_66aca22c7d31e45ff05ff418` 같은)를 사용할 수 있습니다. 스크립트가 이름에서 음성 ID를 자동으로 조회합니다. **[보이스 라이브러리](https://studio.typecast.ai/developers/api/voices)** 또는 [Voices API](https://studio.typecast.ai/developers/api/voices)에서 사용 가능한 보이스와 이름을 찾으세요. 스크립트는 자연스러운 음성을 위해 기본적으로 **스마트 이모션**과 **ssfm-v30 모델**을 사용합니다.
</Tip>

### 단계 2: Apps Script 편집기 열기

1. 메뉴 바에서 **확장 프로그램** 클릭
2. **Apps Script** 선택

<Frame caption="확장 프로그램 메뉴에서 Apps Script 열기">
  <img
    src="/images/03-extensions-menu.webp"
    alt="Apps Script 옵션을 보여주는 확장 프로그램 메뉴"
  />
</Frame>

3. Apps Script 편집기가 있는 새 탭이 열립니다

<Frame caption="기본 코드가 있는 Apps Script 편집기">
  <img
    src="/images/04-apps-script-editor.webp"
    alt="Apps Script 코드 편집기 인터페이스"
  />
</Frame>

### 단계 3: 타입캐스트 연동 코드 추가

<Warning>
  **중요: API 키 필수!** 스크립트를 사용하기 전에 2번째 줄의 `YOUR_API_KEY_HERE`를 실제 타입캐스트 API 키로 반드시 교체해야 합니다. [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키를 받으세요.
</Warning>

1. 기본 `myFunction()` 코드 삭제
2. 다음 코드를 복사하여 붙여넣기:

<AccordionGroup>
  <Accordion title="📋 전체 Apps Script 코드 보기 클릭" icon="code">

```javascript
// 타입캐스트 API 구성
const TYPECAST_API_KEY = "YOUR_API_KEY_HERE"; // 실제 API 키로 교체
const TYPECAST_API_URL = "https://api.typecast.ai/v1/text-to-speech";

/**
 * 스프레드시트가 열릴 때 사용자 정의 메뉴 생성
 */
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu("🎙️ Typecast TTS")
    .addItem("모든 오디오 생성", "generateAllAudio")
    .addItem("선택한 행만 생성", "generateSelectedAudio")
    .addSeparator()
    .addItem("오디오 URL 지우기", "clearAudioUrls")
    .addToUi();
}

/**
 * 텍스트가 있는 모든 행에 대해 오디오 생성
 */
function generateAllAudio() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const lastRow = sheet.getLastRow();

  if (lastRow < 2) {
    SpreadsheetApp.getUi().alert("처리할 데이터가 없습니다!");
    return;
  }

  // 더 나은 성능을 위해 모든 데이터를 한 번에 가져오기
  const dataRange = sheet.getRange(2, 1, lastRow - 1, 4);
  const data = dataRange.getValues();

  let successCount = 0;
  let errorCount = 0;

  // 각 행 처리
  data.forEach((row, index) => {
    const text = row[0];
    const voiceNameOrId = row[1];
    const language = row[2] || "kor"; // 지정되지 않으면 기본값 한국어
    const rowNumber = index + 2;

    // 텍스트 또는 음성 이름/ID가 비어있으면 건너뛰기
    if (!text || !voiceNameOrId) {
      return;
    }

    // 오디오 URL이 이미 있으면 건너뛰기
    if (row[3]) {
      return;
    }

    try {
      const audioUrl = callTypecastAPI(text, voiceNameOrId, language);
      sheet.getRange(rowNumber, 4).setValue(audioUrl);
      successCount++;

      // 요청 제한을 피하기 위해 약간의 지연 추가
      Utilities.sleep(500);
    } catch (error) {
      sheet.getRange(rowNumber, 4).setValue("오류: " + error.message);
      errorCount++;
    }
  });

  SpreadsheetApp.getUi().alert(
    `생성 완료!\n\n성공: ${successCount}\n오류: ${errorCount}`,
  );
}

/**
 * 선택한 행에 대해서만 오디오 생성
 */
function generateSelectedAudio() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const selection = sheet.getActiveRange();
  const startRow = selection.getRow();
  const numRows = selection.getNumRows();

  if (startRow === 1) {
    SpreadsheetApp.getUi().alert("데이터 행을 선택하세요 (헤더 제외)");
    return;
  }

  let successCount = 0;
  let errorCount = 0;

  for (let i = 0; i < numRows; i++) {
    const rowNumber = startRow + i;
    const text = sheet.getRange(rowNumber, 1).getValue();
    const voiceNameOrId = sheet.getRange(rowNumber, 2).getValue();
    const language = sheet.getRange(rowNumber, 3).getValue() || "kor";

    if (!text || !voiceNameOrId) {
      continue;
    }

    try {
      const audioUrl = callTypecastAPI(text, voiceNameOrId, language);
      sheet.getRange(rowNumber, 4).setValue(audioUrl);
      successCount++;

      Utilities.sleep(500);
    } catch (error) {
      sheet.getRange(rowNumber, 4).setValue("오류: " + error.message);
      errorCount++;
    }
  }

  SpreadsheetApp.getUi().alert(
    `생성 완료!\n\n성공: ${successCount}\n오류: ${errorCount}`,
  );
}

/**
 * D열의 모든 오디오 URL 지우기
 */
function clearAudioUrls() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const lastRow = sheet.getLastRow();

  if (lastRow < 2) {
    return;
  }

  const response = SpreadsheetApp.getUi().alert(
    "오디오 URL 지우기",
    "모든 오디오 URL을 지우시겠습니까?",
    SpreadsheetApp.getUi().ButtonSet.YES_NO,
  );

  if (response === SpreadsheetApp.getUi().Button.YES) {
    sheet.getRange(2, 4, lastRow - 1, 1).clearContent();
    SpreadsheetApp.getUi().alert("오디오 URL이 지워졌습니다!");
  }
}

/**
 * Voices API를 호출하여 음성 이름에서 음성 ID 가져오기
 * @param {string} voiceName - 조회할 음성 이름
 * @returns {string} 음성 ID (예: tc_66aca22c7d31e45ff05ff418)
 */
function getVoiceIdByName(voiceName) {
  const url = "https://api.typecast.ai/v3/voices";

  const options = {
    method: "get",
    headers: {
      "X-API-KEY": TYPECAST_API_KEY,
    },
    muteHttpExceptions: true,
  };

  const response = UrlFetchApp.fetch(url, options);
  const responseCode = response.getResponseCode();

  if (responseCode !== 200) {
    throw new Error(`음성 가져오기 실패: ${responseCode}`);
  }

  const voices = JSON.parse(response.getContentText());

  // 이름으로 음성 검색 (대소문자 구분 없음)
  const searchName = voiceName.toLowerCase().trim();
  const matches = voices.filter((v) =>
    [v.voice_name?.eng, v.voice_name?.kor].some(
      (name) => typeof name === "string" && name.toLowerCase().trim() === searchName,
    ),
  );
  if (matches.length > 1) {
    throw new Error("이름이 같은 보이스가 여러 개입니다. 보이스 ID를 사용하세요.");
  }
  const voice = matches[0];

  if (!voice) {
    throw new Error(
      `"${voiceName}" 음성을 찾을 수 없습니다. 음성 이름을 확인하거나 음성 ID를 대신 사용하세요.`,
    );
  }

  if (!voice.models?.some((model) => model.version === "ssfm-v30")) {
    throw new Error("이 보이스는 ssfm-v30을 지원하지 않습니다. ssfm-v30 보이스를 선택하세요.");
  }
  return voice.voice_id;
}

/**
 * 타입캐스트 API를 호출하여 음성 생성
 * @param {string} text - 음성으로 변환할 텍스트
 * @param {string} voiceNameOrId - 음성 이름 (예: "Emily") 또는 음성 ID (예: "tc_66aca22c7d31e45ff05ff418")
 * @param {string} language - 언어 코드 (eng, kor, jpn, cmn 등)
 * @returns {string} 생성된 오디오 파일의 URL
 */
function callTypecastAPI(text, voiceNameOrId, language) {
  // 입력값 정리 및 검증
  text = String(text).trim();
  voiceNameOrId = String(voiceNameOrId).trim();
  language = String(language || "kor").trim();

  if (!text || !voiceNameOrId) {
    throw new Error("텍스트와 음성 이름/ID가 필요합니다");
  }

  // 입력이 음성 ID인지 이름인지 결정
  // 음성 ID는 "tc_"로 시작
  let voiceId;
  if (voiceNameOrId.startsWith("tc_")) {
    voiceId = voiceNameOrId;
    Logger.log("음성 ID 사용: " + voiceId);
  } else {
    // 음성 이름이므로 ID 조회
    Logger.log("음성 이름 조회 중: " + voiceNameOrId);
    voiceId = getVoiceIdByName(voiceNameOrId);
    Logger.log("음성 ID 찾음: " + voiceId);
  }

  // 디버깅용 로그
  Logger.log("API 호출 중, 텍스트: " + text);
  Logger.log("언어: " + language);

  const payload = {
    voice_id: voiceId,
    text: text,
    model: "ssfm-v30",
    language: language,
    prompt: {
      emotion_type: "smart", // 자연스러운 음성을 위한 스마트 이모션 활성화
    },
    output: {
      audio_format: "mp3",
    },
  };

  // 디버깅용 페이로드 로그
  Logger.log("페이로드: " + JSON.stringify(payload));

  const options = {
    method: "post",
    contentType: "application/json",
    headers: {
      "X-API-KEY": TYPECAST_API_KEY,
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true,
  };

  const response = UrlFetchApp.fetch(TYPECAST_API_URL, options);
  const responseCode = response.getResponseCode();

  if (responseCode !== 200) {
    const errorBody = response.getContentText();
    Logger.log("오류 응답: " + errorBody);
    throw new Error(`API 오류: ${responseCode} - ${errorBody}`);
  }

  // 오디오 blob 가져오기
  const audioBlob = response.getBlob();

  // Google Drive에 업로드
  const folder = DriveApp.getRootFolder(); // 또는 특정 폴더 지정
  const fileName = `typecast_${new Date().getTime()}.mp3`;
  const file = folder.createFile(audioBlob.setName(fileName));

  // 링크로 접근 가능하도록 파일 설정
  file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);

  // 파일 URL 반환
  return file.getUrl();
}
```

  </Accordion>
</AccordionGroup>

<Frame caption="편집기에 추가된 Apps Script 코드">
  <img
    src="/images/07-apps-script-code-top.webp"
    alt="편집기의 완전한 Apps Script 코드"
  />
</Frame>

<Warning>
  **🔑 API 키를 잊지 마세요!** 스크립트를 실행하기 전에 2번째 줄을 반드시 업데이트해야 합니다: **이것을:** ```javascript const TYPECAST_API_KEY =
  'YOUR_API_KEY_HERE'; ``` **실제 API 키로:** ```javascript const
  TYPECAST_API_KEY = 'your_actual_api_key_from_typecast'; ``` [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키를 받으세요.
</Warning>

3. **저장** 아이콘 (💾)을 클릭하거나 `⌘+S` (Mac) / `Ctrl+S` (Windows)를 누르세요
4. 프로젝트에 이름을 지정하세요 (예: "타입캐스트 TTS 통합")

### 단계 4: 권한 승인

스크립트를 처음 실행할 때 Google이 권한을 요청합니다:

1. Apps Script 탭을 닫고 스프레드시트로 돌아가기
2. 페이지 새로고침 (F5 또는 `⌘+R`)
3. 메뉴 바에 새 메뉴 **🎙️ 타입캐스트 TTS**가 나타나야 합니다

<Frame caption="Google Sheets의 사용자 정의 타입캐스트 TTS 메뉴">
  <img
    src="/images/08-custom-menu.webp"
    alt="모든 오디오 생성, 선택한 행만 생성, 오디오 URL 지우기 옵션을 보여주는 사용자 정의 메뉴"
  />
</Frame>

4. **🎙️ 타입캐스트 TTS** → **모든 오디오 생성** 클릭

<Frame caption="오디오 생성을 위해 사용자 정의 메뉴 열기">
  <img
    src="/images/12-menu-open.webp"
    alt="모든 사용 가능한 옵션을 보여주는 열린 타입캐스트 TTS 메뉴"
  />
</Frame>

5. Google이 스크립트 승인을 요청합니다:
   - **계속** 클릭
   - Google 계정 선택
   - **고급** → **프로젝트 이름(으)로 이동 (안전하지 않음)** 클릭
   - **허용** 클릭

<Note>
  "안전하지 않음" 경고는 사용자 정의 스크립트이기 때문에 나타납니다. 복사한 코드를 신뢰한다면 진행해도 안전합니다.
</Note>

---

## 사용법

### 모든 행에 대해 음성 생성

1. **🎙️ 타입캐스트 TTS** → **모든 오디오 생성** 클릭
2. 스크립트가 텍스트와 음성 ID가 있는 모든 행을 처리합니다

<Frame caption="스크립트 실행 중 - 텍스트 처리 중">
  <img
    src="/images/13-running-script.webp"
    alt="생성 진행 중을 보여주는 실행 중인 스크립트 알림"
  />
</Frame>

3. 완료되면 성공 메시지가 표시됩니다

<Frame caption="생성이 성공적으로 완료됨">
  <img
    src="/images/14-generation-complete.webp"
    alt="3개의 오디오 파일이 생성되고 0개의 오류를 보여주는 성공 대화상자"
  />
</Frame>

4. D열에 오디오 URL이 나타납니다
5. 생성된 오디오 파일이 Google Drive에 저장됩니다

<Note>
  **스마트 이모션이 기본적으로 활성화되어 있습니다!** 스크립트는 자연스럽고 감정적으로 적절한 음성을 위해 `ssfm-v30` 모델과 함께 `emotion_type: 'smart'`를 사용합니다.
</Note>

<Frame caption="Google Drive URL이 있는 최종 결과">
  <img
    src="/images/15-final-result-with-urls.webp"
    alt="스마트 이모션이 활성화된 상태로 D열에 생성된 Google Drive URL을 보여주는 스프레드시트"
  />
</Frame>

### 선택한 행에 대해 음성 생성

1. 처리할 행 선택 (행 번호를 클릭하고 드래그)
2. **🎙️ 타입캐스트 TTS** → **선택한 행만 생성** 클릭
3. 선택한 행만 처리됩니다

<Tip>
  특정 오디오 파일을 재생성하거나 점진적으로 새 행을 추가할 때 "선택한 행만 생성"을 사용하세요.
</Tip>

### 음성 URL 지우기

모든 생성된 URL을 제거하려면 (Drive에서 오디오 파일은 삭제되지 않음):

1. **🎙️ 타입캐스트 TTS** → **오디오 URL 지우기** 클릭
2. 작업 확인
3. D열의 모든 URL이 지워집니다

---

## 고급 설정

### 스마트 이모션 (기본값)

스크립트는 기본적으로 **스마트 이모션**을 사용하며, 텍스트를 자동으로 분석하여 적절한 감정을 적용합니다:

```javascript
prompt: {
  emotion_type: "smart"; // 텍스트에 가장 적합한 감정을 자동으로 감지
}
```

### 감정 프리셋 사용

감정을 수동으로 제어하려면 `emotion_type`을 `preset`으로 변경하세요:

```javascript
function callTypecastAPI(text, voiceId, language) {
  const payload = {
    voice_id: voiceId,
    text: text,
    model: "ssfm-v30",
    language: language,
    prompt: {
      emotion_type: "preset",
      emotion_preset: "happy", // 옵션: happy, sad, angry, whisper, normal, toneup, 또는 tonedown.
      emotion_intensity: 1.0, // 0.0 ~ 1.0
    },
    output: {
      audio_format: "mp3",
      audio_tempo: 1.0, // 속도: 0.5 (느림) ~ 2.0 (빠름)
      audio_pitch: 0, // 피치: -12 ~ +12 반음
      volume: 100, // 볼륨: 0 ~ 200
    },
  };
  // ... 나머지 코드
}
```

### 지원 언어

스크립트는 ssfm-v30 모델(기본 사용)로 **35+개 언어**를 지원합니다:

| 코드  | 언어  | 코드  | 언어   | 코드  | 언어   |
| ----- | --------- | ----- | ---------- | ----- | ---------- |
| `ara` | 아랍어    | `ind` | 인도네시아어 | `por` | 포르투갈어 |
| `ben` | 벵골어   | `ita` | 이탈리아어    | `ron` | 루마니아어   |
| `bul` | 불가리아어 | `jpn` | 일본어   | `rus` | 러시아어    |
| `ces` | 체코어     | `kor` | 한국어     | `slk` | 슬로바키아어     |
| `dan` | 덴마크어    | `msa` | 말레이어      | `spa` | 스페인어    |
| `deu` | 독일어    | `nan` | 민난어    | `swe` | 스웨덴어    |
| `ell` | 그리스어     | `nld` | 네덜란드어      | `tam` | 타밀어      |
| `eng` | 영어   | `nor` | 노르웨이어  | `tgl` | 타갈로그어    |
| `fin` | 핀란드어   | `pan` | 펀자브어    | `tha` | 태국어       |
| `fra` | 프랑스어    | `pol` | 폴란드어     | `tur` | 터키어    |
| `hin` | 힌디어     | `ukr` | 우크라이나어  | `vie` | 베트남어 |
| `hrv` | 크로아티아어  | `yue` | 광둥어  | `zho` | 중국어    |
| `hun` | 헝가리어 |       |            |       |            |

다른 언어를 사용하려면 C열의 값을 변경하면 됩니다! 언어 코드는 대소문자를 구분하지 않습니다 (`ENG`와 `eng` 모두 작동).

### 특정 Drive 폴더에 저장

오디오 파일을 루트 대신 특정 폴더에 저장하려면:

```javascript
// 이 줄을:
const folder = DriveApp.getRootFolder();

// 이것으로 교체 (폴더 ID 사용):
const folder = DriveApp.getFolderById("YOUR_FOLDER_ID_HERE");

// 또는 새 폴더 생성:
const folder = DriveApp.createFolder("Typecast Audio Files");
```

### 추가 열 설정

더 세밀한 제어를 위해 추가 열로 스프레드시트를 확장할 수 있습니다:

- **E열**: 감정 프리셋 (happy, sad, angry, normal)
- **F열**: 오디오 템포 (0.5 ~ 2.0)
- **G열**: 오디오 피치 (-12 ~ +12)
- **H열**: 상태 (Processing, Done, Error)

그런 다음 스크립트를 수정하여 시트에서 이 값들을 읽도록 합니다.

---

## Google Sheets와 타입캐스트 API를 사용해야 하는 이유

<CardGroup cols={2}>
  <Card title="코딩 불필요" icon="wand-magic-sparkles">
    간단한 복사-붙여넣기 설정. 개발자가 아니어도 단 5분 만에 전문적인
    보이스오버를 쉽게 생성할 수 있습니다.
  </Card>
  <Card title="자동화에 최적" icon="bolt">
    한 번 설정하면 영구적으로 사용 가능. 반복적인 TTS 작업과
    매크로 스타일 워크플로우에 이상적입니다.
  </Card>
  <Card title="배치 처리" icon="layer-group">
    한 번의 클릭으로 수백 개의 오디오 파일을 생성합니다. 전체 콘텐츠
    캘린더를 한 번에 처리할 수 있습니다.
  </Card>
  <Card title="팀 협업" icon="users">
    팀과 스프레드시트를 공유하세요. 모두가 텍스트를 관리하고 함께 오디오를
    생성할 수 있습니다.
  </Card>
</CardGroup>

<Info>
  **타입캐스트 API는 간단합니다.** 몇 줄의 코드와
  Google Sheets만으로 전체 TTS 워크플로우를 자동화할 수 있습니다. 기술적 전문 지식 없이
  대규모로 오디오를 생성해야 하는 콘텐츠 크리에이터, 마케터, 교육자에게 적합합니다.
</Info>

---

## 활용 사례

<AccordionGroup>
  <Accordion title="교육 콘텐츠 제작">
    수업 스크립트에서 강의 내레이션을 만드세요. A열에 텍스트를 추가하고 오디오를
    생성한 다음 동영상에 사용할 수 있습니다.
  </Accordion>
  <Accordion title="팟캐스트 제작">
    공유 Google Sheet에서 인트로/아웃트로 세그먼트, 광고 읽기, 안내 멘트를
    생성하세요.
  </Accordion>
  <Accordion title="상품 설명">
    상품 설명을 접근성 향상이나 마케팅 동영상을 위한
    오디오로 변환하세요.
  </Accordion>
  <Accordion title="SNS 콘텐츠">
    콘텐츠 캘린더에서 Instagram Reels, TikTok, YouTube Shorts용
    보이스오버를 일괄 생성하세요.
  </Accordion>
  <Accordion title="다국어 콘텐츠">
    시트에서 텍스트를 번역하고 글로벌 사용자를 위해 여러 언어로
    오디오를 생성하세요.
  </Accordion>
</AccordionGroup>

---

## 문제 해결

<AccordionGroup>
  <Accordion title="새로고침 후 메뉴가 나타나지 않음">
    - Apps Script 편집기에서 스크립트를 저장했는지 확인 - `onOpen` 함수를 수동으로 실행해 보기: 1. Apps Script 편집기로 돌아가기 2. 함수 드롭다운에서 `onOpen` 선택 3. **실행** 버튼 (▶️) 클릭 - 브라우저 콘솔에서 오류 확인 (F12)
  </Accordion>
  <Accordion title="권한 오류">
    - 프롬프트가 나타났을 때 **허용**을 클릭했는지 확인 - 권한을 지우고 다시 권한 승인 시도: 1. Apps Script 편집기 → 실행 → 권한 지우기 2. 저장하고 닫기 3. 스프레드시트 새로고침 4. 메뉴 다시 시도
  </Accordion>
  <Accordion title="API 오류: 401 - 잘못된 API 키">
    - `YOUR_API_KEY_HERE`를 실제 API 키로 교체했는지 확인 - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/)에서 API 키 확인 - 키 주변에 여분의 공백이 없는지 확인
  </Accordion>
  <Accordion title="API 오류: 402 - 크레딧 부족">
    - [타입캐스트 API 콘솔](https://studio.typecast.ai/developers/api/usage)에서 크레딧 잔액 확인 - 크레딧은 글자 수에 기반하여 차감 - 충분한지 확인
  </Accordion>
  <Accordion title="대용량 데이터셋에서 스크립트 시간 초과">
    - "선택한 행만 생성"을 사용하여 더 작은 배치로 처리 - Apps Script는 6분 실행 제한이 있음 - 매우 큰 데이터셋(500+ 행)은 여러 시트로 분할
  </Accordion>
  <Accordion title="Drive에 오디오 파일이 나타나지 않음">
    - Google Drive 루트 폴더 확인 - 오디오 파일 이름은 `typecast_[timestamp].mp3`입니다 - 스크립트에 Drive 권한이 있는지 확인 (올바르게 승인됨)
  </Accordion>
</AccordionGroup>

---

## 참고 자료

<CardGroup cols={2}>
  <Card
    title="보이스 라이브러리"
    icon="microphone"
    href="https://studio.typecast.ai/developers/api/voices"
  >
    600+개 사용 가능한 음성 둘러보기
  </Card>
  <Card
    title="API 레퍼런스"
    icon="code"
    href="/ko/api-reference/text-to-speech/text-to-speech"
  >
    타입캐스트 API 탐색하기
  </Card>
  <Card
    title="Apps Script 문서"
    icon="google"
    href="https://developers.google.com/apps-script"
  >
    Google Apps Script에 대해 더 알아보기
  </Card>
  <Card
    title="API 키 받기"
    icon="key"
    href="https://studio.typecast.ai/developers/api/"
  >
    타입캐스트 API 키 받기
  </Card>
</CardGroup>
