API를 사용하여 카탈로그 생성 및 업데이트 자동화

참고
PlayFab, App Store 또는 Google Play와 같은 외부 시스템을 사용하는 경우, 해당 시스템에서 데이터를 가져오려면 지침을 따르십시오.

샵 빌더 API 호출을 통해 카탈로그 생성 및 업데이트를 자동화할 수 있습니다. 자동화를 통해 많은 시간을 들이지 않고도 카탈로그를 최신 상태로 유지할 수 있습니다.

이를 통해 유지 관리 및 업데이트에 소요되는 시간이 단축됩니다:

  • 많은 아이템을 포함하는 카탈로그
  • 현지 가격
알림

사용자 참여를 유지하려면 아이템 카탈로그를 생성한 후 엑솔라 측에서 최신 상태로 유지해야 합니다. 아이템을 추가하거나 가격을 변경하는 등 업데이트를 수행한 경우 엑솔라 측에서 카탈로그를 업데이트하는 것이 좋습니다.

수행 가능 작업:

기본 인증은 아이템과 프로모션을 생성하고 업데이트하는 API 호출에 사용됩니다. Authorization:Basic <your_authorization_basic_key>를 전달합니다. 여기에서 <your_authorization_basic_key>는 Base64 표준으로 인코딩된 판매자 ID:API 키 쌍입니다. 이러한 매개 변수를 찾으려면 관리자 페이지으로 이동합니다.

  • 판매자 ID 표시 위치:
    • 회사 설정 > 회사 섹션.
    • 관리자 페이지의 브라우저 주소 표시줄에 있는 URL. 해당 URL은 https://publisher.xsolla.com/<merchant_id>/ 형식으로 되어 있습니다.

아이템 생성 및 업데이트

엑솔라는 다음과 같은 유형의 인게임 아이템을 지원합니다:

  • 가상 아이템
  • 인게임 재화
  • 인게임 재화 패키지
  • 번들

통합을 간소화하기 위해(예: 모바일 애플리케이션에서) 가상 아이템을 사용할 수 있습니다. 이는 모든 인게임 구매 로직을 구현할 수 있는 범용 유형입니다. 이를 통해 데이터 처리를 통합하고 카탈로그 아키텍처의 중복을 방지할 수 있습니다.

다수의 아이템을 생성해야 하는 경우 필요한 아이템 유형의 API 메서드를 필요한 횟수만큼 호출하는 스크립트 생성을 수행할 수 있습니다.

알림

아이템 요청에 대한 응답으로 반환되는 매개 변수 목록은 카탈로그를 업데이트할 때 전달해야 하는 매개 변수 목록과 다릅니다. 필수 및 업데이트된 매개 변수 외에도 아이템 요청에 대한 응답으로 반환되는 아이템 업데이트 호출에서 매개 변수를 전달해 주세요.

예시:

가상 아이템 가져오기 호출에서는 사용자 제한 데이터가 포함된 limits 개체가 반환됩니다. 아이템의 가격이나 이름만 업데이트하려면 가상 아이템 업데이트 호출에서 limits 개체의 현재 데이터를 전달해 주세요. limits 개체를 전달하지 않으면 가상 아이템 업데이트 호출로 아이템을 업데이트할 때 제한 데이터가 삭제됩니다.

카탈로그에 아이템을 추가하려면 다음 API 호출을 사용하십시오:

카탈로그를 업데이트 방법:

  1. 다음 API 호출을 통해 카탈로그에서 데이터를 가져옵니다:
  2. 다음 API 호출을 통해 새 매개 변수 값을 전달하십시오:

API를 통한 자동 아이템 생성 스크립트

시스템에서 데이터를 기반으로 다수의 아이템을 생성해야 하는 경우 API를 사용하여 이 프로세스를 자동화할 수 있습니다.

필요한 작업:

아이템 그룹을 사용하려면 관리자 페이지에서 아이템 그룹을 미리 생성하세요.

여러 유형의 아이템을 사용하려면 다음 순서로 생성해야 합니다.

  1. 관리자 페이지의 아이템 그룹
  2. 인게임 재화
  3. 가상 아이템
  4. 인게임 재화 패키지.
  5. 번들

다음은 가상 아이템 생성 메서드를 반복적으로 호출하여 가상 아이템을 생성하는 스크립트의 예시입니다.

이 스크립트는 JavaScript와 JavaScript 런타임인 Node.js를 사용하여 개발되었습니다.

  1. “node-fetch” 모듈의 fetch 함수를 가져와서 엑솔라 서버로 HTTP 요청을 전송합니다.
Copy
Full screen
Small screen
1import fetch from "node-fetch";
  1. 요청 인증에 필요한 상수를 설정합니다. <your project_id from PA><your api key from PA> 대신 프로젝트 ID 및 API 키 값을 입력하면 나중에 API 요청에서 사용할 수 있도록 Base64를 사용하여 인코딩됩니다.
Copy
Full screen
Small screen
1const projectId = <your project_id from PA>;
2
3const apiKey = <your api key from PA>;
4
5const buff = new Buffer(`${projectId}:${apiKey}`);
6
7const basicAuth = buff.toString('base64')
  1. API 속도 제한을 초과하지 않도록 요청을 전송할 때 지연 메커니즘을 구현합니다. 다음을 수행할 수 있습니다:
    • sleep 도우미 기능을 사용하여 요청 간의 고정된 지연을 설정할 수 있습니다.
    • delay 도우미 기능을 사용하여 지수 백오프를 구현하십시오. 이 경우, 부하를 분산하기 위해 추가 랜덤 값(지터)을 축가한 상태에서 429 Too Many Requests 오류가 발생할 때마다 반복 요청 간의 대기 시간이 증가합니다.
고정 지연 옵션:
Copy
Full screen
Small screen
1function sleep(ms) {
2
3   return new Promise(resolve => setTimeout(resolve, ms));
4
5}

지수 백오프 옵션:

Copy
Full screen
Small screen
 1/** Pause for the specified number of milliseconds */
 2function delay(ms) {
 3  return new Promise((resolve) => setTimeout(resolve, ms));
 4}
 5
 6/**
 7 * A fetch wrapper that handles HTTP 429 responses:
 8 * - pure exponential backoff with jitter (ignores Retry-After);
 9 * - limited number of retry attempts.
10 */
11async function fetchWithRateLimit(
12  url,
13  options = {},
14  {
15    maxAttempts = 4,     // 1 initial attempt + 3 retries (balanced approach)
16    baseDelayMs = 1000,  // 1 second base delay (15x the 67ms interval for 15 RPS)
17    factor = 1.5,        // gentler backoff multiplier (1s → 1.5s → 2.25s → 3.4s)
18    maxDelayMs = 4000,   // 4 seconds max delay (prevents excessive waiting)
19    jitterMs = 300       // 300ms random jitter (spreads retries without long delays)
20  } = {}
21) {
22  let attempt = 0;
23
24  while (true) {
25    attempt += 1;
26    const res = await fetch(url, options);
27
28    if (res.status !== 429) return res;
29
30    // 429: calculate pause (exponential backoff + jitter)
31    const backoff = Math.min(
32      Math.floor(baseDelayMs * Math.pow(factor, attempt - 1)),
33      maxDelayMs
34    );
35    const jitter = Math.floor(Math.random() * (jitterMs + 1)); // 0..jitterMs
36    const waitMs = backoff + jitter;
37
38    if (attempt >= maxAttempts) {
39      // retry limit reached — return the last response
40      return res;
41    }
42
43    await delay(waitMs);
44  }
45}
  1. 시스템에서 아이템 데이터를 가져오려면 시스템 고유의 getItems 함수를 구현합니다.
Copy
Full screen
Small screen
1async function getItems() {
2
3   // receive items from the original system or read from a pre-prepared file
4
5   return items;
6
7}
  1. 가상 아이템 생성 API 호출의 데이터 형식에 따라 아이템 데이터의 형식을 지정하려면 시스템에 맞는 prepareData 함수를 구현해야 합니다.
Copy
Full screen
Small screen
1function prepareData(items) {
2
3   // format items in accordance with API requirements
4
5   return formattedItems;
6
7}
  1. 가상 아이템을 생성하기 위해 엑솔라 API에 POST 요청을 전송하는 createItem 함수를 추가합니다.
Copy
Full screen
Small screen
 1async function createItem(item) {
 2
 3   const url = `https://store.xsolla.com/api/v2/project/${projectId}/admin/items/virtual_items`;
 4
 5
 6
 7   return await fetch(url, {
 8
 9       method: "POST",
10
11       headers: {
12
13           Authorization: `Basic ${basicAuth}`,
14
15           "Content-Type": "application/json"
16
17       },
18
19       body: JSON.stringify(item),
20
21   });
22
23}
  1. 지정된 SKU가 있는 가상 아이템의 존재 여부를 확인하는 checkItemExist 함수를 추가합니다. 이 함수는 엑솔라 API에 GET 요청을 보냅니다.
    • 404 HTTP 코드가 포함된 응답이 수신되면 지정된 SKU를 가진 아이템을 찾을 수 없으므로 해당 아이템을 생성해야 합니다.
    • 200 HTTP 코드가 포함된 응답이 수신되면 지정된 SKU를 가진 아이템이 있으며, 해당 아이템을 생성할 필요가 없습니다.
Copy
Full screen
Small screen
 1async function checkItemExist(sku) {
 2
 3   const url = `https://store.xsolla.com/api/v2/project/${projectId}/admin/items/virtual_items/sku/${sku}`;
 4
 5   const response = await fetch(url, {
 6
 7       method: "GET",
 8
 9       headers: {
10
11           Authorization: `Basic ${basicAuth}`
12
13       }
14
15   });
16
17   return response.status !== 404;
18
19}
  1. 아이템 목록을 살펴보고 엑솔라 측 시스템에 해당 SKU를 보유한 아이템이 있는지 확인하는 createItems 함수를 추가합니다. 해당 SKU를 보유한 아이템이 없는 경우, 이 함수가 해당 아이템을 생성합니다. 진행 정보는 콘솔에 표시됩니다.
Copy
Full screen
Small screen
 1async function createItems(items) {
 2
 3   let success = 0;
 4
 5   let alreadyCreated = 0;
 6
 7   for (let i = 0; i < items.length; i++) {
 8
 9       const item = items[i];
10
11       if (item['sku'] === undefined) {
12
13           console.log(`${i} Field "sku" not specified`);
14
15           continue;
16
17       }
18
19       const sku = item['sku'];
20
21       if (await checkItemExist(sku)) {
22
23           console.log(`${i} Item with sku "${sku}" already created`);
24
25           alreadyCreated++;
26
27           continue;
28
29       }
30
31       const response = await createItem(item);
32
33       if (response.status === 201) {
34
35           console.log(`${i} Item with sku "${sku}" successfully created`)
36
37           success++;
38
39       } else {
40
41           const jsonData = await response.json();
42
43           console.log(`${i} An error occurred while creating the items with sku "${sku}"`);
44
45           console.log(jsonData);
46
47       }
48
49       // add a delay so as not to run into rate limits
50
51       await sleep(500);
52
53   }
54
55   console.log(`${success} items out of ${items.length} created. ${alreadyCreated} items already existed`);
56
57}
  1. 위의 모든 함수를 올바른 순서로 호출하는 run 함수를 추가합니다.
Copy
Full screen
Small screen
1async function run() {
2
3 const items = await getItems();
4
5 const formattedItems = prepareData(items);
6
7 await createItems(formattedItems);
8
9}

전체 코드:

이 기사가 도움이 되었나요?
감사합니다!
개선해야 할 점이 있을까요? 메시지
유감입니다
이 기사가 도움이 안 된 이유를 설명해 주세요. 메시지
의견을 보내 주셔서 감사드립니다!
메시지를 검토한 후 사용자 경험 향상에 사용하겠습니다.
마지막 업데이트: 2026년 2월 13일

오자 또는 기타 텍스트 오류를 찾으셨나요? 텍스트를 선택하고 컨트롤+엔터를 누르세요.

문제 보고
콘텐츠를 항상 검토합니다. 여러분의 피드백은 콘텐츠를 개선에 도움이 됩니다.
후속 조치를 위해 이메일을 제공해 주세요
의견을 보내 주셔서 감사드립니다!
피드백을 보내는 중 문제가 발생했습니다
잠시 후 다시 시도하거나 doc_feedback@xsolla.com으로 연락해 주세요.