구글시트 데이터를 Apps Script로 외부 API에 보내고 응답 코드를 확인하는 흐름입니다.
UrlFetchApp 오류는 무엇부터 확인해야 할까
Google Apps Script에서 UrlFetchApp.fetch()로 외부 API, 웹훅, n8n, Make, Notion, Slack 같은 서비스에 데이터를 보낼 때 오류가 난다면 먼저 응답 코드를 확인해야 합니다. 대부분의 원인은 URL 오타, 인증 정보 누락, headers 형식, payload 변환 방식, 호출 제한 중 하나로 좁혀집니다.
대표적인 오류 메시지는 아래처럼 나타납니다. 메시지 안의 returned code 숫자가 원인 분류의 출발점입니다.
Exception: Request failed for https://example.com returned code 401.
Truncated server response: {"error":"Unauthorized"}
(use muteHttpExceptions option to examine full response)
먼저 볼 순서
401은 인증, 403은 권한, 404는 URL, 429는 호출 제한, 500은 상대 서버 문제일 가능성이 큽니다. 처음부터 코드를 모두 고치기보다 응답 코드와 응답 본문을 보고 한 단계씩 좁히는 편이 빠릅니다.
자주 나오는 오류 메시지 원문
UrlFetchApp 오류는 같은 Request failed for 문장으로 시작해도 뒤의 응답 코드에 따라 처리 방향이 달라집니다. 아래 메시지는 외부 API 호출 자동화에서 자주 만나는 형태입니다.
| 오류 메시지 | 먼저 의심할 부분 |
|---|---|
returned code 401 |
API 키, Bearer token, 인증 헤더 |
returned code 403 |
접근 권한, 허용 도메인, 앱 권한, IP 제한 |
returned code 404 |
API URL, endpoint, 경로 오타 |
returned code 429 |
외부 API rate limit, 반복 호출, 트리거 간격 |
returned code 500 |
상대 서버 내부 오류, 일시 장애, 잘못된 요청 처리 |
DNS error 또는 Address unavailable |
도메인 오류, 비공개 주소, 사내망 주소, 네트워크 접근 제한 |
응답 코드별 원인 정리
응답 코드는 외부 API가 Apps Script 요청을 어떻게 해석했는지 알려주는 신호입니다. 같은 코드라도 서비스마다 세부 메시지는 다를 수 있으므로 muteHttpExceptions: true로 응답 본문을 함께 확인하는 것이 좋습니다.
401, 403, 404, 429, 500 오류는 확인해야 할 위치가 다릅니다.
| 응답 코드 | 주요 원인 | 처리 방향 |
|---|---|---|
| 400 | 요청 형식 오류 | 필수 값, JSON 구조, 날짜 형식, 파라미터 이름을 확인합니다. |
| 401 | 인증 실패 | API 키, 토큰, Authorization 헤더, Bearer 접두어를 확인합니다. |
| 403 | 권한 부족 또는 접근 차단 | API 사용 권한, 워크스페이스 권한, 허용 도메인, IP 제한을 확인합니다. |
| 404 | URL 또는 endpoint 오류 | 기본 URL, 버전 경로, 리소스 ID, 슬래시 위치를 확인합니다. |
| 429 | 호출 횟수 초과 | 반복문 호출, 시간 기반 트리거 간격, Apps Script quota, 외부 API rate limit을 나눠 봅니다. |
| 500 | 상대 서버 내부 오류 | 같은 요청을 잠시 후 다시 보내고, 요청 본문이 서버에서 처리 가능한 형식인지 확인합니다. |
빠른 점검 순서
구글시트 데이터를 외부 API로 보내는 자동화라면 아래 순서대로 확인하면 됩니다. 처음부터 토큰을 새로 만들기보다 요청이 실제로 어떤 모양으로 나가는지 먼저 봐야 합니다.
- API URL과 endpoint가 정확한지 확인합니다.
method가get인지post인지 확인합니다.headers에Authorization과Content-Type이 맞게 들어갔는지 확인합니다.- JSON 전송이면
payload를JSON.stringify()로 변환했는지 확인합니다. muteHttpExceptions: true를 넣고 응답 코드와 응답 본문을 확인합니다.- 429 오류라면 반복 호출, 트리거 간격, Apps Script quota, 외부 API rate limit을 함께 확인합니다.
주의할 점
401과 403은 비슷해 보이지만 처리 방향이 다릅니다. 401은 인증 정보가 틀리거나 누락된 경우가 많고, 403은 인증 후에도 해당 리소스에 접근할 권한이 없을 때 자주 나타납니다.
GET 요청 기본 예제
GET 요청은 데이터를 조회할 때 주로 사용합니다. 먼저 muteHttpExceptions: true를 넣고 응답 코드와 본문을 기록하면 오류 원인을 보기 쉽습니다.
function testGetRequest() {
const url = 'https://example.com/api/items';
const response = UrlFetchApp.fetch(url, {
method: 'get',
muteHttpExceptions: true
});
Logger.log(response.getResponseCode());
Logger.log(response.getContentText());
}
getResponseCode()가 200이면 요청이 정상 처리된 것입니다. 401, 403, 404가 나온다면 코드 로직보다 URL, 인증 정보, 접근 권한을 먼저 확인하는 편이 좋습니다.
POST 요청 기본 예제
POST 요청은 구글시트의 행 데이터를 외부 API, 웹훅, Slack, Notion, n8n, Make 같은 서비스로 보낼 때 자주 사용합니다. JSON으로 보내려면 contentType: 'application/json'과 JSON.stringify(payload) 조합을 확인해야 합니다.
function testPostRequest() {
const url = 'https://example.com/api/items';
const payload = {
name: 'sample',
status: 'new'
};
const options = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(payload),
muteHttpExceptions: true,
headers: {
Authorization: 'Bearer YOUR_API_TOKEN'
}
};
const response = UrlFetchApp.fetch(url, options);
Logger.log(response.getResponseCode());
Logger.log(response.getContentText());
}
Authorization: 'Bearer YOUR_API_TOKEN'에서 실제 토큰을 코드에 그대로 공개하지 않도록 주의해야 합니다. 샘플 코드에서는 YOUR_API_TOKEN처럼 표시하고, 실제 운영에서는 별도 저장 방식을 사용하는 편이 안전합니다.
muteHttpExceptions로 실제 응답 본문 확인하기
muteHttpExceptions: true는 Apps Script가 400번대나 500번대 응답에서 바로 멈추지 않게 하고, 응답 코드와 응답 본문을 확인할 수 있게 해줍니다. 외부 API가 보내는 오류 메시지에는 잘못된 필드명, 권한 부족 사유, rate limit 안내가 들어 있는 경우가 많습니다.
function checkApiResponse() {
const url = 'https://example.com/api/items';
const response = UrlFetchApp.fetch(url, {
method: 'get',
muteHttpExceptions: true
});
const statusCode = response.getResponseCode();
const body = response.getContentText();
Logger.log('응답 코드: ' + statusCode);
Logger.log('응답 본문: ' + body);
}
이 설정은 원인 확인용으로 유용합니다. 다만 오류를 숨기는 용도가 아니므로, 운영 코드에서는 응답 코드가 200번대가 아닐 때 기록을 남기거나 재시도 여부를 판단하는 로직을 함께 두는 것이 좋습니다.
401, 403 오류 해결 순서
401과 403은 인증과 권한 문제에 가깝습니다. URL이나 payload를 고치기 전에 API 문서에서 인증 방식이 API key인지, Bearer token인지, Basic 인증인지 먼저 확인해야 합니다.
401 오류가 날 때
401은 API가 요청자를 확인하지 못했다는 의미로 보는 것이 자연스럽습니다. 토큰이 비어 있거나, 만료됐거나, Bearer 접두어가 빠졌거나, Authorization 헤더 이름이 잘못된 경우가 많습니다.
headers: {
Authorization: 'Bearer YOUR_API_TOKEN'
}
403 오류가 날 때
403은 인증 정보가 있더라도 해당 작업을 할 권한이 부족할 때 자주 나타납니다. Notion 데이터베이스 권한, Slack 앱 scope, 외부 API의 프로젝트 권한, 허용 도메인, IP 제한을 확인해야 합니다.
일부 API는 허용 IP 목록을 사용합니다. UrlFetchApp 요청은 사용자의 개인 PC가 아니라 Google 인프라를 통해 나가므로, 고정 IP 하나만 허용하는 방식과 맞지 않을 수 있습니다. 이런 경우에는 API 제공자의 허용 방식이나 중간 서버, 노코드 도구 연결 방식을 다시 검토해야 합니다.
429 오류 해결 순서
429는 호출이 너무 많다는 신호입니다. 이때는 Apps Script의 일일 호출 한도와 외부 API의 rate limit을 나눠서 봐야 합니다. 둘 중 어느 쪽 제한에 걸렸는지에 따라 해결 방법이 달라집니다.
| 점검 항목 | 확인 방법 | 처리 방향 |
|---|---|---|
| 반복문 안의 fetch | 행마다 API를 1회씩 호출하는지 확인 | 가능하면 여러 행을 한 번에 묶어 보냅니다. |
| 트리거 간격 | 1분마다 같은 API를 호출하는지 확인 | 5분, 10분, 1시간 단위로 조정합니다. |
| 외부 API 제한 | API 문서의 분당·시간당 제한 확인 | 재시도 간격과 전송량을 줄입니다. |
| Apps Script quota | Apps Script quotas 공식 문서 확인 | 호출 횟수 자체를 줄이는 구조로 바꿉니다. |
Utilities.sleep()은 짧은 간격 조절에는 도움이 될 수 있지만, 일일 호출 한도 자체를 늘려주지는 않습니다. 반복 호출이 많은 자동화라면 행마다 호출하는 구조보다 변경된 데이터만 모아서 보내는 구조가 더 안정적입니다.
API 키와 토큰 보관 주의점
API 키와 토큰은 코드에 직접 넣지 않는 편이 좋습니다. 구글시트를 공동 편집하거나 Apps Script 코드를 복사해 공유할 때 토큰이 함께 노출될 수 있기 때문입니다. GitHub 공개 저장소, 블로그 예제, 캡처 이미지에도 실제 키가 들어가지 않게 확인해야 합니다.
간단한 설정값은 PropertiesService를 사용해 스크립트 속성으로 분리할 수 있습니다. 아래 예시는 실제 토큰 값을 출력하지 않고 설정 여부만 확인하는 형태입니다.
function getApiToken() {
const token = PropertiesService.getScriptProperties().getProperty('API_TOKEN');
Logger.log(token ? '토큰이 설정되어 있습니다.' : '토큰이 없습니다.');
}
운영 코드에서는 토큰 전체를 Logger.log()로 출력하지 않는 것이 좋습니다. 실행 로그를 공유하거나 오류 화면을 캡처할 때 인증 정보가 드러날 수 있습니다.
외부 API 호출이 어려울 때 대안
UrlFetchApp으로 직접 API를 호출하기 어렵다면 n8n, Make, Zapier 같은 자동화 도구를 중간에 둘 수 있습니다. 구글시트에서 웹훅으로 데이터를 보내고, 이후 인증이 복잡한 API 연결은 자동화 도구에서 처리하는 방식입니다.
다만 노코드 도구를 사용해도 보안과 권한 확인은 그대로 필요합니다. API 키 보관, 연결 계정 권한, 웹훅 URL 노출, 호출 제한은 도구를 바꿔도 계속 점검해야 하는 항목입니다.
공식 자료로 더 확인하기
UrlFetchApp 오류는 외부 API 정책과 Apps Script 제한이 함께 걸릴 수 있습니다. 코드 수정 전 공식 문서에서 요청 옵션, 할당량, 속성 저장 방식을 확인하면 원인을 더 정확히 좁힐 수 있습니다.
fetch(), fetchAll(), 요청 옵션, HTTP 응답 처리 방식, 외부 요청 권한 범위를 확인할 수 있습니다.
URL Fetch 호출 수, 실행 시간, 트리거 실행 시간, Properties 읽기·쓰기 제한 등 자동화 운영에 필요한 한도를 확인할 수 있습니다.
Google Apps Script quotas 공식 문서에서 한도 확인하기API 토큰, 서버 주소, 사용자 설정값처럼 코드와 분리해 관리할 수 있는 key-value 저장 방식을 확인할 수 있습니다.
Google Apps Script PropertiesService 공식 문서에서 속성 저장 방식 확인하기함께 보면 좋은 글
FAQ
UrlFetchApp 오류는 응답 코드, 인증 정보, 요청 형식, 호출 제한을 순서대로 확인하면 대부분 원인을 좁힐 수 있습니다.
댓글