> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Ranking SDK for Game Engine API 레퍼런스

게임 엔진용 GS2-Ranking SDK의 모델 사양과 API 레퍼런스



## 모델

### EzScore

스코어<br>

게임 플레이어별·카테고리별로 등록된 스코어를 보유하는 엔티티입니다.<br>
각 스코어 엔트리는 고유 ID로 식별되며, 카테고리 및 스코어 등록 사용자와 연결됩니다.<br>
합산 모드에서는 새로운 스코어가 별도의 엔트리를 생성하는 대신 AddScore 조작을 통해 기존 합계에 가산됩니다.<br>
카테고리의 최소값·최대값 범위를 벗어나는 스코어는 등록 시 거부됩니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| categoryName | string |  | ✓ |  |  ~ 128자 | 카테고리명 |
| userId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| uniqueId | string |  | ✓ | UUID |  ~ 36자 | 고유 ID<br>이 스코어 엔트리를 고유하게 식별하는 UUID입니다.<br>생성 시 자동으로 생성됩니다. uniqueByUserId가 비활성화된 경우, 동일 사용자의 동일 카테고리 내 여러 스코어 엔트리를 구분하기 위해 사용됩니다. |
| scorerUserId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| score | long |  | ✓ |  | 0 ~ 9223372036854775805 | 스코어<br>플레이어가 등록한 스코어 값입니다.<br>카테고리에 설정된 최소값·최대값 범위 내여야 합니다. 합산 모드에서는 AddScore 조작을 통해 이 값을 증가시킬 수 있습니다. |
| metadata | string |  |  |  |  ~ 512자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |

**관련 메서드:**
putScore - 랭킹에 스코어 등록하기
getScore - 플레이어가 등록한 특정 스코어 취득하기
listScores - 특정 플레이어가 등록한 스코어 목록 취득하기


---

### EzRanking

랭킹<br>

랭킹 리더보드의 한 항목을 나타내며, 사용자의 순위, 점수 및 관련 메타데이터를 포함합니다.<br>
글로벌 랭킹(모든 플레이어가 공유 보드에서 배치 집계로 경쟁)과 스코프 랭킹(구독한 플레이어를 기반으로 한 사용자별 보드에서 실시간 반영)의 두 종류가 있습니다.<br>
랭킹은 카테고리에 설정된 정렬 방향으로 정렬되며, 동일 점수인 항목은 동일한 순위를 공유하면서도 서로 다른 인덱스를 유지합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| rank | long |  | ✓ |  | 1 ~ 9223372036854775805 | 순위<br>이 항목의 랭킹 순위(1부터 시작)입니다.<br>동일 점수인 항목은 동일한 순위 값을 공유합니다. 예를 들어 두 사용자가 1위로 동점인 경우, 둘 다 순위 1이 되고 다음 항목은 순위 3이 됩니다. |
| index | long |  | ✓ |  | 0 ~ 9223372036854775805 | 인덱스<br>랭킹 목록 내의 0부터 시작하는 순차 인덱스입니다.<br>rank 와 달리, 여러 항목이 동일 점수를 공유하더라도 인덱스는 항상 유일하며 연속됩니다. 페이지네이션이나 범위 기반 쿼리에 사용됩니다. |
| userId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| score | long |  | ✓ |  | 0 ~ 9223372036854775805 | 스코어<br>이 랭킹 엔트리의 스코어 값입니다.<br>합산 모드의 경우, 등록된 모든 스코어의 누적 합계입니다. 랭킹 정렬 순서에 사용되는 값은 카테고리의 orderDirection 설정에 따라 달라집니다. |
| metadata | string |  |  |  |  ~ 512자 | 메타데이터<br>이 랭킹 엔트리에 연결된 임의의 메타데이터입니다.<br>스코어 등록 시 설정되며, 랭킹 결과와 함께 반환됩니다. 최대 512자. |
| createdAt | long |  | ✓ |  |  | 작성일시<br>UNIX 시간·밀리초<br>※ 서버 측에서 자동으로 설정 |

**관련 메서드:**
getNearRanking - 특정 스코어 부근의 랭킹 취득하기
getRank - 특정 플레이어의 순위와 스코어 취득하기
getRanking - 랭킹 리더보드 취득하기


---

### EzSubscribeUser

구독 사용자<br>

스코프 랭킹 카테고리 내 개별 구독 관계를 나타냅니다.<br>
각 항목은 상위 사용자가 지정된 카테고리에서 대상 사용자의 점수를 구독(팔로우)하고 있음을 나타냅니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| userId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| targetUserId | string |  | ✓ |  |  ~ 128자 | 구독 대상 사용자 ID<br>구독 대상 플레이어의 사용자 ID입니다.<br>이 사용자의 점수는 구독한 사용자의 지정된 카테고리에 대한 스코프 랭킹에 표시됩니다. |

**관련 메서드:**
listSubscribes - 프렌드 랭킹용으로 팔로우 중인 플레이어 목록 취득하기
subscribe - 플레이어를 팔로우하여 프렌드 랭킹에 포함하기
unsubscribe - 플레이어의 팔로우를 해제하여 프렌드 랭킹에서 제외하기


---

### EzCategoryModel

카테고리 모델<br>

카테고리마다 서로 다른 랭킹을 생성할 수 있습니다.<br>

카테고리에는 등록 가능한 스코어의 최소값·최대값을 설정할 수 있으며, 해당 범위를 벗어나는 스코어는 폐기됩니다.<br>
랭킹을 집계할 때 스코어가 작은 것을 상위(오름차순)로 할지, 큰 것을 상위(내림차순)로 할지를 설정할 수 있습니다.<br>

랭킹의 종류로 `글로벌`과 `스코프`를 선택할 수 있습니다.<br>
글로벌은 모든 플레이어가 동일한 결과를 참조하는 것이며, 스코프는 친구 내 랭킹이나 길드 내 랭킹처럼 게임 플레이어마다 결과가 다른 랭킹입니다.<br>

글로벌 랭킹은 카테고리별로 랭킹 집계 간격을 15분~24시간으로 설정할 수 있습니다.<br>
스코프 랭킹은 실시간으로 집계 결과가 반영됩니다.<br>

랭킹 데이터에는 세대라는 설정이 있으며, 세대를 변경함으로써 등록된 스코어를 리셋할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ |  |  ~ 128자 | 카테고리 모델 이름<br>카테고리 모델 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| metadata | string |  |  |  |  ~ 1024자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| scope | 문자열 열거형<br>enum {<br>"global",<br>"scoped"<br>}<br> |  | ✓ |  |  | 랭킹 종류<br>이 카테고리의 랭킹 타입입니다.<br>"global" 은 모든 플레이어가 공유하는 단일 리더보드를 만들며, 설정된 간격으로 배치 집계됩니다.<br>"scoped" 는 구독한 플레이어(친구나 길드 멤버 등)를 기반으로 한 사용자별 리더보드를 만들며, 점수가 실시간으로 반영됩니다.global: 글로벌 / scoped: 스코프 /  |
| globalRankingSetting | [EzGlobalRankingSetting](#ezglobalrankingsetting) | {scope} == "global" | ✓※ |  |  | 글로벌 랭킹 설정<br>글로벌 랭킹 모드 전용 설정입니다. 집계 간격, 고정 시각, 점수의 유니크 여부, 세대 관리, 추가 기간 한정 스코프를 포함합니다.<br>scope 가 "global" 로 설정된 경우에만 적용됩니다.<br><br>※ scope이(가) "global" 이면 필수 |
| entryPeriodEventId | string |  |  |  |  ~ 1024자 | 점수 등록 기간 이벤트 ID<br>점수 등록을 받는 기간을 정의하는 GS2-Schedule 이벤트의 GRN입니다.<br>이 기간 외의 점수 등록 요청은 거부됩니다. 설정하지 않으면 점수는 언제든지 등록할 수 있습니다. |
| accessPeriodEventId | string |  |  |  |  ~ 1024자 | 접근 기간 이벤트 ID<br>랭킹 데이터를 열람할 수 있는 기간을 정의하는 GS2-Schedule 이벤트의 GRN입니다.<br>이 기간 외의 랭킹 조회 요청은 거부됩니다. 설정하지 않으면 랭킹은 언제든지 접근할 수 있습니다. |

**관련 메서드:**
getCategory - 특정 랭킹 카테고리의 상세 정보 취득하기
listCategories - 랭킹 카테고리 목록 취득하기


---

### EzGlobalRankingSetting

글로벌 랭킹 설정<br>

글로벌은 모든 플레이어가 동일한 결과를 참조하는 방식입니다.<br>
랭킹 집계 간격은 15분~24시간으로 설정할 수 있습니다.<br>

랭킹 데이터에는 세대라는 설정이 있으며, 세대를 변경함으로써 등록된 점수를 초기화할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| calculateIntervalMinutes | int |  | ✓ |  | 15 ~ 1440 | 집계 간격(분)<br>연속되는 랭킹 재집계 사이의 간격(분)입니다.<br>시스템은 이 간격으로 등록된 모든 점수를 기반으로 글로벌 랭킹을 정기적으로 재집계합니다.<br>범위: 15~1440분(15분~24시간). |
| additionalScopes | [List&lt;EzScope&gt;](#ezscope) |  |  |  | 0 ~ 10 items | 추가 스코프 목록<br>추가적인 기간 한정 집계 스코프 목록입니다.<br>각 스코프는 지정한 일수 이내에 등록된 점수만을 대상으로 하는 별도의 랭킹을 정의합니다.<br>전체 기간의 글로벌 랭킹과 함께 데일리·위클리·먼슬리 등의 리더보드를 만들 수 있습니다. 최대 10건. |


**관련 모델:**
EzCategoryModel - 카테고리 모델



---

### EzScope

집계 스코프<br>

글로벌 랭킹 모드에서의 추가적인 기간 한정 집계 스코프를 정의합니다.<br>
일반적으로 글로벌 랭킹은 등록된 모든 스코어를 대상으로 집계됩니다.<br>
스코프를 추가하면 지정한 일수 이내에 등록된 스코어만을 대상으로 하는 별도의 랭킹을 생성할 수 있으며, 전체 기간 랭킹과 함께 데일리·위클리·먼슬리 등의 리더보드를 구현할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ |  |  ~ 128자 | 스코프 이름<br>카테고리 내에서 이 집계 스코프를 고유하게 식별하는 이름입니다.<br>여러 개의 기간 한정 랭킹 보드를 구분하기 위해 사용됩니다(예: "daily", "weekly"). 최대 128자. |
| targetDays | long |  | ✓ |  | 1 ~ 365 | 집계 대상 일수<br>집계 윈도우에 포함할 일수입니다.<br>현재 시각으로부터 이 일수 이내에 등록된 스코어만 스코프 랭킹의 대상이 됩니다. 범위: 1~365일. |


**관련 모델:**
EzGlobalRankingSetting - 글로벌 랭킹 설정



---

## 메서드

### getCategory

특정 랭킹 카테고리의 상세 정보 취득하기<br>

랭킹 카테고리명을 지정하여, 스코어링 규칙과 설정을 포함한 상세 정보를 취득합니다.<br>

응답에는 다음이 포함됩니다:<br>
- 스코어 범위: 등록 가능한 스코어의 최솟값과 최댓값<br>
- 정렬 순서: 스코어가 높을수록 상위(내림차순)인지, 스코어가 낮을수록 상위(오름차순, 타임어택 랭킹 등에 유용)인지<br>
- 스코프 타입: 글로벌 랭킹인지 스코프 랭킹(프렌드)인지<br>
- 글로벌 랭킹의 경우: 계산 간격과 타이밍 설정<br>
- 엔트리/액세스 기간: GS2-Schedule 이벤트와 연동되어 있는 경우, 스코어 등록이나 랭킹 열람이 가능한 기간<br>

상세 화면에서 랭킹의 규칙이나 설정을 표시할 때 사용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름<br>카테고리 모델 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzCategoryModel](#ezcategorymodel) | 카테고리 모델|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        categoryName: "category-0001"
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        categoryName: "category-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->CategoryModel(
        "category-0001" // categoryName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).category_model(
        "category-0001"
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        categoryName: "category-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        categoryName: "category-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->CategoryModel(
        "category-0001" // categoryName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Ranking::Model::FCategoryModel> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).category_model(
        "category-0001"
    )

# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
    # 값이 변화했을 때 호출됨
    # value에는 변경 후의 값이 전달됩니다
    pass
)

# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### listCategories

랭킹 카테고리 목록 취득하기<br>

네임스페이스에 정의되어 있는 모든 랭킹 카테고리를 취득합니다.<br>
각 카테고리는 독립된 리더보드를 나타냅니다. 예를 들어 "하이스코어", "보스 타임어택", "주간 배틀 승리 수" 등입니다.<br>

랭킹에는 2가지 타입이 있습니다:<br>
- 글로벌 랭킹: 모든 플레이어가 하나의 리더보드에서 순위가 매겨집니다. 랭킹은 정기적으로 재계산되기(실시간이 아님) 때문에, 스코어 등록부터 랭킹 반영까지 지연이 있습니다.<br>
- 스코프 랭킹: 플레이어별로 자기 자신과 구독(팔로우)하고 있는 플레이어만 표시되는 개인화된 리더보드입니다("프렌드 랭킹"과 같은 것입니다). 이쪽은 실시간으로 계산됩니다.<br>

랭킹 화면에서 이용 가능한 리더보드 목록을 표시할 때 사용합니다. 예를 들어 "세계 랭킹", "프렌드 랭킹", "주간 랭킹" 등의 탭을 표시하는 화면입니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzCategoryModel&gt;](#ezcategorymodel) | 카테고리 모델 목록|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    );
    var items = await domain.CategoryModelsAsync(
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    );
    var it = domain.CategoryModels(
    );
    List<EzCategoryModel> items = new List<EzCategoryModel>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    );
    const auto It = Domain->CategoryModels(
    );
    TArray<Gs2::UE5::Ranking::Model::FEzCategoryModelPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeCategoryModels(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeCategoryModels(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeCategoryModels(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeCategoryModels(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeCategoryModels(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeCategoryModels(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### listSubscribes

프렌드 랭킹용으로 팔로우 중인 플레이어 목록 취득하기<br>

지정된 랭킹 카테고리에서 현재 플레이어가 구독(팔로우)하고 있는 플레이어의 목록을 취득합니다.<br>
스코프 랭킹(프렌드 랭킹)에서 사용합니다. 다른 플레이어를 구독하면 해당 플레이어의 스코어가 개인화된 프렌드 랭킹에 표시되게 됩니다.<br>

랭킹 화면에서 "팔로우 중" 목록을 표시할 때 사용합니다. 프렌드 리더보드에 어떤 플레이어의 스코어가 포함되어 있는지 확인할 수 있습니다.<br>

예를 들어 PlayerA, PlayerB, PlayerC를 구독하고 있는 경우, 프렌드 랭킹에는 이 3명과 자기 자신의 스코어가 표시됩니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름 |
| gameSession | GameSession | | ✓|  |  | GameSession |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzSubscribeUser&gt;](#ezsubscribeuser) | 구독 대상 사용자 정보 목록|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var items = await domain.SubscribeUsersAsync(
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var it = domain.SubscribeUsers(
    );
    List<EzSubscribeUser> items = new List<EzSubscribeUser>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    const auto It = Domain->SubscribeUsers(
    );
    TArray<Gs2::UE5::Ranking::Model::FEzSubscribeUserPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeSubscribeUsers(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeSubscribeUsers(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeSubscribeUsers(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeSubscribeUsers(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeSubscribeUsers(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeSubscribeUsers(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### subscribe

플레이어를 팔로우하여 프렌드 랭킹에 포함하기<br>

지정된 카테고리에서 다른 플레이어를 구독하여, 해당 플레이어의 스코어가 자신의 스코프 랭킹(프렌드 랭킹)에 표시되도록 합니다.<br>
"팔로우" 기능과 같은 것입니다. 구독 후 대상 플레이어의 스코어가 개인화된 프렌드 리더보드에 표시되게 됩니다.<br>

주요 사용 방법:<br>
- 플레이어가 친구를 추가했을 때, 모든 랭킹 카테고리에서 자동으로 구독하기<br>
- 플레이어 프로필 화면에 "리더보드에서 팔로우" 버튼을 배치하기<br>
- 대전 후, 상대했던 플레이어를 팔로우할 수 있게 하기<br>

자기 자신을 구독할 수는 없습니다(자신의 스코어는 프렌드 랭킹에 자동으로 포함됩니다).

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리명 |
| gameSession | GameSession | | ✓|  |  | GameSession |
| targetUserId | string |  | ✓|  |  ~ 128자 | 구독할 사용자 ID |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzSubscribeUser](#ezsubscribeuser) | 구독 대상 사용자 정보|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var result = await domain.SubscribeAsync(
        targetUserId: "user-0002"
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var future = domain.SubscribeFuture(
        targetUserId: "user-0002"
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    const auto Future = Domain->Subscribe(
        "user-0002" // targetUserId
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).ranking_category(
        "category-0001",
        null
    )

var async_result = await domain.subscribe(
    "user-0002" # target_user_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### unsubscribe

플레이어의 팔로우를 해제하여 프렌드 랭킹에서 제외하기<br>

지정된 랭킹 카테고리에서 지정된 플레이어의 구독을 해제합니다.<br>
구독 해제 후, 대상 플레이어의 스코어는 스코프 랭킹(프렌드 랭킹)에 표시되지 않게 됩니다.<br>

플레이어가 프렌드 리더보드에서 누군가의 스코어를 표시하지 않도록 하고 싶을 때 사용합니다. 예를 들어 친구 해제 시나 팔로우 해제 시입니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리명 |
| gameSession | GameSession | | ✓|  |  | GameSession |
| targetUserId | string |  | ✓|  |  ~ 128자 | 구독 해제할 사용자 ID |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzSubscribeUser](#ezsubscribeuser) | 해제한 구독 대상 사용자 정보|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    ).SubscribeUser(
        targetUserId: "user-0002"
    );
    var result = await domain.UnsubscribeAsync(
    );

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    ).SubscribeUser(
        targetUserId: "user-0002"
    );
    var future = domain.UnsubscribeFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    )->SubscribeUser(
        "user-0002" // targetUserId
    );
    const auto Future = Domain->Unsubscribe(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
    const auto Result = Future->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).ranking_category(
        "category-0001",
        null
    ).subscribe_user(
        "user-0002"
    )

var async_result = await domain.unsubscribe(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### getNearRanking

특정 스코어 부근의 랭킹 취득하기<br>

지정된 스코어 값을 중심으로 한 랭킹의 일부를 취득합니다.<br>
플레이어가 "자신이 어느 정도의 순위인지"를 확인하는 데 편리합니다. 예를 들어 리더보드 상에서 플레이어의 스코어 전후 몇 명을 표시할 수 있습니다.<br>

일반적인 사용 방법: 플레이어가 게임을 마친 후, 자신의 스코어와 그 부근의 랭킹을 표시:<br>
"98위: PlayerX (5,200 pt) → 당신: 5,150 pt → 99위: PlayerY (5,100 pt)"<br>

이 API는 글로벌 랭킹에서만 사용할 수 있습니다. 스코프 랭킹(프렌드)의 경우는 목록이 충분히 작으므로, 대신 GetRanking을 사용해 주세요.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름<br>카테고리 모델 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| additionalScopeName | string |  | |  |  ~ 128자 | 스코프 이름<br>카테고리 내에서 이 집계 스코프를 고유하게 식별하는 이름입니다.<br>여러 개의 기간 한정 랭킹 보드를 구분하기 위해 사용됩니다(예: "daily", "weekly"). 최대 128자. |
| score | long |  | ✓|  | 0 ~ 9223372036854775805 | 스코어<br>이 랭킹 엔트리의 스코어 값입니다.<br>합산 모드의 경우, 등록된 모든 스코어의 누적 합계입니다. 랭킹 정렬 순서에 사용되는 값은 카테고리의 orderDirection 설정에 따라 달라집니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzRanking&gt;](#ezranking) | 랭킹 목록|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: "user-0001"
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var items = await domain.NearRankingsAsync(
        score: 1000L
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: "user-0001"
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var it = domain.NearRankings(
        score: 1000L
    );
    List<EzRanking> items = new List<EzRanking>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->User(
        "user-0001" // userId
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    const auto It = Domain->NearRankings(
        1000L // score
    );
    TArray<Gs2::UE5::Ranking::Model::FEzRankingPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


---

### getRank

특정 플레이어의 순위와 스코어 취득하기<br>

특정 플레이어의 랭킹 정보를 순위와 스코어를 포함하여 취득합니다.<br>
결과 화면이나 프로필 페이지에서 "당신의 순위: 42위 (8,500 pt)"와 같이 표시할 때 사용합니다.<br>

scorerUserId(순위를 조회하고 싶은 플레이어)를 지정해야 합니다. uniqueId는 카테고리가 플레이어별로 스코어를 하나만 허용하는 경우는 생략할 수 있습니다(기본값 "0"). 카테고리가 플레이어별로 여러 스코어를 허용하는 경우는, uniqueId를 지정하여 어떤 스코어를 조회할지 특정합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름<br>카테고리 모델 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| additionalScopeName | string |  | |  |  ~ 128자 | 스코프 이름<br>카테고리 내에서 이 집계 스코프를 고유하게 식별하는 이름입니다.<br>여러 개의 기간 한정 랭킹 보드를 구분하기 위해 사용됩니다(예: "daily", "weekly"). 최대 128자. |
| scorerUserId | string |  | ✓|  |  ~ 128자 | 스코어를 획득한 사용자의 사용자 ID |
| gameSession | GameSession | | ✓|  |  | GameSession |
| uniqueId | string |  | | "0" |  ~ 36자 | 스코어의 고유 ID |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzRanking](#ezranking) | 랭킹|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    ).Ranking(
        scorerUserId: "user-0001",
        index: null
    );
    var item = await domain.ModelAsync(
        scorerUserId : "user-0001"
    );

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    ).Ranking(
        scorerUserId: "user-0001",
        index: null
    );
    var future = domain.Model(
        scorerUserId : "user-0001"
    );
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    )->Ranking(
        "user-0001", // scorerUserId
        nullptr // index
    );
    const auto Future = Domain->Model(
        "user-0001" // scorerUserId
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).ranking_category(
        "category-0001",
        null
    ).ranking(
        "user-0001",
        null
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    ).Ranking(
        scorerUserId: "user-0001",
        index: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    ).Ranking(
        scorerUserId: "user-0001",
        index: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    )->Ranking(
        "user-0001", // scorerUserId
        nullptr // index
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Ranking::Model::FRanking> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).ranking_category(
        "category-0001",
        null
    ).ranking(
        "user-0001",
        null
    )

# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
    # 값이 변화했을 때 호출됨
    # value에는 변경 후의 값이 전달됩니다
    pass
)

# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### getRanking

랭킹 리더보드 취득하기<br>

지정된 카테고리의 랭킹 목록을 취득합니다. 플레이어가 스코어 순으로 나열되어 표시됩니다.<br>
리더보드 화면을 표시하기 위한 메인 API입니다. 예를 들어 "1위: PlayerA (10,500 pt), 2위: PlayerB (9,800 pt), ..."와 같은 표시입니다.<br>

startIndex를 지정하여 특정 순위부터 취득을 시작할 수 있습니다(예: startIndex=0으로 상위부터, startIndex=99로 100위 부근부터).<br>

글로벌 랭킹의 경우, 데이터는 마지막 계산 결과에 기반하므로 스코어 등록부터 랭킹 갱신까지 지연이 있을 수 있습니다.<br>
스코프 랭킹(프렌드)의 경우, 데이터는 실시간으로 계산됩니다.<br>

카테고리가 추가 스코프를 지원하는 경우, additionalScopeName을 지정하여 랭킹을 더욱 좁힐 수 있습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름<br>카테고리 모델 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| additionalScopeName | string |  | |  |  ~ 128자 | 스코프 이름<br>카테고리 내에서 이 집계 스코프를 고유하게 식별하는 이름입니다.<br>여러 개의 기간 한정 랭킹 보드를 구분하기 위해 사용됩니다(예: "daily", "weekly"). 최대 128자. |
| gameSession | GameSession | | |  |  | GameSession |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |
| pageToken | string |  | |  |  ~ 4096자 | 데이터 취득을 시작할 위치를 지정하는 토큰 |
| startIndex | long |  | |  | 0 ~ 9223372036854775805 | 랭킹 취득을 시작할 인덱스 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzRanking&gt;](#ezranking) | 랭킹 목록|
| nextPageToken | string | 목록의 나머지를 취득하기 위한 페이지 토큰|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var items = await domain.RankingsAsync(
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var it = domain.Rankings(
    );
    List<EzRanking> items = new List<EzRanking>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    const auto It = Domain->Rankings(
    );
    TArray<Gs2::UE5::Ranking::Model::FEzRankingPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeRankings(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeRankings(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeRankings(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeRankings(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeRankings(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeRankings(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### putScore

랭킹에 스코어 등록하기<br>

지정된 랭킹 카테고리에 플레이어의 스코어를 등록합니다.<br>
플레이어가 기록하고 싶은 스코어를 달성했을 때 호출합니다. 예를 들어 스테이지 클리어 후, 타임어택 종료 시, 대전 종료 시 등입니다.<br>

스코어에는 메타데이터(문자열)를 옵션으로 첨부할 수 있습니다. 리더보드 상에서 스코어와 함께 표시하고 싶은 추가 정보를 저장하는 데 유용합니다. 예를 들어 플레이어의 캐릭터 이름, 팀 구성, 리플레이 데이터 등입니다.<br>

스코어의 처리 방식은 카테고리의 타입에 따라 다릅니다:<br>
- 글로벌 랭킹: 스코어가 수집되어, 다음 스케줄된 간격에서 랭킹이 재계산됩니다. 랭킹이 갱신될 때까지 지연이 있습니다.<br>
- 스코프 랭킹: 스코어가 즉시 반영되어, 이 플레이어를 구독(팔로우)하고 있는 플레이어의 프렌드 랭킹에 표시됩니다.<br>

주의: 카테고리 설정에 따라, 플레이어별로 최고 스코어(오름차순 랭킹의 경우는 최저 스코어)만 유지되는 경우와 여러 스코어가 허용되는 경우가 있습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름<br>카테고리 모델 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| score | long |  | ✓|  | 0 ~ 9223372036854775805 | 스코어<br>이 랭킹 엔트리의 스코어 값입니다.<br>합산 모드의 경우, 등록된 모든 스코어의 누적 합계입니다. 랭킹 정렬 순서에 사용되는 값은 카테고리의 orderDirection 설정에 따라 달라집니다. |
| metadata | string |  | |  |  ~ 512자 | 메타데이터<br>이 랭킹 엔트리에 연결된 임의의 메타데이터입니다.<br>스코어 등록 시 설정되며, 랭킹 결과와 함께 반환됩니다. 최대 512자. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzScore](#ezscore) | 등록한 스코어|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var result = await domain.PutScoreAsync(
        score: 1000L,
        metadata: null
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RankingCategory(
        categoryName: "category-0001",
        additionalScopeName: null
    );
    var future = domain.PutScoreFuture(
        score: 1000L,
        metadata: null
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RankingCategory(
        "category-0001", // categoryName
        nullptr // additionalScopeName
    );
    const auto Future = Domain->PutScore(
        1000L // score
        // metadata
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).ranking_category(
        "category-0001",
        null
    )

var async_result = await domain.put_score(
    1000, # score
    null # metadata
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### getScore

플레이어가 등록한 특정 스코어 취득하기<br>

지정된 랭킹 카테고리에서 지정된 플레이어의 스코어 레코드 하나를 취득합니다.<br>
특정 스코어의 상세 정보를 표시할 때 사용합니다. 예를 들어 스코어 값, 등록 일시, 첨부된 메타데이터(캐릭터 이름이나 리플레이 데이터 등)를 표시할 수 있습니다.<br>

uniqueId는 플레이어가 여러 스코어를 가진 경우 어떤 스코어를 취득할지 지정합니다. 카테고리가 플레이어별로 스코어를 하나만 허용하는 경우(가장 일반적인 케이스)에는 uniqueId를 생략하거나 "0"으로 설정할 수 있습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리명 |
| gameSession | GameSession | | ✓|  |  | GameSession |
| scorerUserId | string |  | ✓|  |  ~ 128자 | 사용자ID |
| uniqueId | string |  | | "0" |  ~ 36자 | 스코어의 고유 ID |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzScore](#ezscore) | 스코어|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Score(
        categoryName: "category-0001",
        scorerUserId: "user-0002",
        uniqueId: "unique-0001"
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Score(
        categoryName: "category-0001",
        scorerUserId: "user-0002",
        uniqueId: "unique-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Score(
        "category-0001", // categoryName
        "user-0002", // scorerUserId
        "unique-0001" // uniqueId
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).score(
        "category-0001",
        "user-0002",
        "unique-0001"
    )

var async_result = await domain.model()
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Score(
        categoryName: "category-0001",
        scorerUserId: "user-0002",
        uniqueId: "unique-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Score(
        categoryName: "category-0001",
        scorerUserId: "user-0002",
        uniqueId: "unique-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Score(
        "category-0001", // categoryName
        "user-0002", // scorerUserId
        "unique-0001" // uniqueId
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Ranking::Model::FScore> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->Unsubscribe(CallbackId);

```

**Godot**
```gdscript

var domain = ez.ranking.namespace_(
        "namespace-0001"
    ).me(game_session).score(
        "category-0001",
        "user-0002",
        "unique-0001"
    )

# 이벤트 핸들링 시작
var callback_id = domain.subscribe_model(func(value):
    # 값이 변화했을 때 호출됨
    # value에는 변경 후의 값이 전달됩니다
    pass
)

# 이벤트 핸들링 정지
domain.unsubscribe_model(callback_id)

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---

### listScores

특정 플레이어가 등록한 스코어 목록 취득하기<br>

지정된 랭킹 카테고리에서 특정 플레이어가 등록한 모든 스코어를 취득합니다.<br>
플레이어의 스코어 이력을 표시할 때 사용합니다. 예를 들어 타임어택 스테이지의 전체 도전 기록을 목록으로 표시하는 경우입니다.<br>

랭킹 표시(모든 플레이어를 스코어 순으로 표시)와는 달리, 이 API는 한 명의 특정 플레이어의 등록 스코어에 초점을 맞추고 있습니다.<br>

카테고리가 플레이어별로 스코어를 하나만 허용하는 경우는 1건만 반환됩니다. 여러 스코어가 허용되는 경우(예: 여러 번의 도전)에는 모든 스코어가 반환됩니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| categoryName | string |  | ✓|  |  ~ 128자 | 카테고리 모델 이름 |
| scorerUserId | string |  | ✓|  |  ~ 128자 | 사용자ID |
| gameSession | GameSession | | ✓|  |  | GameSession |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |
| pageToken | string |  | |  |  ~ 1024자 | 데이터 취득을 시작할 위치를 지정하는 토큰 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzScore&gt;](#ezscore) | 스코어 목록|
| nextPageToken | string | 목록의 나머지를 취득하기 위한 페이지 토큰|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.ScoresAsync(
        categoryName: "category-0001",
        scorerUserId: "user-0002"
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var it = domain.Scores(
        categoryName: "category-0001",
        scorerUserId: "user-0002"
    );
    List<EzScore> items = new List<EzScore>();
    while (it.HasNext())
    {
        yield return it.Next();
        if (it.Error != null)
        {
            onError.Invoke(it.Error, null);
            break;
        }
        if (it.Current != null)
        {
            items.Add(it.Current);
        }
        else
        {
            break;
        }
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto It = Domain->Scores(
        "category-0001", // categoryName
        "user-0002" // scorerUserId
    );
    TArray<Gs2::UE5::Ranking::Model::FEzScorePtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


##### 값 변경 이벤트 핸들링




**Unity (UniTask)**
```csharp
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeScores(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeScores(callbackId);

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Ranking.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeScores(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeScores(callbackId);

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Ranking->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->SubscribeScores(
        []() {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    Domain->UnsubscribeScores(CallbackId);

```


**⚠️ Warning**

이 이벤트는 SDK가 가진 로컬 캐시의 값이 변경되었을 때 호출됩니다.

로컬 캐시는 SDK가 가진 API의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-Distributor를 통한 스탬프 시트의 실행, 또는 GS2-Gateway의 알림을 활성화한 GS2-JobQueue의 실행에 의해 변화한 것만이 대상이 됩니다.

따라서 이 방법 이외로 값이 변경된 경우 콜백은 호출되지 않습니다.

---



