μΏ ν‘ ννΈλμ€ μμ΅ κ·Ήλνλ₯Ό μν μλ λ§ν¬ λ³νκΈ° κ΅¬μΆ λ°©λ²
μΏ ν‘ ννΈλμ€ λ§ν¬ λ³νκΈ° κ΅¬μΆ λ°©λ² μλ΄. λ°λ³΅ μμ μμ΄ λΈλ‘κ·Έμμ μλμΌλ‘ μΏ ν‘ μν λ§ν¬λ₯Ό μμ΅μ© λ₯λ§ν¬λ‘ λ³νν΄ μμ΅ λλ½μ λ°©μ§ν©λλ€.
μΏ ν‘ ννΈλμ€λ₯Ό ν΅ν΄ λΈλ‘κ·Έ μμ΅νλ₯Ό μλνλ μ΄μμλΌλ©΄, λꡬλ ν λ²μ―€μ κ²ͺμ΄λ΄€μ λΆνΈν¨μ΄ μμ΅λλ€. λ°λ‘ μΏ ν‘ μν λ§ν¬λ₯Ό νΈλνΉ μ½λλ‘ λ³ννλ κ³Όμ μ λ²κ±°λ‘μμ λλ€.
λ§€λ² μνμ μ μ ν ν, ν΄λΉ μν λ§ν¬λ₯Ό μμ μ μΏ ν‘ ννΈλμ€ νΈλνΉ μ½λλ‘ λ³νν΄μΌλ§ μμ΅μ΄ μ μμ μΌλ‘ λ°μν©λλ€.
λ§μ½ λ§ν¬μ μ΄μμμ νΈλνΉ μ½λκ° ν¬ν¨λμ΄ μμ§ μλ€λ©΄, μνμ΄ μ무리 λ§μ΄ νλ €λ μμ΅μ λ°μνμ§ μμ΅λλ€. λλΆλΆμ μ΄μμλ μΏ ν‘ ννΈλμ€ κ³΅μ μ¬μ΄νΈμ μ μν΄ μλμΌλ‘ λ§ν¬λ₯Ό λ³ννλ λ°©μμ μ¬μ©νκ³ μλλ°, μ΄ λ°©λ²μ λ§€μ° λ²κ±°λ‘κ³ , νΉν κΈνκ² μνμ ꡬ맀νλ €λ ꡬλ
μλ€μ΄ νΈλνΉ λ§ν¬λ₯Ό μ§μ μμ²νλ μν©λ λ°μν©λλ€.
λΈλ‘κ·Έ μμ΅νλ₯Ό μν μΏ ν‘ μν λ§ν¬ μλν ν΄ λ§λ€κΈ°
μ΄λ΄ λ, μ΄μμκ° μ§μ λ§λ μΏ ν‘ λ§ν¬ λ³νκΈ°λ₯Ό λΈλ‘κ·Έλ λ³λμ μΈλΆ νμ΄μ§μ λ°°μΉν΄ λλ©΄, ꡬλ μλ λ°©λ¬Έμ λͺ¨λκ° μμ½κ² λ³νλ λ§ν¬λ₯Ό νμ©ν μ μμ΄ μ΄μμμ μμ΅ μ°½μΆμλ λμμ΄ λ©λλ€.
μ¦, μΏ ν‘ λ§ν¬ λ³νκΈ°λ λΈλ‘κ·Έ μμ΅νλ₯Ό μΆκ΅¬νλ μ΄λ€μκ² μμ΄ λ§€μ° μ μ©ν λꡬμ λλ€.μ΄λ² κΈμμλ λꡬλ μ½κ² μμ μ μΏ ν‘ μν λ§ν¬λ₯Ό μμ΅μ© νΈλνΉ λ§ν¬λ‘ μλ λ³νν μ μλ λ°©λ²μ μκ°ν©λλ€.
μΏ ν‘ μν λ§ν¬ λ³νκΈ°μ νμμ±
μΏ ν‘ ννΈλμ€μ μμ΅μ λ¨μ ν΄λ¦μ΄ μλλΌ, ν΄λ¦ ν μ€μ μνμ ꡬ맀νμ λ λ°μν©λλ€. κ·Έλ¦¬κ³ κ·Έ ꡬ맀λ λ°λμ μ΄μμμ ννΈλμ€ μ½λ(νΈλνΉ μ½λ)κ° ν¬ν¨λ λ§ν¬λ₯Ό ν΅ν΄ μ΄λ£¨μ΄μ ΈμΌλ§ μΈμ λ©λλ€. μ¦, μ무리 μ’μ μνμ μκ°νκ³ λ°©λ¬Έμκ° λ§μλ
β€ νΈλνΉ μ½λκ° λΉ μ§ λ§ν¬λ‘ μ°κ²°λλ©΄ μμ΅μ '0μ'μ λλ€.
λ¬Έμ μ : λ§€λ² μλμΌλ‘ λ§ν¬ λ³ν?
μΏ ν‘ ννΈλμ€ κ³΅μ μ¬μ΄νΈμμ λ₯λ§ν¬(μ§§μ λ§ν¬)λ‘ λ³ννλ €λ©΄ λ€μκ³Ό κ°μ κ³Όμ μ΄ νμν©λλ€:
- μΏ ν‘ μν νμ΄μ§ μ μ
- λ§ν¬ 볡μ¬
- ννΈλμ€ μ¬μ΄νΈμ λΆμ¬λ£κΈ°
- λ₯λ§ν¬ μμ± λ²νΌ ν΄λ¦
- λ€μ 볡μ¬ν΄μ λΈλ‘κ·Έμ μ½μ
λ¨μν λ§ν¬ νλ μ½μ
νλ €κ³ 5λ¨κ³λ μλͺ¨νλ μμ
μκ°λ λ§μ΄ κ±Έλ¦¬κ³ , μ€μ κ°λ₯μ±λ λμ΅λλ€.
ν΄κ²°μ± : λ§ν¬ μλ λ³νκΈ° μ§μ ꡬμΆνκΈ°
μ΄ λ¬Έμ λ₯Ό ν΄κ²°νκΈ° μν΄ Google Apps Scriptλ₯Ό νμ©νμ¬
μΏ ν‘ μν URLμ μλμΌλ‘ λ₯λ§ν¬λ‘ λ³νν΄μ£Όλ μλ λ³νκΈ° μΉ μ±μ μ§μ ꡬμΆν μ μμ΅λλ€.
μ΄μμ λ³ΈμΈμ λΈλ‘κ·Έ κ΄λ¦¬μ νμ΄μ§λ κ°μΈ λμ보λμ μλ λꡬλ₯Ό μ½μ
ν΄λλ©΄,
β μΈμ λ μ§ λΉ λ₯΄κ² λ§ν¬ λ³ν κ°λ₯
β λ§€λ² ννΈλμ€ μ¬μ΄νΈ μ μν νμ μμ
β ν΄λ¦λ§μΌλ‘ μλ μμ΅ λ§ν¬ μμ±
μ£Όμ κΈ°λ₯ μμ½
| κΈ°λ₯ | μ€λͺ |
| μλ μλͺ μ²λ¦¬ | HMAC κΈ°λ° μλͺ μ μλ μμ±νμ¬ μΏ ν‘ API μΈμ¦ |
| API μ°λ | μΏ ν‘ λ₯λ§ν¬ APIμ μ§μ ν΅μ |
| κ²°κ³Ό UI μΆλ ₯ | λ³νλ μ§§μ URLμ μ¦μ νμνκ³ λ³΅μ¬ λ²νΌ μ 곡 |
| ν΄λΌμ΄μΈνΈ-μλ² λΆλ¦¬ | API Keyλ Google Apps Scriptμλ§ μ‘΄μ¬νλ―λ‘ λ³΄μ μ μ§ |
Google Apps Scriptμ μΏ ν‘ API νμ©
μΏ ν‘ APIλ Access Key, Secret Key, Affiliate IDκ° νμν μΈμ¦ κΈ°λ° κ΅¬μ‘°μ λλ€. μ΄λ₯Ό μν΄ μλ² μΈ‘μλ μλμ κ°μ μ€ν¬λ¦½νΈλ₯Ό μμ±ν©λλ€.

μ€μ νμ© μμ μ½λ
λΈλ‘κ·Έ κΈμ μμ±νλ©΄μ μν λ§ν¬λ₯Ό μ½μ
ν΄μΌ ν λ,
β μΏ ν‘ λ§ν¬λ₯Ό λ³΅μ¬ μλ λ³νκΈ° λΆμ¬λ£κΈ° λ³ν κ²°κ³Ό λ³΅μ¬ λΈλ‘κ·Έ λ³Έλ¬Έμ μ½μ
μ΄ κ³Όμ μ ν΅ν΄ λλ½ μμ΄ μμ΅ λ°μ κ°λ₯ λ§ν¬ νλ³΄κ° κ°λ₯ν©λλ€.
μ€μ μ μ© μ½λ 보기:λΈλ‘κ·Έ μν λ±λ‘ μ½λ μλ μμ±κΈ° μ¬μ©λ² λ° SEO μ΅μ ν
μλ μ½λλ₯Ό Google Apps Script(GS)μ κ·Έλλ‘ λ³΅μ¬ν΄ μ¬μ©νμλ©΄ λ©λλ€.
λ³κ²½μ΄ νμν λΆλΆμ μΏ ν‘μμ λ°κΈλ°μ λ³ΈμΈμ ν€ κ°μΌλ‘ μλ νλͺ©λ§ μμ νμλ©΄ λ©λλ€.
μ μ© λ°©λ²μ΄ κΆκΈνμ κ²½μ°, μ΄ λ§ν¬(μΏ ν‘ ννΈλμ€ μν μλ λ±λ‘ 1ν μμ΅ν! λΈλ‘κ·Έμ μν μλ λ±λ‘νλ λ°©λ²)λ₯Ό μ°Έκ³ νμλ©΄ λ©λλ€.
ACCESS_KEY: "123456789", // Access Keyλ₯Ό μ¬κΈ°μ μ
λ ₯
SECRET_KEY: "123456789", // Secret Keyλ₯Ό μ¬κΈ°μ μ
λ ₯ (κ°μ₯ μ€μ!)
AFFILIATE_ID: "AF1234567", //ννΈλμ€ IDλ₯Ό μ¬κΈ°μ μ
λ ₯
// ==== μ€μ κ° ( μ€μ ν€ κ° μ μ©) ====
const COUPANG_CONFIG = {
ACCESS_KEY: "123456789", // Access Keyλ₯Ό μ¬κΈ°μ μ
λ ₯
SECRET_KEY: "123456789", // Secret Keyλ₯Ό μ¬κΈ°μ μ
λ ₯ (κ°μ₯ μ€μ!)
AFFILIATE_ID: "AF1234567", //ννΈλμ€ IDλ₯Ό μ¬κΈ°μ μ
λ ₯
API_BASE_URL: "https://api-gateway.coupang.com",
DEEPLINK_PATH: "/v2/providers/affiliate_open_api/apis/openapi/v1/deeplink"
};
// ==== GMT λ μ§ νμ μμ± ν¨μ (YYMMDD'T'HHmmss'Z' ν¬λ§·) ====
function cpServer_getCoupangApiDate() {
const date = new Date();
const year = date.getUTCFullYear().toString().substring(2);
const month = (date.getUTCMonth() + 1).toString().padStart(2, '0');
const day = date.getUTCDate().toString().padStart(2, '0');
const hours = date.getUTCHours().toString().padStart(2, '0');
const minutes = date.getUTCMinutes().toString().padStart(2, '0');
const seconds = date.getUTCSeconds().toString().padStart(2, '0');
const datetime = `${year}${month}${day}T${hours}${minutes}${seconds}Z`;
Logger.log(`[cpServer_getCoupangApiDate] μμ±λ λ μ§/μκ°: "${datetime}"`);
return datetime;
}
// ==== HMAC Signature μμ± ν¨μ ====
function cpServer_generateCoupangSignature(method, uri, secretKey, accessKey) {
const parts = uri.split('?');
const path = parts[0];
const query = (parts.length === 2) ? parts[1] : '';
const datetime = cpServer_getCoupangApiDate();
const stringToSign = `${datetime}${method}${path}${query}`; // 쿼리 λ¬Έμμ΄ μμ '?' μμ
Logger.log(`[cpServer_generateCoupangSignature] μλͺ
ν λ¬Έμμ΄ (stringToSign): "${stringToSign}"`);
// SECRET_KEY μ ν¨μ± κ²μ¬ κ°ν
const actualSecretKey = String(secretKey).trim(); // String() κ°μ λ³ν λ° κ³΅λ°± μ κ±°
if (typeof actualSecretKey !== 'string' || actualSecretKey.length === 0) {
Logger.log("[cpServer_generateCoupangSignature] β μ€λ₯: SECRET_KEYκ° μ ν¨ν λ¬Έμμ΄μ΄ μλκ±°λ λΉμ΄ μμ΅λλ€. Apps Script μ€μ κ°μ νμΈνμΈμ.");
throw new Error("Invalid or empty SECRET_KEY configured in Apps Script.");
}
try {
const signatureBytes = Utilities.computeHmacSha256Signature(stringToSign, actualSecretKey);
const signatureHex = cpServer_bytesToHex(signatureBytes);
Logger.log(`[cpServer_generateCoupangSignature] μμ±λ μκ·Έλμ² (Hex): ${signatureHex}`);
const authorizationHeader = `CEA algorithm=HmacSHA256, access-key=${accessKey}, signed-date=${datetime}, signature=${signatureHex}`;
Logger.log(`[cpServer_generateCoupangSignature] μ΅μ’
Authorization ν€λ: "${authorizationHeader}"`);
return authorizationHeader;
} catch (e) {
Logger.log(`[cpServer_generateCoupangSignature] β μΉλͺ
μ μ€λ₯: HMAC μλͺ
μμ± μ€ μμΈ λ°μ. SECRET_KEY μ€μ νμΈ. λ©μμ§: ${e.message}`);
throw new Error(`Failed to generate HMAC signature. Please check SECRET_KEY in Apps Script: ${e.message}`);
}
}
// ==== λ°μ΄νΈ λ°°μ΄μ 16μ§μ λ¬Έμμ΄λ‘ λ³ν ====
function cpServer_bytesToHex(bytes) {
if (!bytes) {
Logger.log("[cpServer_bytesToHex] β κ²½κ³ : bytes μΈμκ° undefined μ΄κ±°λ null μ
λλ€. μ΄λ SECRET_KEY λ¬Έμ λ‘ μΈν΄ μλͺ
λ°μ΄νΈκ° μμ±λμ§ μμμ μ μμ΅λλ€.");
return "";
}
return Array.from(bytes, function(byte) {
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}
// ==== λ₯λ§ν¬ λ³ν API νΈμΆ ν¨μ (μ£Όμ λ‘μ§) ====
function cpServer_callCoupangDeeplinkApi(originalUrl) {
const method = 'POST'; // λ₯λ§ν¬ APIλ POST μμ²
const requestBody = {
coupangUrls: [originalUrl]
};
// URIλ DEEPLINK_PATHμ _E_ νλΌλ―Έν°λ₯Ό ν¬ν¨
const uri = `${COUPANG_CONFIG.DEEPLINK_PATH}?_E_=${COUPANG_CONFIG.AFFILIATE_ID}`;
const url = COUPANG_CONFIG.API_BASE_URL + uri;
Logger.log(`[cpServer_callCoupangDeeplinkApi] μλ³Έ URL: ${originalUrl}`);
Logger.log(`[cpServer_callCoupangDeeplinkApi] λ₯λ§ν¬ μμ² URI: ${uri}`);
Logger.log(`[cpServer_callCoupangDeeplinkApi] λ₯λ§ν¬ μμ² URL: ${url}`);
Logger.log(`[cpServer_callCoupangDeeplinkApi] μμ² λ³Έλ¬Έ: ${JSON.stringify(requestBody)}`);
try {
const authorizationHeader = cpServer_generateCoupangSignature(method, uri, COUPANG_CONFIG.SECRET_KEY, COUPANG_CONFIG.ACCESS_KEY);
const options = {
method: method,
headers: {
'Authorization': authorizationHeader,
'Content-Type': 'application/json;charset=UTF-8',
},
payload: JSON.stringify(requestBody),
muteHttpExceptions: true // HTTP μ€λ₯ μ μμΈ λ°μ λμ μλ΅ κ°μ²΄ λ°ν
};
const response = UrlFetchApp.fetch(url, options);
const responseCode = response.getResponseCode();
const responseBody = response.getContentText();
Logger.log(`[cpServer_callCoupangDeeplinkApi] μλ΅ μ½λ: ${responseCode}`);
Logger.log(`[cpServer_callCoupangDeeplinkApi] μλ΅ λ³Έλ¬Έ: ${responseBody}`);
if (responseCode === 200) {
const responseJson = JSON.parse(responseBody);
// rCodeκ° '0' λλ '0000' (λ μ€ νλλΌλ) μΌ λ μ±κ³΅μΌλ‘ μ²λ¦¬
if ((responseJson.rCode === '0' || responseJson.rCode === '0000') && responseJson.data && responseJson.data.length 0) {
return { success: true, shortenUrl: responseJson.data[0].shortenUrl };
} else {
// μΏ ν‘ API μλ΅μ 200μ΄μ§λ§ rCodeκ° μ±κ³΅μ΄ μλκ±°λ λ°μ΄ν°κ° μλ κ²½μ°
Logger.log(`[cpServer_callCoupangDeeplinkApi] β μΏ ν‘ API μλ΅ μ€λ₯ (rCode != 0 λλ 0000 λλ λ°μ΄ν° μμ): ${responseBody}`);
return { success: false, error: `μΏ ν‘ API μλ΅ μ€λ₯: ${responseJson.message || responseBody || 'μ μ μμ'}` };
}
} else {
// HTTP μ€λ₯ λ°μ (200 OKκ° μλ κ²½μ°)
let errorMessage = `HTTP μ€λ₯ (${responseCode}): `;
try {
const errorJson = JSON.parse(responseBody);
if (errorJson.code && errorJson.message) {
errorMessage += `μ½λ: ${errorJson.code}, λ©μμ§: ${errorJson.message}`;
} else {
errorMessage += `μλ΅ λ³Έλ¬Έ: ${responseBody}`; // JSON νμ± μ€ν¨ μ μλ³Έ λ³Έλ¬Έ ν¬ν¨
}
} catch (e) {
errorMessage += `μλ΅ λ³Έλ¬Έ νμ± μ€ν¨ λλ λ³Έλ¬Έ μμ: ${responseBody}`;
}
Logger.log(`[cpServer_callCoupangDeeplinkApi] β HTTP μ€λ₯ μμΈ: ${errorMessage}`);
return { success: false, error: `HTTP μ€λ₯: ${errorMessage}` };
}
} catch (e) {
// UrlFetchApp μ체 μμΈ λλ μλͺ
μμ± μ€ μμΈ λ°μ
Logger.log(`[cpServer_callCoupangDeeplinkApi] β Apps Script μλ² μμΈ λ°μ: ${e.message}`);
return { success: false, error: `Apps Script μλ² μ€λ₯: ${e.message}` };
}
}
// ==== JSON μλ΅ μμ± ν¬νΌ ν¨μ ====
function cpServer_createJsonResponse(data) {
return ContentService.createTextOutput(JSON.stringify(data))
.setMimeType(ContentService.MimeType.JSON);
}
// ==== μΉ μ± GET μμ² μ²λ¦¬ (ν΄λΌμ΄μΈνΈμ μμ²μ λ°μ) ====
function doGet(e) {
Logger.log("[doGet] GET μμ² μμ μμ.");
const rawUrls = e.parameter.urls;
let urlsToProcess = [];
if (Array.isArray(rawUrls) && rawUrls.length 0) {
urlsToProcess = [rawUrls[0]]; // λ°°μ΄λ‘ λ€μ΄μ¨ κ²½μ° μ²« λ²μ§Έλ§
} else if (typeof rawUrls === 'string' && rawUrls) {
urlsToProcess = [rawUrls]; // λ¨μΌ λ¬Έμμ΄λ‘ λ€μ΄μ¨ κ²½μ°
} else {
Logger.log("[doGet] μ€λ₯: λ³νν URLμ΄ GET μμ² νλΌλ―Έν°μ μμ΅λλ€.");
return cpServer_createJsonResponse({ success: false, error: "λ³νν URLμ μ°Ύμ μ μμ΅λλ€." });
}
const originalUrl = urlsToProcess[0];
Logger.log(`[doGet] μ²λ¦¬ν URL: ${originalUrl}`);
const apiResult = cpServer_callCoupangDeeplinkApi(originalUrl);
if (apiResult.success) {
return cpServer_createJsonResponse({ success: true, shortenUrl: apiResult.shortenUrl });
} else {
return cpServer_createJsonResponse({ success: false, error: apiResult.error });
}
}
// ==== μΉ μ± POST μμ² μ²λ¦¬ (μ΄ μ±μμλ μ¬μ©λμ§ μμ) ====
function doPost(e) {
Logger.log("[doPost] POST μμ²μ΄ κ°μ§λμμ§λ§, μ΄ μΉ μ±μ λ₯λ§ν¬ λ³νμ μν΄ GET μμ²μ μ²λ¦¬ν©λλ€.");
return cpServer_createJsonResponse({ success: false, error: "μ΄ μΉ μ±μ λ₯λ§ν¬ λ³νμ μν΄ GET μμ²μ μ²λ¦¬ν©λλ€." });
}
β μ£Όμ: ACCESS_KEY, SECRET_KEY, AFFILIATE_IDλ λ°λμ κ°μΈ μ λ³΄λ‘ λ³΄νΈλμ΄μΌ νλ©°, μΈλΆμ λ ΈμΆλμ§ μλλ‘ Google Apps Scriptμμλ§ κ΄λ¦¬νμΈμ.
μ¬μ©μ μΈν°νμ΄μ€ μ μ© λ°©λ² (μ€μ μ μ© μ½λ ν¬ν¨)
HTML, CSS, JavaScriptλ‘ κ΅¬μ±λ μΈν°νμ΄μ€λ μλμ κ°μ΅λλ€:
μλ HTML μ½λλ μνμλ μμΉμ μ μ ν μ½μ νμ¬ νμ©νμλ©΄ λ©λλ€.
div class="coupang-converter"
h1μΏ ν‘ ννΈλμ€ λ§ν¬ λ³νκΈ°/h1
pμΏ ν‘ μν URLμ μ
λ ₯νμΈμ (νλλ§ μ
λ ₯):/p
div class="input-area"
textarea class="url-input" placeholder="μ¬κΈ°μ μΏ ν‘ μν URLμ μ
λ ₯νμΈμ."/textarea
/div
div class="action-buttons"
button class="convert-btn" onclick="cpClient_convertLinks()"λ§ν¬ λ³ν/button
button class="reset-btn" onclick="cpClient_clearAll()"μ΄κΈ°ν/button
/div
div class="result-container" id="cpClient_result"/div
/div
λ³νλ κ²°κ³Όλ μλμ κ°μ΄ μΆλ ₯λ©λλ€:
https://link.coupang.com/shorten/xxxxxxxx μμ΅μ΄ λ°μνλ λ₯λ§ν¬
[λ§ν¬ 볡μ¬]
μλ° μ€ν¬λ¦½νΈ μ½λ
Google Apps Script μΉ μ± URLμ μ¬κΈ°μ λΆμ¬λ£μΌμΈμ!
constAPI_ENDPOINT="ꡬκΈappscriptμΉμ£Όμ";
script
// === Configuration ===
// Google Apps Script μΉ μ± URLμ μ¬κΈ°μ λΆμ¬λ£μΌμΈμ!
const API_ENDPOINT = "κ΅¬κΈ app script μΉμ£Όμ";
// === DOM Element References ===
const cpClient_elements = {
urlInput: document.querySelector('.coupang-converter .url-input'),
resultDiv: document.getElementById('cpClient_result'),
convertBtn: document.querySelector('.coupang-converter .convert-btn'),
resetBtn: document.querySelector('.coupang-converter .reset-btn')
};
// === Main Link Conversion Function ===
async function cpClient_convertLinks() {
cpClient_setLoadingState(true);
const inputUrlsText = cpClient_elements.urlInput.value.trim();
if (!inputUrlsText) {
cpClient_showResult('error', 'λ³νν μΏ ν‘ μν URLμ μ
λ ₯ν΄μ£ΌμΈμ.');
cpClient_setLoadingState(false);
return;
}
const firstValidUrl = inputUrlsText.split('\n')
.map(url = url.trim())
.find(url = url.startsWith('http') && url.includes('coupang.com'));
if (!firstValidUrl) {
cpClient_showResult('error', 'μ ν¨ν μΏ ν‘ μν URLμ μ°Ύμ μ μμ΅λλ€. μ€μ μΏ ν‘ μν νμ΄μ§ λ§ν¬λ₯Ό μ
λ ₯ν΄μ£ΌμΈμ.');
cpClient_setLoadingState(false);
return;
}
try {
const queryParams = new URLSearchParams();
queryParams.append('urls', firstValidUrl);
const fetchUrl = `${API_ENDPOINT}?${queryParams.toString()}`;
console.log('ν΄λΌμ΄μΈνΈ fetch URL (GET):', fetchUrl); // λλ²κΉ
μ© λ‘κ·Έ
const response = await fetch(fetchUrl);
if (!response.ok) {
const errorDetail = await response.text();
cpClient_showResult('error', `μ€λ₯ λ°μ: ${response.status} - ${errorDetail}. μΉ μ± URL λ° λ°°ν¬ κΆνμ νμΈν΄μ£ΌμΈμ.`);
throw new Error(`Server responded with an error: HTTP ${response.status} - ${errorDetail}`);
}
const responseData = await response.json();
if (responseData.success && responseData.shortenUrl) {
cpClient_showConvertedLink(responseData.shortenUrl);
} else {
// μλ²μμ success: falseλ₯Ό λ°ννκ±°λ shortenUrlμ΄ μλ κ²½μ°
cpClient_showResult('error', responseData.error || 'λ§ν¬ λ³ν μ€ν¨. μ
λ ₯ν URLμ΄ μ ννμ§ νμΈν΄μ£ΌμΈμ.');
console.error('μλ² μλ΅ μ€λ₯:', responseData.error);
}
} catch (error) {
// λ€νΈμν¬ μ€λ₯ λλ κΈ°ν μμμΉ λͺ»ν μ€λ₯
cpClient_showResult('error', `μ€λ₯ λ°μ: ${error.message}. μΉ μ± URL λ° λ°°ν¬ κΆνμ νμΈν΄μ£ΌμΈμ.`);
console.error('Fetch Error:', error);
} finally {
cpClient_setLoadingState(false);
}
}
function cpClient_showConvertedLink(shortenUrl) {
const resultDiv = cpClient_elements.resultDiv;
resultDiv.className = 'result-container success';
const htmlContent = `
pλ³ν κ²°κ³Ό/p
div class="link-item"
div class="converted-link"
a href="${shortenUrl}" target="_blank"${shortenUrl}/a
/div
button class="copy-btn" onclick="cpClient_copyToClipboard('${shortenUrl}')"λ§ν¬ 볡μ¬/button
/div
`;
resultDiv.innerHTML = htmlContent;
}
function cpClient_showResult(type, message) {
const resultDiv = cpClient_elements.resultDiv;
resultDiv.className = `result-container ${type}`;
resultDiv.innerHTML = `p${message}/p`;
}
// === μ΄κΈ°ν ν¨μ ===
function cpClient_clearAll() {
cpClient_elements.urlInput.value = '';
cpClient_elements.resultDiv.innerHTML = '';
cpClient_elements.resultDiv.className = 'result-container';
}
function cpClient_copyToClipboard(text) {
navigator.clipboard.writeText(text)
.then(() = alert('λ§ν¬κ° ν΄λ¦½λ³΄λμ 볡μ¬λμμ΅λλ€!'))
.catch(err = {
console.error('λ³΅μ¬ μ€ν¨:', err);
alert('λ§ν¬ 볡μ¬μ μ€ν¨νμ΅λλ€. μλμΌλ‘ 볡μ¬ν΄μ£ΌμΈμ.');
});
}
function cpClient_setLoadingState(isLoading) {
const resultDiv = cpClient_elements.resultDiv;
const convertBtn = cpClient_elements.convertBtn;
const resetBtn = cpClient_elements.resetBtn;
if (isLoading) {
resultDiv.innerHTML = 'div class="loading"span class="spinner"/span μ²λ¦¬ μ€.../div';
convertBtn.disabled = true;
resetBtn.disabled = true;
} else {
convertBtn.disabled = false;
resetBtn.disabled = false;
}
}
/script
CSS μ μ© λ°©λ²
λ°©λ² 1: HTML λͺ¨λμ μ§μ μ½μ
- λΈλ‘κ·Έ κΈ μμ± νλ©΄μμ HTML νΈμ§ λͺ¨λ μ ν
style.../styleνκ·Έ μ 체λ₯Ό κΈ μλ¨ λλ λ³Έλ¬Έ μ€κ°μ μ½μ
λ°©λ² 2: μ€ν¨μ μꡬ μ μ© (μ¬λ¬ κΈμ κ³΅ν΅ μ μ©)
- κ΄λ¦¬μ μ€ν¨ νΈμ§
style.cssλλcustom.cssνμΌ μ΄κΈ° .coupang-converterμ΄ν μ 체 CSS λΆμ¬λ£κΈ°- μ΄ν κΈμμλ
styleμμ΄div class="coupang-converter".../divλ§ μ¬μ©
style
/* μΏ ν‘ ννΈλμ€ λ³νκΈ° μ 체 컨ν
μ΄λ μ€νμΌ */
.coupang-converter {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 20px auto; /* μν λ§μ§ μΆκ°, μ€μ μ λ ¬ */
padding: 20px;
background: #f5f5f5;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
box-sizing: border-box; /* ν¨λ©, borderκ° widthμ ν¬ν¨λλλ‘ */
}
/* μ λͺ© μ€νμΌ */
.coupang-converter h1 {
font-size: 20px;
margin-top: 0;
margin-bottom: 15px;
color: #333;
text-align: center;
}
/* μ€λͺ
문ꡬ μ€νμΌ */
.coupang-converter p {
font-size: 14px;
color: #555;
margin-bottom: 15px;
text-align: center;
}
/* μ
λ ₯ μμ μ€νμΌ */
.coupang-converter .input-area {
margin-bottom: 20px;
}
.coupang-converter .url-input {
width: 100%;
height: 120px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical; /* μΈλ‘ ν¬κΈ° μ‘°μ κ°λ₯ */
box-sizing: border-box; /* ν¨λ©μ΄ λλΉμ ν¬ν¨λλλ‘ */
font-size: 14px;
}
/* λ²νΌ 컨ν
μ΄λ μ€νμΌ */
.coupang-converter .action-buttons {
display: flex; /* λ²νΌμ κ°λ‘λ‘ μ λ ¬ */
gap: 10px; /* λ²νΌ μ¬μ΄ κ°κ²© */
margin-bottom: 20px;
justify-content: center; /* λ²νΌ μ€μ μ λ ¬ */
}
/* κ³΅ν΅ λ²νΌ μ€νμΌ */
.coupang-converter .convert-btn,
.coupang-converter .reset-btn {
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s ease; /* νΈλ² ν¨κ³Ό λΆλλ½κ² */
}
.coupang-converter .convert-btn {
background: #4285f4; /* κ΅¬κΈ λΈλ£¨ μμ */
color: white;
}
.coupang-converter .convert-btn:hover {
background: #3367d6; /* λ§μ°μ€ μ€λ² μ μ΄λμ΄ λΈλ£¨ */
}
.coupang-converter .reset-btn {
background: #e0e0e0; /* μ°ν νμ */
color: #333;
}
.coupang-converter .reset-btn:hover {
background: #c7c7c7; /* λ§μ°μ€ μ€λ² μ μ½κ° μ§ν νμ */
}
/* κ²°κ³Ό 컨ν
μ΄λ μ€νμΌ */
.coupang-converter .result-container {
min-height: 50px; /* κ²°κ³Όμ°½ μ΅μ λμ΄ */
padding: 15px;
border-radius: 4px;
overflow-wrap: break-word; /* κΈ΄ URLμ΄ λμΉμ§ μλλ‘ */
}
/* μ±κ³΅ λ©μμ§ μ€νμΌ */
.coupang-converter .result-container.success {
background: #e8f5e9; /* μ°ν μ΄λ‘μ */
border: 1px solid #c8e6c9;
}
/* μ€λ₯ λ©μμ§ μ€νμΌ */
.coupang-converter .result-container.error {
background: #ffebee; /* μ°ν λΉ¨κ°μ */
border: 1px solid #ffcdd2;
color: #d32f2f; /* μ€λ₯ λ©μμ§ ν
μ€νΈ μμ */
}
/* κ°λ³ λ§ν¬ μμ΄ν
μ€νμΌ (λ¨μΌ URL μ²λ¦¬μ λ§κ² κ°μν) */
.coupang-converter .link-item {
padding: 10px;
background: white;
border-radius: 4px;
border: 1px solid #ddd;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
text-align: center; /* μ€μ μ λ ¬ */
}
.coupang-converter .converted-link {
word-break: break-all;
margin: 5px 0;
font-size: 14px;
color: #4285f4;
font-weight: bold;
}
/* λ³΅μ¬ λ²νΌ μ€νμΌ */
.coupang-converter .copy-btn {
background: #34a853;
color: white;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
margin-top: 10px; /* μμͺ½ μ¬λ°± λλ¦Ό */
font-size: 12px;
transition: background-color 0.3s ease;
}
.coupang-converter .copy-btn:hover {
background: #2b8c3f;
}
/* λ‘λ© μ€νΌλ μ€νμΌ */
.coupang-converter .loading {
text-align: center;
padding: 20px;
font-size: 16px;
color: #555;
}
.coupang-converter .spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(0,0,0,0.1);
border-radius: 50%;
border-top-color: #4285f4;
animation: spin 1s linear infinite;
margin-right: 10px;
vertical-align: middle;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/style
μλνλ μ νμ΄ μλ νμ
μμ΅ν λΈλ‘κ·Έλ₯Ό μ΄μνλ€λ©΄, λ°λ³΅μ μΈ μμμ μ μλννλ κ²μ΄ κ³§ μμ°μ±κ³Ό μμ΅μ κ²°μ μ§λ μμμ λλ€. μ΄λ²μ μκ°ν μΏ ν‘ ννΈλμ€ μλ λ§ν¬ λ³νκΈ°λ μ΄μμμ νΈμμ±μ κ·Ήλννλ©΄μλ μμ΅ λλ½μ λ°©μ§ν μ μλ κ°λ ₯ν λꡬμ λλ€.
μΏ ν‘ μν λ§ν¬λ₯Ό μ μλμΌλ‘ λ³νν΄μΌ νλμ?
μΏ ν‘ μμ΅μ νΈλνΉ μ½λκ° ν¬ν¨λ λ§ν¬λ₯Ό ν΅ν΄ κ΅¬λ§€κ° μ΄λ£¨μ΄μ§ λλ§ λ°μν©λλ€. νμ§λ§ λ§€λ² μλμΌλ‘ λ§ν¬λ₯Ό λ³ννλ 건 λ²κ±°λ‘κ³ μ€μνκΈ° μ½μ΅λλ€. μλ λ³νκΈ°λ₯Ό μ¬μ©νλ©΄ μμ΅ λλ½μ λ°©μ§νκ³ λ§ν¬ μ½μ μ κ°νΈνκ² μ²λ¦¬ν μ μμ΅λλ€.
λ§ν¬ μλ λ³νκΈ°λ μ΄λ»κ² λ§λ€ μ μλμ?
Google Apps Scriptλ₯Ό νμ©ν΄ μΏ ν‘ APIμ μ°λλλ λ₯λ§ν¬ μλ λ³ν μΉ μ±μ λ§λ€ μ μμ΅λλ€. λ³κ²½ν 건 ACCESS_KEY, SECRET_KEY, AFFILIATE_IDλ§μ΄λ©°, μ½λλ₯Ό 볡μ¬ν΄ λΆμ¬λ£κΈ°λ§ νλ©΄ λ°λ‘ μλν©λλ€.
μλ λ§ν¬ λ³νκΈ°λ₯Ό μ¬μ©νλ©΄ μ΄λ€ μ μ΄ μ’μμ§λμ?
μλ λ§ν¬ λ³νκΈ°λ₯Ό μ¬μ©νλ©΄ λ§€λ² μΏ ν‘ ννΈλμ€ μ¬μ΄νΈμ μ μν νμ μμ΄ ν΄λ¦ ν λ²μΌλ‘ μν λ§ν¬λ₯Ό μμ΅ λ°μ κ°λ₯ν λ₯λ§ν¬λ‘ κ°νΈνκ² λ³νν μ μμ΅λλ€. λν, λΈλ‘κ·Έ κ΄λ¦¬μ νμ΄μ§λ κ°μΈ λμ보λμ λ³νκΈ°λ₯Ό μ½μ ν΄λλ©΄ μΈμ λ λΉ λ₯΄κ² λ§ν¬λ₯Ό λ§λ€ μ μμ΄ μ΄μ ν¨μ¨μ±μ΄ ν¬κ² ν₯μλ©λλ€. μ΄λ‘ μΈν΄ μμ΅ λλ½ μνμ΄ μ€μ΄λ€κ³ , λ°©λ¬Έμλ€λ μμ½κ² λ³νλ λ§ν¬λ₯Ό νμ©ν μ μμ΄ κ²°κ΅ μ΄μμμ μμ΅ μ¦λμ ν° λμμ΄ λ©λλ€.