Published:

Last Updated:

쿠팑 νŒŒνŠΈλ„ˆμŠ€ 수읡 κ·ΉλŒ€ν™”λ₯Ό μœ„ν•œ μžλ™ 링크 λ³€ν™˜κΈ° ꡬ좕 방법

쿠팑 νŒŒνŠΈλ„ˆμŠ€ 링크 λ³€ν™˜κΈ° ꡬ좕 방법 μ•ˆλ‚΄. 반볡 μž‘μ—… 없이 λΈ”λ‘œκ·Έμ—μ„œ μžλ™μœΌλ‘œ 쿠팑 μƒν’ˆ 링크λ₯Ό 수읡용 λ”₯링크둜 λ³€ν™˜ν•΄ 수읡 λˆ„λ½μ„ λ°©μ§€ν•©λ‹ˆλ‹€.


쿠팑 νŒŒνŠΈλ„ˆμŠ€λ₯Ό 톡해 λΈ”λ‘œκ·Έ μˆ˜μ΅ν™”λ₯Ό μ‹œλ„ν•˜λŠ” 운영자라면, λˆ„κ΅¬λ‚˜ ν•œ λ²ˆμ―€μ€ κ²ͺ어봀을 λΆˆνŽΈν•¨μ΄ μžˆμŠ΅λ‹ˆλ‹€. λ°”λ‘œ 쿠팑 μƒν’ˆ 링크λ₯Ό νŠΈλž˜ν‚Ή μ½”λ“œλ‘œ λ³€ν™˜ν•˜λŠ” κ³Όμ •μ˜ λ²ˆκ±°λ‘œμ›€μž…λ‹ˆλ‹€.

맀번 μƒν’ˆμ„ μ„ μ •ν•œ ν›„, ν•΄λ‹Ή μƒν’ˆ 링크λ₯Ό μžμ‹ μ˜ 쿠팑 νŒŒνŠΈλ„ˆμŠ€ νŠΈλž˜ν‚Ή μ½”λ“œλ‘œ λ³€ν™˜ν•΄μ•Όλ§Œ 수읡이 μ •μƒμ μœΌλ‘œ λ°œμƒν•©λ‹ˆλ‹€.
λ§Œμ•½ 링크에 운영자의 νŠΈλž˜ν‚Ή μ½”λ“œκ°€ ν¬ν•¨λ˜μ–΄ μžˆμ§€ μ•Šλ‹€λ©΄, μƒν’ˆμ΄ 아무리 많이 νŒ”λ €λ„ μˆ˜μ΅μ€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λŒ€λΆ€λΆ„μ˜ μš΄μ˜μžλŠ” 쿠팑 νŒŒνŠΈλ„ˆμŠ€ 곡식 μ‚¬μ΄νŠΈμ— 접속해 μˆ˜λ™μœΌλ‘œ 링크λ₯Ό λ³€ν™˜ν•˜λŠ” 방식을 μ‚¬μš©ν•˜κ³  μžˆλŠ”λ°, 이 방법은 맀우 번거둭고, 특히 κΈ‰ν•˜κ²Œ μƒν’ˆμ„ κ΅¬λ§€ν•˜λ €λŠ” κ΅¬λ…μžλ“€μ΄ νŠΈλž˜ν‚Ή 링크λ₯Ό 직접 μš”μ²­ν•˜λŠ” 상황도 λ°œμƒν•©λ‹ˆλ‹€.

λΈ”λ‘œκ·Έ μˆ˜μ΅ν™”λ₯Ό μœ„ν•œ 쿠팑 μƒν’ˆ 링크 μžλ™ν™” 툴 λ§Œλ“€κΈ°

이럴 λ•Œ, μš΄μ˜μžκ°€ 직접 λ§Œλ“  쿠팑 링크 λ³€ν™˜κΈ°λ₯Ό λΈ”λ‘œκ·Έλ‚˜ λ³„λ„μ˜ μ™ΈλΆ€ νŽ˜μ΄μ§€μ— λ°°μΉ˜ν•΄ 두면, κ΅¬λ…μžλ‚˜ 방문자 λͺ¨λ‘κ°€ μ†μ‰½κ²Œ λ³€ν™˜λœ 링크λ₯Ό ν™œμš©ν•  수 μžˆμ–΄ 운영자의 수읡 μ°½μΆœμ—λ„ 도움이 λ©λ‹ˆλ‹€.

즉, 쿠팑 링크 λ³€ν™˜κΈ°λŠ” λΈ”λ‘œκ·Έ μˆ˜μ΅ν™”λ₯Ό μΆ”κ΅¬ν•˜λŠ” μ΄λ“€μ—κ²Œ μžˆμ–΄ 맀우 μœ μš©ν•œ λ„κ΅¬μž…λ‹ˆλ‹€.이번 κΈ€μ—μ„œλŠ” λˆ„κ΅¬λ‚˜ μ‰½κ²Œ μžμ‹ μ˜ 쿠팑 μƒν’ˆ 링크λ₯Ό 수읡용 νŠΈλž˜ν‚Ή 링크둜 μžλ™ λ³€ν™˜ν•  수 μžˆλŠ” 방법을 μ†Œκ°œν•©λ‹ˆλ‹€.

쿠팑 μƒν’ˆ 링크 λ³€ν™˜κΈ°μ˜ ν•„μš”μ„±

쿠팑 νŒŒνŠΈλ„ˆμŠ€μ˜ μˆ˜μ΅μ€ λ‹¨μˆœ 클릭이 μ•„λ‹ˆλΌ, 클릭 ν›„ μ‹€μ œ μƒν’ˆμ„ κ΅¬λ§€ν–ˆμ„ λ•Œ λ°œμƒν•©λ‹ˆλ‹€. 그리고 κ·Έ κ΅¬λ§€λŠ” λ°˜λ“œμ‹œ 운영자의 νŒŒνŠΈλ„ˆμŠ€ μ½”λ“œ(νŠΈλž˜ν‚Ή μ½”λ“œ)κ°€ ν¬ν•¨λœ 링크λ₯Ό 톡해 μ΄λ£¨μ–΄μ Έμ•Όλ§Œ μΈμ •λ©λ‹ˆλ‹€. 즉, 아무리 쒋은 μƒν’ˆμ„ μ†Œκ°œν•˜κ³  λ°©λ¬Έμžκ°€ λ§Žμ•„λ„

➀ νŠΈλž˜ν‚Ή μ½”λ“œκ°€ λΉ μ§„ 링크둜 μ—°κ²°λ˜λ©΄ μˆ˜μ΅μ€ '0원'μž…λ‹ˆλ‹€.

문제점: 맀번 μˆ˜λ™μœΌλ‘œ 링크 λ³€ν™˜?

쿠팑 νŒŒνŠΈλ„ˆμŠ€ 곡식 μ‚¬μ΄νŠΈμ—μ„œ λ”₯링크(짧은 링크)둜 λ³€ν™˜ν•˜λ €λ©΄ λ‹€μŒκ³Ό 같은 과정이 ν•„μš”ν•©λ‹ˆλ‹€:

  1. 쿠팑 μƒν’ˆ νŽ˜μ΄μ§€ 접속
  2. 링크 볡사
  3. νŒŒνŠΈλ„ˆμŠ€ μ‚¬μ΄νŠΈμ— λΆ™μ—¬λ„£κΈ°
  4. λ”₯링크 생성 λ²„νŠΌ 클릭
  5. λ‹€μ‹œ λ³΅μ‚¬ν•΄μ„œ λΈ”λ‘œκ·Έμ— μ‚½μž…

λ‹¨μˆœνžˆ 링크 ν•˜λ‚˜ μ‚½μž…ν•˜λ €κ³  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만이며, μ½”λ“œλ₯Ό 볡사해 λΆ™μ—¬λ„£κΈ°λ§Œ ν•˜λ©΄ λ°”λ‘œ μž‘λ™ν•©λ‹ˆλ‹€.

μžλ™ 링크 λ³€ν™˜κΈ°λ₯Ό μ‚¬μš©ν•˜λ©΄ μ–΄λ–€ 점이 μ’‹μ•„μ§€λ‚˜μš”?

μžλ™ 링크 λ³€ν™˜κΈ°λ₯Ό μ‚¬μš©ν•˜λ©΄ 맀번 쿠팑 νŒŒνŠΈλ„ˆμŠ€ μ‚¬μ΄νŠΈμ— 접속할 ν•„μš” 없이 클릭 ν•œ 번으둜 μƒν’ˆ 링크λ₯Ό 수읡 λ°œμƒ κ°€λŠ₯ν•œ λ”₯링크둜 κ°„νŽΈν•˜κ²Œ λ³€ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, λΈ”λ‘œκ·Έ κ΄€λ¦¬μž νŽ˜μ΄μ§€λ‚˜ 개인 λŒ€μ‹œλ³΄λ“œμ— λ³€ν™˜κΈ°λ₯Ό μ‚½μž…ν•΄λ‘λ©΄ μ–Έμ œλ“  λΉ λ₯΄κ²Œ 링크λ₯Ό λ§Œλ“€ 수 μžˆμ–΄ 운영 νš¨μœ¨μ„±μ΄ 크게 ν–₯μƒλ©λ‹ˆλ‹€. 이둜 인해 수읡 λˆ„λ½ μœ„ν—˜μ΄ 쀄어듀고, λ°©λ¬Έμžλ“€λ„ μ†μ‰½κ²Œ λ³€ν™˜λœ 링크λ₯Ό ν™œμš©ν•  수 μžˆμ–΄ κ²°κ΅­ 운영자의 수읡 μ¦λŒ€μ— 큰 도움이 λ©λ‹ˆλ‹€.