κ΅¬κΈ μ±μ€ μ€ν¬λ¦½νΈλ‘ μΏ ν‘ μν μλ νμνκΈ°ο½μ½λμ μ€μ λ°©λ²
μΏ ν‘ ννΈλμ€ μνμ λΈλ‘κ·Έμ μλ λ ΈμΆνλ λ°©λ²μ μκ°ν©λλ€. API ν€ μ€μ , Apps Script νμ©, μ€μκ° μν μ λ°μ΄νΈ μλνκΉμ§ μμΈν μλ΄ν©λλ€.
λΈλ‘κ·Έλ₯Ό μ΄μνλ©΄μ μΏ ν‘ ννΈλμ€ μμ΅μ μ¬λ¦¬κΈ° μν΄ λ§€λ² μν λ§ν¬λ₯Ό μΌμΌμ΄ κ²μνκ³ μ½μ νλ μΌμ΄ λ²κ±°λ‘κ³ μκ° λλΉμ²λΌ λκ»΄μ§μ ¨λ€λ©΄, μ΄ κΈμ΄ λ°λ‘ ν΄λ΅μ΄ λ μ μμ΅λλ€. νΉν μ½ν μΈ κ° λ§μμ§μλ‘ μλ μμ μ νκ³λ λΆλͺ ν΄μ§κΈ° λ§λ ¨μ΄μ£ .
μ€λμ Google Apps Scriptλ₯Ό νμ©ν΄ μΏ ν‘ ννΈλμ€ μνμ μλμΌλ‘ λΆλ¬μ λΈλ‘κ·Έμ λ ΈμΆνλ λ°©λ²μ μκ°ν©λλ€. μ΄ λ°©μμ ν λ²λ§ μ€μ νλ©΄ μ΅μ μΏ ν‘ μν μ λ³΄κ° μ€μκ°μΌλ‘ μ λ°μ΄νΈλλ©° μλ λ°μλκΈ° λλ¬Έμ, λ§€λ² μλ‘ μ½μ ν νμ μμ΄ κΎΈμ€ν μμ΅ μ°½μΆκ³Ό κ΄λ¦¬ ν¨μ¨μ±μ λμμ μ‘μ μ μμ΅λλ€.
λΈλ‘κ·Έ μμ΅ μλν, μΏ ν‘ API κΈ°λ° μ€μκ° μν μ λ°μ΄νΈ, κ·Έλ¦¬κ³ λ°μν λμμΈ μ μ©κΉμ§, μ§κΈλΆν° μκ°ν λ°©λ²μ μΏ ν‘ ννΈλμ€λ₯Ό νμ©ν λΈλ‘κ·Έ μ΄μμ μμ΄ κΌ νμν μ λ΅μ΄ λ κ²μ λλ€.
μΏ ν‘ ννΈλ μλ κ΄κ³ μ μ©λ°©λ²
λ¨Όμ μΏ ν‘ ννΈλμ€ μ¬μ΄νΈμ μ μν΄ Access Keyμ Secret Keyλ₯Ό 볡μ¬ν΄ λ©λͺ¨ν΄ λμΈμ.
1. μΏ ν‘ ννΈλμ€ μ¬μ΄νΈ μ μ ν API ν€ λ³΅μ¬

2. Google Apps Script, ννΈλμ€ μλν
Google Apps Scriptλ ꡬκΈμ΄ μ 곡νλ ν΄λΌμ°λ κΈ°λ° μλ°μ€ν¬λ¦½νΈ νλ«νΌμ λλ€. κ΅¬κΈ μ€νλ λμνΈ, μ§λ©μΌ λ±κ³Ό μ½κ² μ°λλλ©° μΈλΆ APIμμ ν΅μ λ κ°λ¨ν©λλ€.
μ Apps ScriptμΈκ°μ?
- λ¬΄λ£ λ° μλ²λ¦¬μ€: λ³λ μλ² μμ΄ κ΅¬κΈ μΈνλΌ μ¬μ© κ°λ₯
- μ¬μ΄ μ°λ: κ΅¬κΈ μλΉμ€ λ° μΈλΆ APIμ κ°λ¨ν μ°κ²°
- μλν: 24μκ° μλμΌλ‘ μ΅μ μν μ 보 κ°μ Έμ€κΈ°
μ΄ Apps Scriptλ‘ μΏ ν‘ ννΈλμ€ APIμ ν΅μ νμ¬ μνλͺ , κ°κ²©, μ΄λ―Έμ§, λ§ν¬ λ±μ JSON ννλ‘ λ°μμ¬ μ μμ΅λλ€.
3. Google Apps Script μ€μ λ° μ¬μ© λ°©λ²
1. Google Apps Script μ μν©λλ€.
- script.google.comμ μ μ

2. λ¨Όμ μΏ ν‘ APIμ μ°λν Apps Script μ νλ‘μ νΈλ₯Ό μμ±ν©λλ€.
νλ‘μ νΈλ₯Ό μμ±ν ν κΈ°λ³Έμ μΌλ‘ μ€μ λ μ½λ μμ ν©λλ€.

3. Code.gs νμΌμ μ½λ λΆμ¬λ£κΈ°
- μ 체 Apps Script μ½λλ₯Ό 볡μ¬ν΄ λΆμ¬λ£κΈ°
- COUPANG_ACCESS_KEY, COUPANG_SECRET_KEY, COUPANG_AFFILIATE_IDλ₯Ό μμ μ μ λ³΄λ‘ μμ
- μΏ ν‘ μνμ€ λ ΈμΆνκ³ μ νλ μΉ΄ν κ³ λ¦¬ λ³κ²½.
- λλ¨Έμ§ κ·Έλλ‘....
μΏ ν‘ μΉ΄ν κ³ λ¦¬ μν λ²νΈ
| Id | μ΄λ¦ |
|---|---|
| 1001 | μ¬μ±ν¨μ |
| 1002 | λ¨μ±ν¨μ |
| 1010 | λ·°ν° |
| 1011 | μΆμ°/μ μλ |
| 1012 | μν |
| 1013 | μ£Όλ°©μ©ν |
| 1014 | μνμ©ν |
| 1015 | νμΈν λ¦¬μ΄ |
| 1016 | κ°μ λμ§νΈ |
| 1017 | μ€ν¬μΈ /λ μ |
| 1018 | μλμ°¨μ©ν |
| 1019 | λμ/μλ°/DVD |
| 1020 | μꡬ/μ·¨λ―Έ |
| 1021 | 문ꡬ/μ€νΌμ€ |
| 1024 | ν¬μ€/건κ°μν |
| 1025 | κ΅λ΄μ¬ν |
| 1026 | ν΄μΈμ¬ν |
| 1029 | λ°λ €λλ¬Όμ©ν |
| 1030 | μ μλν¨μ |
μΏ ν‘ μΉ΄ν κ³ λ¦¬ μν
const PRODUCT_LIST_API_PATH ="/v2/providers/affiliate_open_api/apis/openapi/products/bestcategories/1001?limit=50"
μΏ ν‘ PL μν
const PRODUCT_LIST_API_PATH = "/v2/providers/affiliate_open_api/apis/openapi/products/coupangPL";
// ==== μ€μ κ° (λ³ΈμΈμ μ λ³΄λ‘ λ³κ²½) ====
const COUPANG_ACCESS_KEY = "λΉμ μ ACCESS KEY"; // μΏ ν‘ API Access Key
const COUPANG_SECRET_KEY = "λΉμ μ SECRET KEY"; // μΏ ν‘ API Secret Key
const COUPANG_AFFILIATE_ID = "λΉμ μ AFFILIATE ID"; // μΏ ν‘ ννΈλμ€ ID (lptag=AF1234567μ AF1234567 λΆλΆ)
// μΏ ν‘ API κΈ°λ³Έ URL λ° Deep Link κ²½λ‘
const COUPANG_API_BASE_URL = "https://api-gateway.coupang.com";
const DEEPLINK_API_PATH = "/v2/providers/affiliate_open_api/apis/openapi/v1/deeplink";
// μν λͺ©λ‘ μ‘°ν API κ²½λ‘(μν μΉ΄ν
κ³ λ¦¬λ‘ λ³κ²½)
const PRODUCT_LIST_API_PATH = "/v2/providers/affiliate_open_api/apis/openapi/products/coupangPL";
// ---
// ==== GMT λ μ§ νμ μμ± ν¨μ (yyMMdd'T'HHmmss'Z' ν¬λ§·) ====
function getCoupangApiDate() {
const date = new Date();
const year = date.getUTCFullYear().toString().substring(2); // YY (μ: 2025 - 25)
const month = (date.getUTCMonth() + 1).toString().padStart(2, '0'); // MM
const day = date.getUTCDate().toString().padStart(2, '0'); // DD
const hours = date.getUTCHours().toString().padStart(2, '0'); // HH
const minutes = date.getUTCMinutes().toString().padStart(2, '0'); // mm
const seconds = date.getUTCSeconds().toString().padStart(2, '0'); // ss
return `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
}
// ==== HMAC Signature μμ± ν¨μ ====
function generateCoupangSignature(method, uri, secretKey, accessKey) {
const parts = uri.split('?');
const path = parts[0];
const query = (parts.length === 2) ? parts[1] : '';
const datetime = getCoupangApiDate();
const stringToSign = `${datetime}${method}${path}${query}`;
const signatureBytes = Utilities.computeHmacSha256Signature(stringToSign, secretKey);
const signatureHex = bytesToHex(signatureBytes);
return `CEA algorithm=HmacSHA256, access-key=${accessKey}, signed-date=${datetime}, signature=${signatureHex}`;
}
// ==== λ°μ΄νΈ λ°°μ΄μ 16μ§μ λ¬Έμμ΄λ‘ λ³ννλ ν¬νΌ ν¨μ ====
function bytesToHex(bytes) {
return Array.from(bytes, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}
// ---
// ==== Deep Link μμ± API νΈμΆ ν¨μ (μ¬μ©μ μμ² μ POSTλ‘ νΈμΆ κ°λ₯) ====
function generateCoupangDeepLink(coupangUrls) {
const method = 'POST';
const uri = DEEPLINK_API_PATH;
const url = COUPANG_API_BASE_URL + uri;
const requestPayload = {
coupangUrls: coupangUrls
};
const requestBody = JSON.stringify(requestPayload);
const authorizationHeader = generateCoupangSignature(method, uri, COUPANG_SECRET_KEY, COUPANG_ACCESS_KEY);
const options = {
method: method,
headers: {
'Authorization': authorizationHeader,
'Content-Type': 'application/json;charset=UTF-8'
},
payload: requestBody,
muteHttpExceptions: true
};
try {
const response = UrlFetchApp.fetch(url, options);
const responseCode = response.getResponseCode();
const responseBody = response.getContentText();
if (responseCode === 200) {
const parsedResponse = JSON.parse(responseBody);
Logger.log("Deep Link API Response: " + JSON.stringify(parsedResponse, null, 2));
return parsedResponse;
} else {
Logger.log(`API Error: ${responseCode} - ${responseBody}`);
return null;
}
} catch (e) {
Logger.log(`Fetch Error: ${e.message}`);
return null;
}
}
// ---
// ==== μν λͺ©λ‘ μ‘°ν API νΈμΆ ν¨μ ====
function fetchCoupangProducts(limit = 20) { // κΈ°λ³Έκ° 20κ°
const method = 'GET';
const uri = `${PRODUCT_LIST_API_PATH}?limit=${limit}`;
const url = COUPANG_API_BASE_URL + uri;
const authorizationHeader = generateCoupangSignature(method, uri, COUPANG_SECRET_KEY, COUPANG_ACCESS_KEY);
const options = {
method: method,
headers: {
'Authorization': authorizationHeader,
},
muteHttpExceptions: true
};
try {
const response = UrlFetchApp.fetch(url, options);
const responseCode = response.getResponseCode();
const responseBody = response.getContentText();
if (responseCode === 200) {
const parsedResponse = JSON.parse(responseBody);
Logger.log(`Product List API Response (limit=${limit}): ` + JSON.stringify(parsedResponse, null, 2));
return parsedResponse;
} else {
Logger.log(`API Error: ${responseCode} - ${responseBody}`);
return null;
}
} catch (e) {
Logger.log(`Fetch Error: ${e.message}`);
return null;
}
}
// ---
// ==== μΉ μ±μΌλ‘ λ°°ν¬ν ν¨μ (GET μμ² μ²λ¦¬: μν λͺ©λ‘ μ‘°ν) ====
function doGet(e) {
let limit = parseInt(e.parameter.limit); // λΈλ‘κ·Έμμ λ³΄λΈ limit νλΌλ―Έν°λ₯Ό λ°μ
if (isNaN(limit) || limit 1 || limit 100) {
limit = 20; // limit κ°μ΄ μκ±°λ μ ν¨νμ§ μμΌλ©΄ κΈ°λ³Έ 20κ°
}
const productsResult = fetchCoupangProducts(limit);
// μΏ ν‘ API μλ΅μμ 'rCode'κ° '0'μ΄κ³ 'data' νλκ° μ‘΄μ¬ν λλ§ 'data' νλμ λ΄μ©μ λ°ν
if (productsResult && productsResult.rCode === "0" && productsResult.data) {
return ContentService.createTextOutput(JSON.stringify(productsResult.data, null, 2))
.setMimeType(ContentService.MimeType.JSON);
} else {
// μ€λ₯κ° μκ±°λ λ°μ΄ν°κ° μλ κ²½μ°, μ€λ₯ λ©μμ§λ₯Ό ν¬ν¨νμ¬ λ°ν
const errorMessage = productsResult && productsResult.rMessage ? productsResult.rMessage : "μν λͺ©λ‘ μ‘°ν μ€ν¨ λλ λ°μ΄ν° μμ.";
Logger.log(`μν μ‘°ν μ€ν¨: ${errorMessage}`);
return ContentService.createTextOutput(JSON.stringify({ error: errorMessage }))
.setMimeType(ContentService.MimeType.JSON);
}
}
// ==== μΉ μ±μΌλ‘ λ°°ν¬ν ν¨μ (POST μμ² μ²λ¦¬: Deep Link μμ± - νμ¬ λΈλ‘κ·Έμμλ GETλ§ μ¬μ©) ====
function doPost(e) {
try {
const requestData = JSON.parse(e.postData.contents);
const coupangUrls = requestData.coupangUrls;
if (!coupangUrls || !Array.isArray(coupangUrls) || coupangUrls.length === 0) {
return ContentService.createTextOutput(JSON.stringify({ error: "'coupangUrls' λ°°μ΄μ JSON λ°λμ ν¬ν¨ν΄μ£ΌμΈμ." }))
.setMimeType(ContentService.MimeType.JSON);
}
const deepLinkResult = generateCoupangDeepLink(coupangUrls);
if (deepLinkResult) {
return ContentService.createTextOutput(JSON.stringify(deepLinkResult, null, 2))
.setMimeType(ContentService.MimeType.JSON);
} else {
return ContentService.createTextOutput(JSON.stringify({ error: "Deep Link μμ± μ€ν¨" }))
.setMimeType(ContentService.MimeType.JSON);
}
} catch (err) {
Logger.log("doPost μ€λ₯: " + err.message);
return ContentService.createTextOutput(JSON.stringify({ error: "μμ² μ²λ¦¬ μ€ μ€λ₯ λ°μ: " + err.message }))
.setMimeType(ContentService.MimeType.JSON);
}
}
4. Apps Script μΉ μ± λ°°ν¬

- μ½λλ₯Ό μ μ₯ν ν, μλ¨ λ©λ΄μμ λ°°ν¬ μ λ°°ν¬λ₯Ό ν΄λ¦ν©λλ€.
5. μ ν μ νμμ μΉ μ±μ μ νν©λλ€.


7.μ΄λ―Έμ§ μμλλ‘ λ²νΌμ ν΄λ¦ν©λλ€.

8. ALLOW λ²νΌμ ν΄λ¦νκ³ μ£Όμ 볡μ¬ν μλ£

9. 볡μ¬ν μ£Όμ μ μ λ°μ΄ν° νμΈν μ£Όμ λ©λͺ¨
κΈ΄ μΉ μ± μ£Όμ(https://script.googleusercontent.com/macros/echo?...)κ° μμ±λ©λλ€. μ΄ μ£Όμλ₯Ό μ νν 볡μ¬ν΄ λμΈμ. μ΄ URLμ λΈλ‘κ·Έμμ μΉ μ±μ μ κ·Όνλ μ μΌν κ²½λ‘μ λλ€.

ν°μ€ν 리 λΈλ‘κ·Έμ μΏ ν‘ μν λ ΈμΆ μ½λ μ μ©
μ΄μ μνμ λ ΈμΆνκΈ° μν΄ λΈλ‘κ·Έμ μ€μ μ μ§νν©λλ€.
1. ν°μ€ν 리 κ΄λ¦¬μ νμ΄μ§ HTML λͺ¨λ
μλ μ½λλ₯Ό μΏ ν‘ μνμ μλμΌλ‘ λ
ΈμΆνκ³ μ νλ μμΉμ μ½μ
ν©λλ€.
μ κΈμ μ μ©νκ±°λ κΈ°μ‘΄ κΈμ μΆκ°ν μ μμΌλ©°, κΈ°λ³Έ μ€ν¨μ κ³ μ νμ¬ μ¬μ©ν μλ μμ΅λλ€.
μλν°μμ HTML λͺ¨λλ‘ μ νν λ€ μ½λλ₯Ό λΆμ¬λ£μ΄ μ£ΌμΈμ.
pμ΄ ν¬μ€ν
μ μΏ ν‘ ννΈλμ€ νλμ μΌνμΌλ‘ μΌμ μ‘μ μμλ£λ₯Ό μ 곡λ°μ΅λλ€./p
div id="coupang-products-container"
pμ μλ§ κΈ°λ€λ €μ£ΌμΈμ. μν μ 보λ₯Ό λΆλ¬μ€λ μ€μ
λλ€.../p
/div
2. HTML/JavaScript μ½λ μ½μ
- μ 곡λ μ½λλ₯Ό 볡μ¬ν΄ HTML λͺ¨λμ λΆμ¬λ£μ΅λλ€.
- μ€μ: μ½λ λ΄ const apiUrl = 'URL μ λ ₯&limit=3'; μ λ ₯λΆλΆμ 볡μ¬ν μΉ μ± URLμ μ νν μ½μ νμΈμ.
const apiUrl = 'λ³΅μ¬ URLμ λ ₯_&limit=3'; // λ ΈμΆ μνμ μ€μ (νμ¬ 3κ°)
script
async function loadCoupangProducts() {
const container = document.getElementById('coupang-products-container');
const apiUrl = 'URL μ
λ ₯&limit=3'; // μ€μ URL μ
λ ₯
try {
const response = await fetch(apiUrl);
if (!response.ok) throw new Error(`API μμ² μ€ν¨: ${response.status}`);
const data = await response.json();
const products = Array.isArray(data) ? data.slice(0, 3) :
data?.data?.slice(0, 3) || [];
if (products.length 0) {
let html = `
p class="product-title"μΏ ν‘ μΆμ² μν/p
div class="product-grid"
${products.map(product = `
div class="product-item"
a href="${product.productUrl || '#'}" target="_blank" rel="noopener"
img class="product-image" src="${product.productImage || 'https://via.placeholder.com/150'}" alt="${product.productName || ''}"
p class="product-name"${product.productName || 'μνλͺ
μμ'}/h3
p class="product-price"${product.productPrice?.toLocaleString() + 'μ' || 'κ°κ²© μ 보 μμ'}/p
button class="detail-button"μμΈν 보기/button
/a
/div
`).join('')}
/div
`;
container.innerHTML = html;
} else {
container.innerHTML = 'p class="no-products"νμν μνμ΄ μμ΅λλ€./p';
}
} catch (error) {
container.innerHTML = 'p class="error-message"μν λ‘λ© μ€ μ€λ₯ λ°μ/p';
console.error(error);
}
}
window.addEventListener('DOMContentLoaded', loadCoupangProducts);
/script
3. μΏ ν‘ ννΈλμ€ μλ λ ΈμΆ μνμ CSS μ μ©
μν CSSλ λΈλ‘κ·Έ μ€νμΌμ λ§κ² μμ λ‘κ² μ»€μ€ν°λ§μ΄μ¦ν μ μμ΅λλ€.
μλλ λ€ν¬ λͺ¨λμ λͺ¨λ°μΌμ μ΅μ νλ κΈ°λ³Έ μμμ
λλ€.
/* μν 컨ν
μ΄λ */
#coupang-products-container{
max-width:1000px;
}
.product-grid {
display: grid;
/* κΈ°λ³Έμ μΌλ‘ λͺ¨λ°μΌμμ 1μ΄λ‘ νμ */
grid-template-columns: repeat(1, 1fr);
gap: 20px;
margin-top: 20px;
}
/* κ°λ³ μν μ€νμΌ */
.product-item {
border: 1px solid #eee;
padding: 15px;
border-radius: 8px;
text-align: center;
transition: transform 0.2s;
}
.product-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.product-image {
max-width: 100%;
object-fit: contain;
border-radius: 4px;
margin-bottom: 10px;
}
.product-name {
font-size: 1.1rem;
margin: 0 0 10px;
height: 3em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.product-price {
font-size: 1.2rem;
font-weight: bold;
color: #FF0000;
margin: 0;
}
.detail-button {
background-color: var(--dark); /* μ΄ λ³μκ° μ μλμ΄ μμ§ μλ€λ©΄ λ€λ₯Έ μμ μ½λλ‘ λ³κ²½νμΈμ (μ: #1a73e8) */
color: white;
padding: 8px 15px;
border: none;
border-radius: 5px;
cursor: pointer;
margin-top: 15px;
width: 100%;
}
/* κΈ°ν μ€νμΌ */
.product-title {
font-size: 1.6rem;
color: #333;
margin-bottom: 15px;
}
.no-products,
.error-message {
color: #666;
text-align: center;
padding: 20px;
}
---
/* === λ°μν λ―Έλμ΄ μΏΌλ¦¬ μΆκ° === */
/* νλΈλ¦Ώ (νλ©΄ λλΉκ° 600px μ΄μμΌ λ) */
@media (min-width: 600px) {
.product-grid {
grid-template-columns: repeat(2, 1fr); /* 2μ΄λ‘ λ³κ²½ */
}
}
/* λ°μ€ν¬ν± (νλ©΄ λλΉκ° 992px μ΄μμΌ λ) */
@media (min-width: 992px) {
.product-grid {
grid-template-columns: repeat(3, 1fr); /* 3μ΄λ‘ λ³κ²½ */
}
}
κ²μ μ΅μ ν(SEO) λ° μΆκ° ν
- HTML ꡬ쑰: μν λͺ©λ‘μ κ°μΈλ
divμh2νκ·Έλ κ²μ μμ§μ΄ μ½ν μΈ μ μ€μλλ₯Ό νμ νλ λ° λμμ μ€λλ€.h2μΏ ν‘ μΆμ² μν/h2μ²λΌ ν€μλλ₯Ό ν¬ν¨νμΈμ. - μ΄λ―Έμ§ alt μμ±:
alt="${productName}"μ μ΄λ―Έμ§ μ κ·Όμ±κ³Ό SEO ν₯μμ ν¨κ³Όμ μ λλ€. - λ°μν λμμΈ:
display: gridλ₯Ό μ¬μ©νλ©΄ λͺ¨λ°μΌμμλ μνμ΄ κΉλνκ² λ³΄μ λλ€. - μν κ°μ μ‘°μ :
const queryLimit = 5;κ°μ λ³κ²½ν΄ λ ΈμΆ μν κ°μλ₯Ό μμ λ‘κ² μ‘°μ ν μ μμ΅λλ€. μ:queryLimit = 10;
μ΄ κ³Όμ μ ν΅ν΄ λΈλ‘κ·Έλ μΏ ν‘ ννΈλμ€ μμ΅μ μν κ°λ ₯ν μλν λꡬλ₯Ό κ°μΆκ² λ©λλ€. λ°©λ¬Έμλ€μ νμ μ΅μ μν μ 보λ₯Ό νμΈν μ μμ΄ ν΄λ¦λ₯ κ³Ό μ νμ¨ ν₯μμ κΈ°μ¬νκ² λ©λλ€. μ±κ³΅μ μΈ λΈλ‘κ·Έ μ΄μμ μμν©λλ€!