오류 코드
모든 오류에는 고정 코드가 있습니다. SDK도 같은 코드를 사용하며(Unity는 enum), 요청이 서버에 도달하기 전에 발생하는 문제를 위한 코드가 몇 가지 더 있습니다.
코드 주석과 API 이름은 SDK와 일치하도록 영어로 유지합니다.
API 오류#
| 코드 | HTTP | Unity | 발생 상황 | 재시도 |
|---|---|---|---|---|
validation | 400, 422 | Validation | 입력값이 잘못되었습니다. details.fields에 해당 필드가 표시됩니다. | 아니요 |
unauthorized | 401 | Unauthorized | 키가 잘못되었거나, 플레이어 토큰이 없거나 만료 또는 취소되었습니다. | 아니요 |
forbidden | 403 | Forbidden | 프로젝트가 일시 중지되었거나, 서버 전용 엔드포인트이거나, HTTPS가 필요합니다. | 아니요 |
plan_limit | 403 | PlanLimit | 플랜 한도(플레이어, 항목, 저장 공간 등)에 도달했습니다. 데이터는 삭제되지 않습니다. | 아니요 |
not_found | 404 | NotFound | 알 수 없는 엔드포인트, 리더보드, 플레이어 또는 저장 키입니다. | 아니요 |
conflict | 409 | Conflict | 이미 처리되었습니다. 예: 이번 기간에 이미 수령한 보상. | 아니요 |
revision_mismatch | 409 | RevisionMismatch | 다른 기기에서 클라우드 저장 데이터가 변경되었습니다. | 아니요 |
rate_limited | 429 | RateLimited | 분당 요청이 너무 많거나 리더보드 쿨다운 중입니다. Retry-After초만큼 기다리세요. | 예 |
quota_exceeded | 429 | QuotaExceeded | 계정의 일일 요청 할당량을 모두 사용했습니다. 00:00 UTC에 초기화됩니다. | 아니요 |
server_error | 500 | ServerError | 서버에서 오류가 발생했습니다. 안전한 요청은 SDK가 재시도합니다. | 예 |
SDK 전용 오류#
| 코드 | HTTP | Unity | 발생 상황 | 재시도 |
|---|---|---|---|---|
network | — | Network | 서버에 연결할 수 없습니다. | 예 |
timeout | — | Timeout | 요청이 제한 시간을 초과했습니다. | 예 |
not_configured | — | NotConfigured | Base URL, 프로젝트 ID 또는 클라이언트 키가 없거나 잘못되었습니다. | 아니요 |
not_signed_in | — | NotSignedIn | 로그인하기 전이나 세션이 만료된 후에 플레이어 호출을 했습니다. | 아니요 |
signature_invalid | — | SignatureInvalid | 원격 구성이 검증에 실패하여 사용되지 않았습니다. | 아니요 |
parse_error | — | ParseError | 응답을 읽을 수 없습니다(보통 프록시나 HTML 오류 페이지가 원인입니다). | 아니요 |
link_expired | — | LinkExpired | 계정 연결 페이지가 만료되었습니다. 다시 시작하세요. | 아니요 |
link_cancelled | — | LinkCancelled | 계정 연결 플로우가 취소되었습니다. | 아니요 |
오류 처리#
message는 플레이어가 아닌 개발자에게 보여 주고, 분기 처리는 코드로 하세요. 재시도 가능한 오류만 재시도하고, 로그 페이지에서 요청을 찾을 수 있도록 요청 ID를 기록하세요.
var top = await Majacloud.Leaderboards.GetTopAsync("coins", 10);
if (top.Ok)
{
foreach (var entry in top.Value.Entries)
Debug.Log($"#{entry.Rank} {entry.PlayerName} {entry.Score}");
}
else if (top.Error.IsRetryable)
{
// Network, timeout, 5xx or rate limit: try again later
Debug.Log("Retry in " + top.Error.RetryAfterSeconds + " s");
}
else
{
Debug.LogWarning(top.Error.Code + ": " + top.Error.Message + " (" + top.Error.RequestId + ")");
}var top := await Majacloud.leaderboards.get_top("coins", 10)
if top.ok:
for entry in top.value.entries:
print("#%d %s %d" % [entry.rank, entry.player_name, entry.score])
elif top.is_retryable():
# Network, timeout, 5xx or rate limit: try again later
print("Retry in ", top.retry_after, " s")
else:
push_warning("%s: %s (%s)" % [top.error, top.message, top.request_id])const top = await mc.leaderboards.getTop('coins', { limit: 10 });
if (top.ok) {
for (const entry of top.value.entries) {
console.log(`#${entry.rank} ${entry.player_name} ${entry.score}`);
}
} else if (top.error.retryable) {
// Network, timeout, 5xx or rate limit: try again later
console.log('Retry in', top.error.retryAfter, 's');
} else {
console.warn(`${top.error.code}: ${top.error.message} (${top.error.requestId})`);
}// Success
{ "ok": true, "entries": [ … ], "total": 42 }
// Error (HTTP 4xx/5xx)
{
"ok": false,
"error": {
"code": "rate_limited",
"message": "Project rate limit reached. Try again in a minute.",
"request_id": "6f1c2a9e-…",
"details": { "limit": 120, "window": "minute" }
}
}