엔진 API(DataStoreService)를 통해 스튜디오 또는 라이브 게임에서 데이터 저장소에 접근하는 것 외에도, 오픈 클라우드 API를 사용하여 외부 스크립트 및 기타 도구에서 표준 및 순서가 지정된 데이터 저장소에 접근할 수 있습니다.
오픈 클라우드를 통한 데이터 저장소 접근은 여러 가지 잠재적인 사용 사례를 열어줍니다. 예를 들어:
- 사용자 인벤토리를 수정하거나 환불을 발행하는 등의 지원 요청을 직접 처리할 수 있는 고객 지원 포털
- 외부 웹사이트에 표시할 수 있는 글로벌 리더보드
- 현재 데이터 저장소에서 항목을 읽고, 이를 새로운 스키마에 매핑한 후, 새로운 데이터 저장소에 항목을 다시 쓰는 스크립트를 사용한 스키마 업데이트
이 페이지의 예제는 Node.js 및 Python을 사용하여 사용자 인벤토리 지원 포털과 외부 리더보드를 만드는 방법을 보여주지만, 원하는 언어를 사용하셔도 됩니다. 오픈 클라우드 API는 HTTP 요청을 보낼 수 있는 모든 프로그래밍 언어를 지원합니다.
엔진 API와의 차이점
오픈 클라우드 API는 동일한 기본 데이터 저장소에 접근하며 DataStoreService와 유사하게 작동하지만, 몇 가지 주요 차이점이 있습니다:
우주 ID: 엔진 API와 달리, 오픈 클라우드 API는 상태 비저장(stateless)이며 어디서나 호출할 수 있으므로 항상 우주 ID(당신의 게임의 고유 식별자)를 제공해야 합니다.
생성 및 업데이트에 대한 별도의 권한: 엔진 API는 DataStore:SetAsync()를 호출할 때 존재하지 않는 경우 새 항목을 생성하지만, 오픈 클라우드의 항목 생성 및 업데이트 메서드는 별도로 존재합니다. 별도의 권한은 특정 상황에서 더 안전하고 유연할 수 있습니다. 예를 들어, 고객 지원 도구가 새로운 사용자를 생성하는 것이 아니라 기존 사용자 프로필만 수정할 수 있도록 하고 싶을 수 있습니다.
데이터 직렬화: 모든 오픈 클라우드 엔드포인트는 데이터를 전송하기 전에 직렬화해야 합니다. 직렬화는 객체를 문자열로 변환하는 것을 의미합니다. 역직렬화는 그 반대입니다; 문자열을 객체로 변환합니다. 엔진 API는 항목 내용을 자동으로 직렬화하고 역직렬화하지만, 오픈 클라우드에서는 직접 JSON으로 항목을 생성하거나 구문 분석해야 합니다.
권한
데이터 저장소는 종종 사용자 프로필 및 가상 화폐와 같은 민감한 정보를 저장합니다. 보안을 유지하기 위해, 각 오픈 클라우드 메서드는 API 키에 추가해야 하는 범위(scopes)를 가집니다, 예를 들어 데이터 저장소 목록 메서드에 대해 universe-datastores.control:list 범위를 추가해야 합니다. 필요한 권한을 추가하지 않으면 API 호출이 오류를 반환합니다. 각 엔드포인트에 필요한 범위에 대한 참조 문서를 참조하세요.
권한 관리에 대한 자세한 내용은 API 키 관리를 참조하세요.
사용자 인벤토리 지원 포털
이 예제는 Inventory라는 데이터 저장소와 각 항목의 스키마 "userId": {"currency": number, "weapon": string, "level": number}를 사용합니다. 키는 userId입니다.
필요한 범위
이 예제를 위한 API 키 생성 시 다음 범위를 추가하세요:
- universe-datastores.objects:list
- universe-datastores.objects:read
- universe-datastores.objects:update
선택적으로 Inventory 데이터 저장소에 대한 권한만 추가하고, IP 주소 제한을 설정하며, 만료 날짜를 설정할 수 있습니다.
사용자 인벤토리 지원 포털을 위한 스크립트 추가
예제 앱에 필요한 권한으로 API 키를 생성한 후, 엔드포인트에 요청을 보내는 스크립트를 생성할 수 있습니다. 이 스크립트는 데이터 저장소에서 처음 10개의 항목을 가져오고, 각 항목의 currency 값을 10만큼 증가시킨 후, 각 항목을 업데이트합니다. 더 큰 데이터 저장소의 경우 페이징 처리를 maxPageSize 및 pageToken 쿼리 매개변수를 사용하여 처리해야 합니다.
incrementCurrency.js
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('API_KEY 환경 변수가 설정되어 있지 않습니다.');
}
const apiHeaderKey = 'x-api-key';
const universeId = '';
const dataStoreId = 'Inventory';
const baseUrl = 'https://apis.roblox.com/cloud/v2/';
async function listEntries(universe, dataStore) {
const listPath = `universes/${universe}/data-stores/${dataStore}/entries`;
const url = baseUrl + listPath;
const response = await fetch(url, {
headers: { [apiHeaderKey]: apiKey }
});
return response.json();
}
async function getEntry(path) {
const url = baseUrl + path;
const response = await fetch(url, {
headers: { [apiHeaderKey]: apiKey }
});
return response.json();
}
async function updateEntry(path, payload) {
const url = baseUrl + path;
const response = await fetch(url, {
method: 'PATCH',
headers: {
[apiHeaderKey]: apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload) // 본문은 문자열이어야 합니다
});
return response;
}
(async () => {
try {
const entries = await listEntries(universeId, dataStoreId);
for (const entry of entries.dataStoreEntries) {
const path = entry.path;
console.log(`\n처리 중인 항목: ${path}`);
const currentData = await getEntry(path);
currentData.value.currency += 10;
const payload = { value: currentData.value };
const updateResponse = await updateEntry(path, payload);
console.log(`상태: ${updateResponse.status}`);
console.log(`응답: ${await updateResponse.text()}`);
}
} catch (error) {
console.error('실행 중 오류가 발생했습니다:', error);
}
})();
테스트하려면, API_KEY 환경 변수를 설정하고, 종속성을 설치한 후 스크립트를 실행하세요:
export API_KEY=<your_key>node incrementCurrency.js
외부 지속적 리더보드
이 예제는 데모 작업을 위해 미리 정의된 사용자 목록을 생성하지만, 실제 게임에서 유용하게 사용되려면 실제 사용자 데이터 저장소가 필요합니다.
필요한 범위
이 예제를 위한 API 키 생성 시 다음 범위를 추가하세요:
- universe.ordered-data-store.scope.entry:read
- universe.ordered-data-store.scope.entry:write
리더보드를 위한 스크립트 추가
예제 앱에 필요한 권한으로 API 키를 생성한 후, 엔드포인트에 요청을 보내는 스크립트를 생성할 수 있습니다. 이 스크립트는 임의의 숫자가 포함된 일부 샘플 항목을 순서가 지정된 데이터 저장소에 추가한 후, 가장 높은 값에서 낮은 값으로 항목을 검색합니다. 더 큰 데이터 저장소의 경우 페이징 처리를 maxPageSize 및 pageToken 쿼리 매개변수를 사용하여 해야 합니다.
leaderboard.js
const apiKey = process.env.API_KEY;
const apiHeaderKey = 'x-api-key';
const universeId = '';
const orderedDataStoreId = 'PlayerScores';
const scopeId = 'global';
const baseUrl = 'https://apis.roblox.com/cloud/v2/';
async function createOrderedEntry(universe, orderedDataStore, entryId, payload) {
const createPath = `universes/${universe}/ordered-data-stores/${orderedDataStore}/scopes/${scopeId}/entries`;
const url = new URL(baseUrl + createPath);
url.searchParams.append('id', entryId);
const response = await fetch(url, {
method: 'POST',
headers: {
[apiHeaderKey]: apiKey,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`API 오류 (${response.status}): ${errorText}`);
}
return response.text();
}
async function listOrderedEntries(universe, orderedDataStore) {
const listPath = `universes/${universe}/ordered-data-stores/${orderedDataStore}/scopes/${scopeId}/entries`;
const url = new URL(baseUrl + listPath);
url.searchParams.append('orderBy', 'value desc');
return fetch(url, {
headers: {
[apiHeaderKey]: apiKey,
},
});
}
async function main() {
if (!apiKey) {
console.error('오류: API_KEY 환경 변수가 설정되어 있지 않습니다.');
process.exit(1);
}
const entryNames = ['Ragdoll', 'Balinese', 'Tabby', 'Siamese'];
console.log('샘플 데이터 생성 중...');
for (const name of entryNames) {
try {
const randomValue = Math.floor(Math.random() * 50) + 1;
const payload = { value: randomValue };
const responseText = await createOrderedEntry(universeId, orderedDataStoreId, name, payload);
console.log(responseText);
} catch (error) {
console.error(`"${name}"의 항목 생성에 실패했습니다: ${error.message}`);
}
}
console.log('\n정렬된 항목 리스트 가져오는 중...');
try {
const playerScoresResponse = await listOrderedEntries(universeId, orderedDataStoreId);
console.log(playerScoresResponse.status);
const responseText = await playerScoresResponse.text();
console.log(responseText);
} catch (error) {
console.error(`항목 목록 가져오기에 실패했습니다: ${error.message}`);
}
}
main();
테스트하려면, API_KEY 환경 변수를 설정하고, 종속성을 설치한 후 스크립트를 실행하세요:
export API_KEY=<your_key>node leaderboard.js