메인 콘텐츠로 건너뛰기
Google SheetsApps Script를 결합하면 스프레드시트에서 바로 텍스트 음성 변환 생성을 자동화할 수 있습니다. 배치 처리, 콘텐츠 자동화, 팀 워크플로우에 완벽합니다!

활용 방법

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

사전 준비 사항

시작하기 전에 다음을 준비하세요:
  1. Google 계정 - Google Sheets 액세스
  2. 타입캐스트 API 키여기서 받기

설정 가이드

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

  1. Google Sheets 열기
  2. 새 빈 스프레드시트 만들기
  3. 첫 번째 행에 헤더로 열 설정:
    • A열: Text - 음성으로 변환할 텍스트
    • B열: Voice - Voice ID (예: tc_66aca22c7d31e45ff05ff418) 또는 음성 라이브러리의 음성 이름
    • C열: Language - 언어 코드 (eng, kor, jpn, cmn 등)
    • D열: Audio URL - 비워두기 (자동으로 채워짐)
  4. 두 번째 행부터 데이터 추가 (1행은 헤더용)
Text, Voice, Language, Audio URL 열이 있는 Google Sheet
캐릭터 이름 (Arin 같은) 또는 voice ID (tc_66aca22c7d31e45ff05ff418 같은)를 사용할 수 있습니다. 스크립트가 이름에서 음성 ID를 자동으로 조회합니다. 캐릭터 라이브러리 또는 Voices API에서 사용 가능한 캐릭터와 이름을 찾으세요. 스크립트는 자연스러운 음성을 위해 기본적으로 스마트 감정ssfm-v30 모델을 사용합니다.

단계 2: Apps Script 편집기 열기

  1. 메뉴 바에서 확장 프로그램 클릭
  2. Apps Script 선택
Apps Script 옵션을 보여주는 확장 프로그램 메뉴
  1. Apps Script 편집기가 있는 새 탭이 열립니다
Apps Script 코드 편집기 인터페이스

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

중요: API 키 필수! 스크립트를 사용하기 전에 2번째 줄의 YOUR_API_KEY_HERE를 실제 타입캐스트 API 키로 반드시 교체해야 합니다. 타입캐스트 API 콘솔에서 API 키를 받으세요.
  1. 기본 myFunction() 코드 삭제
  2. 다음 코드를 복사하여 붙여넣기:
// Typecast 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;
}

/**
 * Typecast 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();
}
편집기의 완전한 Apps Script 코드
🔑 API 키를 잊지 마세요! 스크립트를 실행하기 전에 2번째 줄을 반드시 업데이트해야 합니다: 이것을: javascript const TYPECAST_API_KEY = 'YOUR_API_KEY_HERE'; 실제 API 키로: javascript const TYPECAST_API_KEY = 'your_actual_api_key_from_typecast'; 타입캐스트 API 콘솔에서 API 키를 받으세요.
  1. 저장 아이콘 (💾)을 클릭하거나 ⌘+S (Mac) / Ctrl+S (Windows)를 누르세요
  2. 프로젝트에 이름을 지정하세요 (예: “타입캐스트 TTS 통합”)

단계 4: 권한 승인

스크립트를 처음 실행할 때 Google이 권한을 요청합니다:
  1. Apps Script 탭을 닫고 스프레드시트로 돌아가기
  2. 페이지 새로고침 (F5 또는 ⌘+R)
  3. 메뉴 바에 새 메뉴 🎙️ 타입캐스트 TTS가 나타나야 합니다
모든 오디오 생성, 선택한 행만 생성, 오디오 URL 지우기 옵션을 보여주는 사용자 정의 메뉴
  1. 🎙️ 타입캐스트 TTS모든 오디오 생성 클릭
모든 사용 가능한 옵션을 보여주는 열린 타입캐스트 TTS 메뉴
  1. Google이 스크립트 승인을 요청합니다:
    • 계속 클릭
    • Google 계정 선택
    • 고급프로젝트 이름로 이동 (안전하지 않음) 클릭
    • 허용 클릭
“안전하지 않음” 경고는 사용자 정의 스크립트이기 때문에 나타납니다. 복사한 코드를 신뢰한다면 진행해도 안전합니다.

사용법

모든 행에 대해 음성 생성

  1. 🎙️ 타입캐스트 TTS모든 오디오 생성 클릭
  2. 스크립트가 텍스트와 음성 ID가 있는 모든 행을 처리합니다
생성 진행 중을 보여주는 실행 중인 스크립트 알림
  1. 완료되면 성공 메시지가 표시됩니다
3개의 오디오 파일이 생성되고 0개의 오류를 보여주는 성공 대화상자
  1. D열에 오디오 URL이 나타납니다
  2. 생성된 오디오 파일이 Google Drive에 저장됩니다
스마트 감정이 기본적으로 활성화되어 있습니다! 스크립트는 자연스럽고 감정적으로 적절한 음성을 위해 ssfm-v30 모델과 함께 emotion_type: 'smart'를 사용합니다.
스마트 감정이 활성화된 상태로 D열에 생성된 Google Drive URL을 보여주는 스프레드시트

선택한 행에 대해 음성 생성

  1. 처리할 행 선택 (행 번호를 클릭하고 드래그)
  2. 🎙️ 타입캐스트 TTS선택한 행만 생성 클릭
  3. 선택한 행만 처리됩니다
특정 오디오 파일을 재생성하거나 점진적으로 새 행을 추가할 때 “선택한 행만 생성”을 사용하세요.

음성 URL 지우기

모든 생성된 URL을 제거하려면 (Drive에서 오디오 파일은 삭제되지 않음):
  1. 🎙️ 타입캐스트 TTS오디오 URL 지우기 클릭
  2. 작업 확인
  3. D열의 모든 URL이 지워집니다

고급 설정

스마트 이모션 (기본값)

스크립트는 기본적으로 스마트 이모션을 사용하며, 텍스트를 자동으로 분석하여 적절한 감정을 적용합니다:
prompt: {
  emotion_type: "smart"; // 텍스트에 가장 적합한 감정을 자동으로 감지
}

감정 프리셋 사용

감정을 수동으로 제어하려면 emotion_typepreset으로 변경하세요:
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열의 값을 변경하면 됩니다! 언어 코드는 대소문자를 구분하지 않습니다 (ENGeng 모두 작동).

특정 Drive 폴더에 저장

오디오 파일을 루트 대신 특정 폴더에 저장하려면:
// 이 줄을:
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를 사용해야 하는 이유

코딩 불필요

간단한 복사-붙여넣기 설정. 개발자가 아니어도 단 5분 만에 전문적인 보이스오버를 쉽게 생성할 수 있습니다.

자동화에 최적

한 번 설정하면 영구적으로 사용 가능. 반복적인 TTS 작업과 매크로 스타일 워크플로우에 이상적입니다.

배치 처리

한 번의 클릭으로 수백 개의 오디오 파일을 생성합니다. 전체 콘텐츠 캘린더를 한 번에 처리할 수 있습니다.

팀 협업

팀과 스프레드시트를 공유하세요. 모두가 텍스트를 관리하고 함께 오디오를 생성할 수 있습니다.
타입캐스트 API는 간단합니다. 몇 줄의 코드와 Google Sheets만으로 전체 TTS 워크플로우를 자동화할 수 있습니다. 기술적 전문 지식 없이 대규모로 오디오를 생성해야 하는 콘텐츠 크리에이터, 마케터, 교육자에게 적합합니다.

활용 사례

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

문제 해결

  • Apps Script 편집기에서 스크립트를 저장했는지 확인 - onOpen 함수를 수동으로 실행해 보기: 1. Apps Script 편집기로 돌아가기 2. 함수 드롭다운에서 onOpen 선택 3. 실행 버튼 (▶️) 클릭 - 브라우저 콘솔에서 오류 확인 (F12)
  • 프롬프트가 나타났을 때 허용을 클릭했는지 확인 - 권한을 지우고 다시 권한 승인 시도: 1. Apps Script 편집기 → 실행 → 권한 지우기 2. 저장하고 닫기 3. 스프레드시트 새로고침 4. 메뉴 다시 시도
  • YOUR_API_KEY_HERE를 실제 API 키로 교체했는지 확인 - 타입캐스트 API 콘솔에서 API 키 확인 - 키 주변에 여분의 공백이 없는지 확인
  • “선택한 행만 생성”을 사용하여 더 작은 배치로 처리 - Apps Script는 6분 실행 제한이 있음 - 매우 큰 데이터셋(500+ 행)은 여러 시트로 분할
  • Google Drive 루트 폴더 확인 - 오디오 파일 이름은 typecast_[timestamp].mp3입니다 - 스크립트에 Drive 권한이 있는지 확인 (올바르게 승인됨)

참고 자료

캐릭터 라이브러리

500개 이상의 사용 가능한 음성 둘러보기

API 레퍼런스

타입캐스트 API 탐색하기

Apps Script 문서

Google Apps Script에 대해 더 알아보기

API 키 받기

타입캐스트 API 키 받기