> ## 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

> Apps Script를 사용하여 Google Sheets에서 직접 TTS 생성을 자동화하세요 - 코딩 기술이 필요 없습니다!

<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://typecast.ai/developers/api/)

***

## 설정 가이드

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

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

<Frame caption="TTS 생성 준비가 된 샘플 데이터가 있는 스프레드시트">
  <img src="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/02-sheet-with-data.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=ef97b1cef7eed93d74fa28a3341195f7" alt="Text, Voice, Language, Audio URL 열이 있는 Google Sheet" width="1440" height="900" data-path="images/02-sheet-with-data.png" />
</Frame>

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

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

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

<Frame caption="확장 프로그램 메뉴에서 Apps Script 열기">
  <img src="https://mintcdn.com/neosapienceinc/Q56oN2E_8sKrF22V/images/03-extensions-menu.png?fit=max&auto=format&n=Q56oN2E_8sKrF22V&q=85&s=d638a3d76953f1461d7e8121b5935076" alt="Apps Script 옵션을 보여주는 확장 프로그램 메뉴" width="2448" height="1408" data-path="images/03-extensions-menu.png" />
</Frame>

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

<Frame caption="기본 코드가 있는 Apps Script 편집기">
  <img src="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/04-apps-script-editor.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=5a76f549bc6073d15066416e162d0dee" alt="Apps Script 코드 편집기 인터페이스" width="1440" height="900" data-path="images/04-apps-script-editor.png" />
</Frame>

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

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

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

<AccordionGroup>
  <Accordion title="📋 전체 Apps Script 코드 보기 클릭" icon="code">
    ```javascript theme={null}
    // 타입캐스트 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/v2/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 voice = voices.find((v) => v.voice_name.toLowerCase() === searchName);

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

      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="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/07-apps-script-code-top.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=f66a0f22936604eadd0eccab2ed409df" alt="편집기의 완전한 Apps Script 코드" width="1440" height="900" data-path="images/07-apps-script-code-top.png" />
</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://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="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/08-custom-menu.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=2dc0a87a699b2749e0d6973ec7c59b2a" alt="모든 오디오 생성, 선택한 행만 생성, 오디오 URL 지우기 옵션을 보여주는 사용자 정의 메뉴" width="1079" height="900" data-path="images/08-custom-menu.png" />
</Frame>

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

<Frame caption="오디오 생성을 위해 사용자 정의 메뉴 열기">
  <img src="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/12-menu-open.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=5faa6b3bd7467dc57b2061a521437477" alt="모든 사용 가능한 옵션을 보여주는 열린 타입캐스트 TTS 메뉴" width="1089" height="900" data-path="images/12-menu-open.png" />
</Frame>

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

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

***

## 사용법

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

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

<Frame caption="스크립트 실행 중 - 텍스트 처리 중">
  <img src="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/13-running-script.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=b4db6997c25d1dbb5a94d5981450ce5e" alt="생성 진행 중을 보여주는 실행 중인 스크립트 알림" width="1088" height="900" data-path="images/13-running-script.png" />
</Frame>

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

<Frame caption="생성이 성공적으로 완료됨">
  <img src="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/14-generation-complete.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=406bf896a3288f916dee9707b40245fb" alt="3개의 오디오 파일이 생성되고 0개의 오류를 보여주는 성공 대화상자" width="1086" height="900" data-path="images/14-generation-complete.png" />
</Frame>

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

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

<Frame caption="Google Drive URL이 있는 최종 결과">
  <img src="https://mintcdn.com/neosapienceinc/Z2SKC4k1AnG1xbkd/images/15-final-result-with-urls.png?fit=max&auto=format&n=Z2SKC4k1AnG1xbkd&q=85&s=d6411e648678d807aabe9720595cf2e1" alt="스마트 이모션이 활성화된 상태로 D열에 생성된 Google Drive URL을 보여주는 스프레드시트" width="1440" height="900" data-path="images/15-final-result-with-urls.png" />
</Frame>

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

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

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

### 음성 URL 지우기

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

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

***

## 고급 설정

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

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

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

### 감정 프리셋 사용

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

```javascript theme={null}
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 모델(기본 사용)로 **37개 언어**를 지원합니다:

| 코드    | 언어     | 코드    | 언어     | 코드    | 언어     |
| ----- | ------ | ----- | ------ | ----- | ------ |
| `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 theme={null}
// 이 줄을:
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://typecast.ai/developers/api/)에서 API 키 확인 - 키 주변에 여분의 공백이 없는지 확인
  </Accordion>

  <Accordion title="API 오류: 402 - 크레딧 부족">
    * [타입캐스트 API 콘솔](https://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://typecast.ai/developers/api/voices">
    500개 이상의 사용 가능한 음성 둘러보기
  </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://typecast.ai/developers/api/">
    타입캐스트 API 키 받기
  </Card>
</CardGroup>
