๊ตฌ๊ธ ์ฑ์ค ์คํฌ๋ฆฝํธ๋ก ์ฟ ํก ์ํ ์๋ ํ์ํ๊ธฐ๏ฝ์ฝ๋์ ์ค์ ๋ฐฉ๋ฒ
์ฟ ํก ํํธ๋์ค ์ํ์ ๋ธ๋ก๊ทธ์ ์๋ ๋ ธ์ถํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค. 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;
์ด ๊ณผ์ ์ ํตํด ๋ธ๋ก๊ทธ๋ ์ฟ ํก ํํธ๋์ค ์์ต์ ์ํ ๊ฐ๋ ฅํ ์๋ํ ๋๊ตฌ๋ฅผ ๊ฐ์ถ๊ฒ ๋ฉ๋๋ค. ๋ฐฉ๋ฌธ์๋ค์ ํญ์ ์ต์ ์ํ ์ ๋ณด๋ฅผ ํ์ธํ ์ ์์ด ํด๋ฆญ๋ฅ ๊ณผ ์ ํ์จ ํฅ์์ ๊ธฐ์ฌํ๊ฒ ๋ฉ๋๋ค. ์ฑ๊ณต์ ์ธ ๋ธ๋ก๊ทธ ์ด์์ ์์ํฉ๋๋ค!