Published:

Last Updated:

ꡬ글 μ•±μŠ€ 슀크립트둜 쿠팑 μƒν’ˆ μžλ™ ν‘œμ‹œν•˜κΈ°ο½œμ½”λ“œμ™€ μ„€μ • 방법

쿠팑 νŒŒνŠΈλ„ˆμŠ€ μƒν’ˆμ„ λΈ”λ‘œκ·Έμ— μžλ™ λ…ΈμΆœν•˜λŠ” 방법을 μ†Œκ°œν•©λ‹ˆλ‹€. API ν‚€ μ„€μ •, Apps Script ν™œμš©, μ‹€μ‹œκ°„ μƒν’ˆ μ—…λ°μ΄νŠΈ μžλ™ν™”κΉŒμ§€ μžμ„Ένžˆ μ•ˆλ‚΄ν•©λ‹ˆλ‹€.

λΈ”λ‘œκ·Έλ₯Ό μš΄μ˜ν•˜λ©΄μ„œ 쿠팑 νŒŒνŠΈλ„ˆμŠ€ μˆ˜μ΅μ„ 올리기 μœ„ν•΄ 맀번 μƒν’ˆ 링크λ₯Ό 일일이 κ²€μƒ‰ν•˜κ³  μ‚½μž…ν•˜λŠ” 일이 번거둭고 μ‹œκ°„ λ‚­λΉ„μ²˜λŸΌ λŠκ»΄μ§€μ…¨λ‹€λ©΄, 이 글이 λ°”λ‘œ 해닡이 될 수 μžˆμŠ΅λ‹ˆλ‹€. 특히 μ½˜ν…μΈ κ°€ λ§Žμ•„μ§ˆμˆ˜λ‘ μˆ˜λ™ μž‘μ—…μ˜ ν•œκ³„λŠ” λΆ„λͺ…ν•΄μ§€κΈ° 마련이죠.

μ˜€λŠ˜μ€ Google Apps Scriptλ₯Ό ν™œμš©ν•΄ 쿠팑 νŒŒνŠΈλ„ˆμŠ€ μƒν’ˆμ„ μžλ™μœΌλ‘œ λΆˆλŸ¬μ™€ λΈ”λ‘œκ·Έμ— λ…ΈμΆœν•˜λŠ” 방법을 μ†Œκ°œν•©λ‹ˆλ‹€. 이 방식은 ν•œ 번만 μ„€μ •ν•˜λ©΄ μ΅œμ‹  쿠팑 μƒν’ˆ 정보가 μ‹€μ‹œκ°„μœΌλ‘œ μ—…λ°μ΄νŠΈλ˜λ©° μžλ™ 반영되기 λ•Œλ¬Έμ—, 맀번 μƒˆλ‘œ μ‚½μž…ν•  ν•„μš” 없이 κΎΈμ€€ν•œ 수읡 창좜과 관리 νš¨μœ¨μ„±μ„ λ™μ‹œμ— μž‘μ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

λΈ”λ‘œκ·Έ 수읡 μžλ™ν™”, 쿠팑 API 기반 μ‹€μ‹œκ°„ μƒν’ˆ μ—…λ°μ΄νŠΈ, 그리고 λ°˜μ‘ν˜• λ””μžμΈ μ μš©κΉŒμ§€, μ§€κΈˆλΆ€ν„° μ†Œκ°œν•  방법은 쿠팑 νŒŒνŠΈλ„ˆμŠ€λ₯Ό ν™œμš©ν•œ λΈ”λ‘œκ·Έ μš΄μ˜μ— μžˆμ–΄ κΌ­ ν•„μš”ν•œ μ „λž΅μ΄ 될 κ²ƒμž…λ‹ˆλ‹€.

쿠팑 νŒŒνŠΈλ„ˆ μžλ™ κ΄‘κ³  μ μš©λ°©λ²•

λ¨Όμ € 쿠팑 νŒŒνŠΈλ„ˆμŠ€ μ‚¬μ΄νŠΈμ— 접속해 Access Key와 Secret Keyλ₯Ό 볡사해 λ©”λͺ¨ν•΄ λ‘μ„Έμš”.

1. 쿠팑 νŒŒνŠΈλ„ˆμŠ€ μ‚¬μ΄νŠΈ 접속 ν›„ API ν‚€ 볡사

쿠팑 νŒŒνŠΈλ„ˆ μƒν’ˆ μžλ™ 등둝을 μœ„ν•΄ 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 μ ‘μ†ν•©λ‹ˆλ‹€.

λΈ”λ‘œκ·Έ 쿠팑 μžλ™ κ΄‘κ³  섀정방법2

2. λ¨Όμ € 쿠팑 API와 연동할 Apps Script μƒˆ ν”„λ‘œμ νŠΈλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

ν”„λ‘œμ νŠΈλ₯Ό μƒμ„±ν•œ ν›„ 기본적으둜 μ„€μ •λœ μ½”λ“œ μ‚­μ œν•©λ‹ˆλ‹€.

Google Apps Script 쿠팑 API 섀정방법3

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;

이 과정을 톡해 λΈ”λ‘œκ·ΈλŠ” 쿠팑 νŒŒνŠΈλ„ˆμŠ€ μˆ˜μ΅μ„ μœ„ν•œ κ°•λ ₯ν•œ μžλ™ν™” 도ꡬλ₯Ό κ°–μΆ”κ²Œ λ©λ‹ˆλ‹€. λ°©λ¬Έμžλ“€μ€ 항상 μ΅œμ‹  μƒν’ˆ 정보λ₯Ό 확인할 수 μžˆμ–΄ 클릭λ₯ κ³Ό μ „ν™˜μœ¨ ν–₯상에 κΈ°μ—¬ν•˜κ²Œ λ©λ‹ˆλ‹€. 성곡적인 λΈ”λ‘œκ·Έ μš΄μ˜μ„ μ‘μ›ν•©λ‹ˆλ‹€!