GAS CORS ์ค๋ฅ ์ฐํ๋ฐฉ๋ฒ: Simple Request ์ ๋ต์ผ๋ก ์ธ๋ถ ์น ํต์ ํด๊ฒฐ
GAS๊ฐ OPTIONS ์์ฒญ์ ์ฒ๋ฆฌํ์ง ๋ชปํด ๋ฐ์ํ๋ 405ยทํค๋ ์ค๋ฅ์ ๊ตฌ์กฐ๋ฅผ ์ค๋ช ํ๊ณ , ์์ฒญ ๋ฐฉ์ ์ ํ๋ง์ผ๋ก ์ค๋ฅ๋ฅผ ์ฐํํด ๊ฐ๋ฐ ์์ ์ฑ์ ๋์ด๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
Google Apps Script(GAS) ํ๊ฒฝ์์ ๋ฐ์ํ๋ ๊ณ ์ง์ ์ธ GAS CORS ์ค๋ฅ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ ์ธ๋ถ ์น์ฌ์ดํธ์ ์์ ์ ์ธ ํต์ ์ ๊ตฌํํ๋ ๊ฐ์ฅ ํ์คํ ๋ฐฉ๋ฒ์ ๋๋ค. Simple Request ์ ๋ต์ ํ์ฉํ์ฌ GAS ํ๊ฒฝ์์ ๋ฐ์ํ๋ CORS ์ค๋ฅ๋ฅผ ์๋ฒฝํ ์ฐํํ๊ณ , ํด๋ผ์ด์ธํธ์ ์๋ฒ ์ฝ๋๋ฅผ ์ต์ ํํ๋ ๊ตฌ์ฒด์ ์ธ ํ์ ์์๋ด ๋๋ค.
CORS ์ค๋ฅ์ ๊ทผ๋ณธ ์์ธ๊ณผ HTTP ์์ฒญ ๋ฐฉ์ ๋ถ์
CORS ์ค๋ฅ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ๋ณด์์ ์ํด ๋ค๋ฅธ ๋๋ฉ์ธ์ ์๋ฒ๋ก ์์ฒญ์ ๋ง์ ๋ ๋ฐ์ํ๋ ๋ณด์ ์ ์ฑ ์๋ฐ ์ค๋ฅ์ ๋๋ค. ๋ธ๋ผ์ฐ์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก "๋ค๋ฅธ ์ฌ์ดํธ(B) ์๋ฒ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ฑฐ๋ ๊ฐ์ ธ์ค๋ ค๊ณ ํ๋ฉด", ํด๋น ์๋ฒ๊ฐ ์ ํํ ํ์ฉ ์ค์ ์ ํด์ฃผ์ง ์์ผ๋ฉด ์์ฒญ์ ์ฐจ๋จํฉ๋๋ค.
์๋๋ HTTP์์ ์๋ก ๋ค๋ฅธ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
1. GET ๋ฐฉ์: ์๋ฒ์์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ (Read)
GET์ ์๋ฒ์์ ์ ๋ณด๋ฅผ ์ฝ๊ธฐ(Read) ์ํด ์ฌ์ฉํ๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์์ฒญ ๋ฐฉ์์ ๋๋ค. URL ๋ค์ ?name=value ํํ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํฉ๋๋ค.
์ฅ์
- ์์ฒญ ๋ฐฉ์ ์ค ๊ฐ์ฅ ๋น ๋ฅด๊ณ ๋จ์ํจ, ์บ์ฑ์ด ๊ฐ๋ฅํด ๋ฐ๋ณต ์์ฒญ ์๋๊ฐ ๋นจ๋ผ์ง
- ๋ธ๋ผ์ฐ์ API ๋ฑ ๋ชจ๋ ํ๊ฒฝ์์ ์์ ์ ์ผ๋ก ๋์
๋จ์
- ๋ฐ์ดํฐ๊ฐ URL์ ๋ ธ์ถ๋ผ ๋ณด์์ด ์ฝํจ, ์ ์ก ๊ฐ๋ฅํ ๋ฐ์ดํฐ ๊ธธ์ด์ ์ ํ ์์
- ์๋ฒ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์์ ์๋ ์ ํฉํ์ง ์์
2. POST ๋ฐฉ์: ์๋ฒ์ ๋ฐ์ดํฐ ์ ์ก ๋ฐ ์ฒ๋ฆฌ ์์ฒญ (Create)
POST๋ ์๋ฒ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๊ณ ์์ฑ/์ฒ๋ฆฌ(Create) ์์ ์ ์ํํ ๋ ์ฌ์ฉํฉ๋๋ค. ๋ฐ์ดํฐ๋ body(๋ณธ๋ฌธ)์ ๋ด๊ธฐ ๋๋ฌธ์ ๋ง์ ์์ ์ ์กํ ์ ์์ต๋๋ค.
์ฅ์
- body ์ฌ์ฉ์ผ๋ก ๋ณด์์ฑ์ด GET๋ณด๋ค ๋์, ๋๋ ๋ฐ์ดํฐ ์ ์ก ๊ฐ๋ฅ
- ํ์๊ฐ์ , ๋ก๊ทธ์ธ, AI ํ๋กฌํํธ ๋ฑ ์ฒ๋ฆฌ ์์ ์ ์ต์ ํ
๋จ์
- ์บ์ฑ์ด ์ด๋ ค์, application/json ์ฌ์ฉ ์ Preflight(OPTIONS) ๋ฐ์์ผ๋ก CORS ์ค๋ฅ ์ํ
3. OPTIONS ์ฌ์ ์์ฒญ(Preflight) ์ฒดํฌ
OPTIONS๋ ์ค์ ์์ฒญ์ ๋ณด๋ด๊ธฐ ์ , ์ด ์์ฒญ์ ์ด ์๋ฒ๊ฐ ๋ฐ์์ค ์ ์๋์ง ํ์ธํ๊ธฐ ์ํ ์ฌ์ ์์ฒญ์ ๋๋ค. CORS์ ๋ฐ์ ํ๊ฒ ๊ด๋ จ๋ ๋ฐฉ์์ ๋๋ค.
๋จ์
- ์ค์ ์์ฒญ ์ ์ ํ ๋ฒ ๋ ํต์ ํ์ฌ ์๋ ์ ํ
- Google Apps Script(GAS)๋ OPTIONS๋ฅผ ์ง์ํ์ง ์์ 405 ์ค๋ฅ ๋ฐ์
4. PUT / PATCH / DELETE: ๋ฐ์ดํฐ ์์ ๋ฐ ์ญ์ (Update/Delete)
์ด ์์ฒญ๋ค์ ์๋ฒ์ ๋ฐ์ดํฐ๋ฅผ ์์ /์ญ์ (Update/Delete) ํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ REST API์์ ์์ฃผ ํ์ฉ๋ฉ๋๋ค.
๋จ์
- ๋๋ถ๋ถ Preflight(OPTIONS) ๋ฐ์, CORS ๋ฌธ์ ๊ฐ๋ฅ์ฑ ๋์
๊ฒฐ๋ก ์ ์ผ๋ก, Google Apps Script(GAS)์์ ๋ฐ์ํ๋GAS CORS ์ค๋ฅ๋ ์ธ๋ถ ์น์ฌ์ดํธ ํต์ ์ ๋ฐฉํดํ๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ์ฅ์ ์์ธ์ ๋๋ค.
GAS CORS ์ค๋ฅ ์ ํ ๋ฐ Simple Request ์ ๋ต ๋์
1. 405 Method Not Allowed ์ค๋ฅ
์ธ๋ถ ํด๋ผ์ด์ธํธ์์ POST ์์ฒญ ์ ๋ธ๋ผ์ฐ์ ๊ฐ ๋จผ์ OPTIONS ์์ฒญ์ ๋ณด๋ด์ง๋ง, GAS๋ ๋ณ๋์ OPTIONS ํธ๋ค๋ฌ๊ฐ ์์ด 405 ์ค๋ฅ๋ฅผ ๋ฐ์์ํค๊ณ ์ฐ๊ฒฐ์ด ์ข ๋ฃ๋ฉ๋๋ค.
| ์ํฉ | ์ฒ๋ฆฌ ๊ฒฐ๊ณผ | ์ํ |
|---|---|---|
| OPTIONS ์์ฒญ ์ฒ๋ฆฌ ์์ | 405 Method Not Allowed ๋ฐ์ | "๊ฒ๋ฌธ์ ์ฐจ๋จ" |
2. setHeader is not a function ์ค๋ฅ
GAS ์๋ต ๊ฐ์ฒด์ CORS ํ์ฉ ํค๋(Access-Control-Allow-Origin: *)๋ฅผ ์ถ๊ฐํ๋ ค ํด๋, ContentService.createTextOutput() ๊ฐ์ฒด์๋ `setHeader` ๋ฉ์๋๊ฐ ์ ๊ณต๋์ง ์์ GAS CORS ์ค๋ฅ๊ฐ ๋ฐ๋ณต๋ฉ๋๋ค.
- ์ค์ ์ํฉ: ContentService ๊ฐ์ฒด๋ ๋จ์ ํ ์คํธ ์ถ๋ ฅ๋ง ๊ฐ๋ฅํ๋ฉฐ ํค๋ ์กฐ์์ด ๋ถ๊ฐํฉ๋๋ค.

Simple Request ์ ๋ต์ผ๋ก CORS ์ค๋ฅ ์ฐํ
CORS ์ค๋ฅ๋ ๋๋ถ๋ถ ํ๋ฆฌํ๋ผ์ดํธ(Preflight)์์ ๋ฐ์ํฉ๋๋ค. Simple Request ๋ฐฉ์์ผ๋ก ์์ฒญํ๋ฉด ์ด Preflight ์์ฒด๊ฐ ๋ฐ์ํ์ง ์๊ฒ ๋์ด CORS ์ค๋ฅ๋ฅผ ์ฐํํ ์ ์์ต๋๋ค.
๋ ๊ฐ์ง ํต์ฌ GAS CORS ์ค๋ฅ๋ ๋ชจ๋ ํค๋ ์กฐ์ ๋ฌธ์ ์์ ๋น๋กฏ๋ฉ๋๋ค. ํด๊ฒฐ์ฑ ์ ๋ธ๋ผ์ฐ์ ๊ฐ Pre-flight ์์ฒญ์ ๋ณด๋ด์ง ์๋๋ก ํ๋ Simple Request ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค.
Simple Request ์กฐ๊ฑด
๋ค์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด GAS๋ ์์ฒญ์ Form Data๋ก ์ธ์ํ๊ณ , ์๋์ผ๋ก CORS ํ์ฉ ํค๋๋ฅผ ์๋ต์ ๋ถ์ฌ์ฃผ์ด GAS CORS ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์์ต๋๋ค.
| ํญ๋ชฉ | ํ์ ๊ฐ | ์ญํ |
|---|---|---|
| Method | POST (๋๋ GET/HEAD) | ์ง์ง ํต๊ณผ |
| Content-Type | application/x-www-form-urlencoded | ์ ํด์ง ์๋ฅ ์์ |
Simple Request๊ฐ ๋๊ธฐ ์ํ 3๊ฐ์ง ํ์ ์กฐ๊ฑด
์กฐ๊ฑด 1. ํ์ฉ๋ HTTP ๋ฉ์๋๋ง ์ฌ์ฉํด์ผ ํจ
- GET, HEAD, POST๋ง ํ์ฉ๋ฉ๋๋ค.
- PUT, PATCH, DELETE ์ฌ์ฉ ์ Simple Request๋ ๋ถ๊ฐํ๋ฉฐ Preflight๊ฐ ๋ฐ์ํฉ๋๋ค.
์กฐ๊ฑด 2. ์์ฒญ ํค๋๋ ์์ ํ ํค๋๋ง ์ฌ์ฉํด์ผ ํจ
๋ธ๋ผ์ฐ์ ๊ฐ ํ์ฉํ ๊ธฐ๋ณธ ํค๋๋ค๋ง ์ฌ์ฉํด์ผ ํฉ๋๋ค.
- Accept
- Accept-Language
- Content-Type (ํน์ ๊ฐ๋ง)
์กฐ๊ฑด 3. Content-Type์ ์๋ 3๊ฐ๋ง ๊ฐ๋ฅ
์๋ 3๊ฐ ์ธ ๋ค๋ฅธ ๊ฐ์ด๋ฉด 100% Preflight ๋ฐ์ํฉ๋๋ค.
โ application/json๊ณผ ๊ฐ์ ํ์์ Simple Request๊ฐ ๋ ์ ์์ผ๋ฉฐ Preflight๋ฅผ ์ ๋ฐํฉ๋๋ค.
ํด๋ผ์ด์ธํธ์ ์๋ฒ ์ฝ๋ ์ต์ ํ ๊ฐ์ด๋
Simple Request ์ ๋ต์ ๊ตฌํํ๊ธฐ ์ํด ํด๋ผ์ด์ธํธ์ ์๋ฒ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํด์ผ GAS CORS ์ค๋ฅ๋ฅผ ๊ทผ๋ณธ์ ์ผ๋ก ํด๊ฒฐํ ์ ์์ต๋๋ค.
| ๊ตฌ๋ถ | ๊ธฐ์กด (CORS ๋ฐ์) | ์์ (Simple Request) | ํจ๊ณผ |
|---|---|---|---|
| ํด๋ผ์ด์ธํธ ํค๋ | Content-Type: application/json | Content-Type: application/x-www-form-urlencoded | Pre-flight ์์ฒญ ์๋ต |
| ์๋ฒ ๋ฐ์ดํฐ ์ฝ๊ธฐ | `JSON.parse(e.postData.contents)` | `e.parameter.prompt` | Form Data๋ฅผ ๋ฐ๋ก ์ฝ์ด ์ค๋ฅ ์ ๊ฑฐ |
1. ํด๋ผ์ด์ธํธ (HTML/JS) ์ฝ๋ ์์
๋ฐ์ดํฐ๋ฅผ `URLSearchParams`๋ฅผ ์ฌ์ฉํ์ฌ Form Data ํํ๋ก ์ธ์ฝ๋ฉํฉ๋๋ค.
async function callAppsScript(prompt) {
const formData = new URLSearchParams();
formData.append('prompt', prompt);
const response = await fetch(GOOGLE_SCRIPT_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded', // Simple Request ํ์
},
body: formData.toString() // Form Data ์ ์ก
});
return await response.text();
}
2. ์๋ฒ (Apps Script) ์ฝ๋ ์์
์ ์ก๋ Form Data๋ฅผ `e.parameter` ๊ฐ์ฒด๋ฅผ ํตํด ๋ฐ๋ก ์ ๊ทผํฉ๋๋ค.
function doPost(e) {
const userPrompt = e.parameter.prompt; // e.parameter๋ก Form Data ์ง์ ์ ๊ทผ
if (!userPrompt) {
return createTextResponse("Error: ์์ฒญ ๋ณธ๋ฌธ์ 'prompt'๊ฐ ์์ต๋๋ค.");
}
return createTextResponse("์ฑ๊ณต์ ์ผ๋ก ์ฒ๋ฆฌ๋์์ต๋๋ค!");
}
function createTextResponse(content) {
const output = ContentService.createTextOutput(content);
output.setMimeType(ContentService.MimeType.TEXT);
return output; // setHeader ์์ด๋ GAS๊ฐ ์๋ CORS ์ฒ๋ฆฌ
}
FAQ: GAS CORS ์ค๋ฅ ๊ด๋ จ ์์ฃผ ๋ฌป๋ ์ง๋ฌธ
Q1: ์ ContentService.createTextOutput์๋ setHeader๊ฐ ์๋์?
A1: Apps Script์ ContentService๋ ๋จ์ ํ ์คํธ ์ถ๋ ฅ์ ์ํด ์ค๊ณ๋์ด ํค๋ ์กฐ์์ด ์ ํ๋ฉ๋๋ค. Simple Request ์ ๋ต์ผ๋ก ์ฐํํ๋ ๊ฒ์ด ์ต์ ์ ๋๋ค.
Q2: Simple Request ์ ๋ต์ด ๋ชจ๋ CORS ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋์?
A2: ๋๋ถ๋ถ์ ๋จ์ POST ์์ฒญ์์ ๋ฐ์ํ๋ 405 ์ค๋ฅ์ `setHeader` ๋ถ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค. ํน์ ์ธ์ฆ ํค๋๊ฐ ํ์ํ๊ฑฐ๋ ๋ณต์กํ ์์ฒญ์ ๊ฒฝ์ฐ, ํ๋ก์ ์๋ฒ ๋ฑ ์ถ๊ฐ ๋์์ด ํ์ํ ์ ์์ต๋๋ค.
Q3: e.parameter ๋์ e.postData.contents๋ฅผ ์จ๋ ๋๋์?
A3: `e.postData.contents`๋ ์ฃผ๋ก JSON ์ฌ์ฉ ์ ์ฌ์ฉ๋๋ฉฐ, ์ด๋ ๋ธ๋ผ์ฐ์ ๊ฐ OPTIONS ์์ฒญ์ ๋ณด๋ด GAS CORS ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. Form Data์ `e.parameter` ์ฌ์ฉ์ ๊ถ์ฅํฉ๋๋ค.
5. ์ต์ข GAS CORS ์ค๋ฅ ํด๊ฒฐ ์ฒดํฌ๋ฆฌ์คํธ
์ ๋ฆฌํ๋ฉด, GAS CORS ์ค๋ฅ๋ฅผ ํผํ๋ ํต์ฌ 5๊ฐ์ง ์กฐ๊ฑด์ ๋๋ค.
- ํด๋ผ์ด์ธํธ: `Content-Type`์ `application/x-www-form-urlencoded`๋ก ์ค์
- ํด๋ผ์ด์ธํธ: `URLSearchParams`๋ก ๋ฐ์ดํฐ๋ฅผ ์ธ์ฝ๋ฉํ์ฌ POST
- ์๋ฒ: `e.parameter`๋ก ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ธฐ
- ์๋ฒ: `ContentService.createTextOutput`๋ง ์ฌ์ฉํ์ฌ ์๋ต ๋ฐํ
- ๋ฐฐํฌ: Apps Script๋ฅผ ์ ๋ฒ์ ์ผ๋ก ์ฌ๋ฐฐํฌ
์ด ๊ฐ๋จํ Simple Request ๋ฐฉ๋ฒ์ผ๋ก ์ธ๋ถ ์น์ฌ์ดํธ์ ์์ ์ ์ธ API ํต์ ์ด ๊ฐ๋ฅํ๋ฉฐ, ๊ฐ๋ฐ ์์ฐ์ฑ๊ณผ ์์ ์ฑ์ด ํฌ๊ฒ ํฅ์๋ฉ๋๋ค. ๋ฐ๋ณต๋๋ GAS CORS ์ค๋ฅ๋ฅผ ๊ทผ๋ณธ์ ์ผ๋ก ํด๊ฒฐํ๊ณ ์์ ํ ํต์ ์ ๊ตฌํํ์๊ธฐ ๋ฐ๋๋๋ค.