복사해서 붙여넣는 반복 작업 하나만 정해서 자동화해 보세요. 이 글은 시트 A열의 문의 내용을 읽고 B열에 분류 결과를 채우는 가장 작은 예제입니다.
결론부터
1. 자동화할 일은 입력과 출력이 한 줄씩 정해진 작업으로 시작하세요.
2. API 키는 코드나 시트에 적지 말고 스크립트 속성에 저장하세요.
3. 처음에는 5줄만 돌려 보고, 결과를 직접 확인한 뒤 범위를 넓히세요.
만들 것
A열에 고객 문의가 있고, B열에 “배송”, “환불”, “계정”, “기타” 중 하나가 자동으로 들어갑니다. 분류는 기준이 명확하고 틀려도 사람이 쉽게 고칠 수 있어 첫 자동화로 적합합니다.
준비
- 사용할 AI 서비스에서 API 키를 발급받습니다. 요금과 무료 한도는 해당 서비스 페이지에서 확인하세요.
- 시트에서 확장 프로그램 → Apps Script를 엽니다.
- 프로젝트 설정의 스크립트 속성에
GEMINI_API_KEY라는 이름으로 키를 저장합니다. - 아래 코드를 붙여넣고
MODEL_NAME을 해당 서비스 문서에서 확인한 모델 이름으로 바꿉니다.
코드
아래는 실제 Gemini API를 호출해 6줄을 분류시켜 보고 고친 코드입니다. 처음 버전에서는 모델의 영어 설명문이 분류 칸에 그대로 들어오는 문제가 있어서, 생각 기능을 끄고 결과가 허용된 분류어가 아니면 오류로 처리하게 바꿨습니다. 다른 서비스를 쓰면 주소와 요청·응답 구조가 다르므로 해당 서비스 문서를 따르세요. 배치 처리, 중복 실행 방지, 매일 실행은 2편에서 다룹니다.
/**
* @OnlyCurrentDoc
*/
const SHEET_NAME = '시트1';
const LABELS = ['배송', '환불', '교환', '결제', '기타'];
function classify_(text) {
const key = PropertiesService.getScriptProperties().getProperty('GEMINI_API_KEY');
if (!key) throw new Error('NO_KEY: 스크립트 속성에 GEMINI_API_KEY가 없습니다');
const url = 'https://generativelanguage.googleapis.com/v1beta/models/MODEL_NAME:generateContent';
const payload = {
contents: [{ parts: [{ text:
'다음 고객 문의를 배송, 환불, 교환, 결제, 기타 중 하나로만 분류하세요. 단어 하나만 답하세요.\n\n' + text }] }],
generationConfig: { temperature: 0, thinkingConfig: { thinkingBudget: 0 } }
};
const res = UrlFetchApp.fetch(url, {
method: 'post', contentType: 'application/json',
headers: { 'x-goog-api-key': key },
payload: JSON.stringify(payload), muteHttpExceptions: true
});
const code = res.getResponseCode();
if (code !== 200) throw new Error('HTTP ' + code + ': ' + res.getContentText().slice(0, 150));
const json = JSON.parse(res.getContentText());
if (!json.candidates || !json.candidates[0].content) {
throw new Error('NO_CANDIDATE: ' + res.getContentText().slice(0, 150));
}
const raw = json.candidates[0].content.parts
.filter(function (p) { return !p.thought; })
.map(function (p) { return p.text || ''; })
.join('').trim();
if (LABELS.indexOf(raw) < 0) throw new Error('BAD_OUTPUT: ' + raw.slice(0, 60));
return raw;
}
// A열 문의를 읽어 B열이 빈 줄만 분류합니다. 처음에는 5줄만 넣고 실행하세요.
function classifyRows() {
const sheet = SpreadsheetApp.getActive().getSheetByName(SHEET_NAME);
const last = sheet.getLastRow();
for (let row = 2; row <= last; row++) {
if (sheet.getRange(row, 2).getValue() !== '') continue; // 이미 채워진 줄은 건너뜀
const text = sheet.getRange(row, 1).getValue();
if (!text) continue;
try {
sheet.getRange(row, 2).setValue(classify_(text));
} catch (e) {
sheet.getRange(row, 2).setValue('ERROR: ' + e.message);
}
}
}
처음 실행할 때
- 시트에 문의 5줄만 넣고
classifyRows를 실행합니다. 처음에는 권한 승인 창이 뜹니다. - B열 결과를 눈으로 확인합니다. 기준 밖의 단어가 나오면 프롬프트에 “목록에 없으면 기타”를 추가하세요.
- 오류가 나면 B열에 오류 코드가 적힙니다. 키, 모델 이름, 한도를 순서대로 확인하세요.
- 문제없으면 줄 수를 늘립니다. 실행 시간 제한이 있으므로 수백 줄이 넘으면 나눠서 돌려야 합니다.
안전하게 쓰려면
- 개인정보와 회사 기밀이 든 시트는 보내기 전에 서비스의 데이터 이용 정책을 확인하세요.
- API 키가 들어 있는 스크립트를 공유하지 마세요.
- 결과는 사람이 검토하기 전까지 “초안”으로 취급하세요.
- 비용이 걱정되면 한 번에 처리할 줄 수를 제한하고 사용량 화면을 확인하세요.
다음에 바꿔 볼 것
- 분류 대신 요약, 번역, 핵심 숫자 추출로 바꾸기
- 결과와 함께 “이유 한 줄”을 C열에 받기
- 시간 기반 트리거로 매일 아침 새 줄만 처리하기
다음 단계

답글 남기기