# 사용 사례 샘플: 시즌 종료 시 상위 플레이어에게 게임 내 재화로 보상 제공

> Reward your top players with in-game currency at the end of a season using Triggers.

이 사용 사례 샘플은 시즌 종료 시 상위 플레이어에게 게임 내 재화로 보상을 제공하는 방법을 보여 줍니다. 샘플에서는 [Leaderboards 서비스](/leaderboards.md)에서 발생하는 `reset` 이벤트를 사용하고, [Economy](/economy.md)에서 게임 내 재화로 상위 플레이어에게 보상을 제공합니다.

## 필수 조건##prerequisites

먼저 필수 액세스 역할로 서비스 계정을 생성하고 [UGS CLI](https://services.docs.unity.com/guides/ugs-cli/latest/general/get-started/install-the-cli/)를 구성해야 합니다.

### 서비스 계정을 사용하여 인증##authenticate-using-a-service-account

Triggers 서비스를 호출하려면 먼저 서비스 계정을 사용하여 인증해야 합니다.

1. [Unity Dashboard](https://cloud.unity.com)로 이동합니다.
2. **Administration** > **Service Accounts**를 선택합니다.
3. **New** 버튼을 선택하고 서비스 계정의 이름과 설명을 입력합니다.
4. **Create**를 선택합니다.

다음과 같이 제품 역할을 추가하고 키를 만듭니다.

1. **Manage product roles**를 선택합니다.
2. 다음 역할을 서비스 계정에 추가합니다.
   * LiveOps 드롭다운에서 다음 역할을 선택합니다.
     * **Triggers 구성 편집자**
     * **Triggers 구성 조회자**
     * **Economy 리소스 편집자**
     * **Economy 리소스 조회자**
     * **Leaderboards 관리자**
     * **Leaderboards 조회자**
     * Cloud Code 모듈을 배포 중인 경우 다음 역할을 추가합니다.
       * **Cloud Code 편집자**
       * **Cloud Code 조회자**
     * Cloud Code 스크립트를 배포 중인 경우 다음 역할을 추가합니다.
       * **Cloud Code 편집자**
       * **Cloud Code 조회자**
       * **Cloud Code 퍼블리셔**
   * Admin 드롭다운에서 **Unity Environments Viewer**를 선택합니다.
3. **Save**를 선택합니다.
4. **Add Key**를 선택합니다.
5. base64 인코딩을 사용하여 **Key ID**와 **Secret key**를 인코딩합니다. 포맷은 ‘key\_id:secret\_key’입니다. 이 값을 기록해 둡니다.

자세한 내용은 [인증](../../authentication)을 참고하십시오.

### UGS CLI 구성##configure-the-ugs-cli

다음 단계를 따라 UGS CLI를 시작합니다.

1. [UGS CLI를 설치합니다](https://services.docs.unity.com/guides/ugs-cli/latest/general/get-started/install-the-cli/).

2. 다음을 사용하여 프로젝트 ID와 환경을 구성합니다.
   `ugs config set project-id <your-project-id>`
   `ugs config set environment-name <your-environment-name>`

3. 앞서 인증된 서비스 계정을 사용하여 인증을 받습니다. [인증 받기](https://services.docs.unity.com/guides/ugs-cli/latest/general/get-started/get-authenticated/)를 참고하십시오.

## `reset` 이벤트 확인##examine-the-reset-event

리더보드가 초기화되면 Leaderboards 서비스에서 `reset` 이벤트가 발생합니다. 이벤트 페이로드의 예시는 다음과 같습니다.

```json
{
  "leaderboardId": "string",
  "leaderboardVersionId": "string"
}
```

이벤트 페이로드는 Cloud Code에 파라미터로 전달됩니다.

자세한 내용은 Leaderboards의 [초기화 이벤트](/triggers/manage-events/supported-ugs-events.md#reset)를 참고하십시오.

## 배포 파일 생성##create-deployment-files

이 샘플에서는 UGS CLI를 사용하여 서비스별로 구성 파일을 배포하여 구현합니다.

사용 사례 샘플을 구현하려면 다음 파일을 설정해야 합니다.

* `COIN.ecc`: Economy 재화 구성을 포함합니다.
* `leaderboard.lb`: Leaderboards 구성을 포함합니다.
* `triggers-config.tr`: Triggers 구성을 포함합니다.
* 리더보드가 초기화될 때 상위 플레이어에게 게임 내 재화로 보상을 지급하는 로직을 정의하는 Cloud Code 모듈이나 스크립트입니다.

`reward-top-players`라는 폴더를 만들어 모든 파일을 저장합니다. 한번에 모든 파일을 배포할 수 있습니다.

### Economy 설정##set-up-economy

게임 내 재화를 사용해 상위 플레이어에게 보상하려면 Economy에서 가상 재화를 만들어야 합니다.

UGS CLI를 사용해 Economy config 파일을 만듭니다.

```bash
ugs economy currency new-file COIN.ecc
```

다음 콘텐츠를 `COIN.ecc` 파일에 추가합니다.

```json
{
  "$schema": "https://ugs-config-schemas.unity3d.com/v1/economy/economy-currency.schema.json",
  "initial": 0,
  "name": "Coin"
}
```

구성을 통해 가상 재화와 `Coin` 이름을 정의합니다. 파일 이름은 재화 ID와 상응합니다.

### Leaderboards 설정##set-up-leaderboards

UGS CLI를 사용해 리더보드 구성 파일을 만듭니다.

```bash
ugs leaderboards new-file leaderboard.lb
```

다음 콘텐츠를 `leaderboard.lb` 파일에 추가합니다.

```json
{
  "$schema": "https://ugs-config-schemas.unity3d.com/v1/leaderboards.schema.json",
  "SortOrder": "asc",
  "UpdateType": "keepBest",
  "Name": "leaderboard"
}
```

구성을 통해 오름차순으로 리더보드를 정의하며 최고 점수를 계속 표시합니다. 파일 이름은 리더보드 ID와 상응합니다.

### Cloud Code 설정##set-up-cloud-code

리더보드가 초기화될 때 상위 플레이어에게 게임 내 재화로 보상을 지급하는 로직을 정의하려면 Cloud Code 모듈이나 스크립트를 생성해야 합니다.

#### Cloud Code C# 모듈##cloud-code-c-module

Economy 및 Leaderboards 클라이언트 SDK를 사용하여 Economy 서비스, Leaderboards 서비스와 상호 작용할 수 있습니다.

UGS CLI를 사용하여 Cloud Code 모듈을 새로 만듭니다.

```bash
ugs cc m new-file RewardTopPlayers RewardTopPlayers
```

그런 다음 샘플 솔루션에 다음 콘텐츠를 추가합니다. 문자열 인수 `leaderboardId`를 포함해 모듈 함수를 만듭니다.

```csharp
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Unity.Services.CloudCode.Apis;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudCode.Shared;
using Unity.Services.Economy.Model;
using Unity.Services.Leaderboards.Model;

namespace RewardTopPlayers;

public class RewardTopPlayers
{
    private const string CoinsKey = "COIN";
    private const int Amount = 10;

    private readonly ILogger<RewardTopPlayers> _logger;
    public RewardTopPlayers(ILogger<RewardTopPlayers> logger)
    {
        _logger = logger;
    }

    [CloudCodeFunction("IssueReward")]
    public async Task Reward(IExecutionContext ctx, IGameApiClient gameApiClient, string leaderboardId, string leaderboardVersionId)
    {
        var top5Players = await GetTop5(ctx, gameApiClient, leaderboardId, leaderboardVersionId);
        foreach (var score in top5Players)
        {
            try
            {
                // Retrieve the player's configuration to ensure currency configuration is synced
                var configResponse = await gameApiClient.EconomyConfiguration.GetPlayerConfigurationAsync(ctx, ctx.ServiceToken, ctx.ProjectId, score.PlayerId);
                var res = await gameApiClient.EconomyCurrencies.IncrementPlayerCurrencyBalanceAsync(ctx, ctx.ServiceToken, ctx.ProjectId,
                    score.PlayerId,
                    CoinsKey, new CurrencyModifyBalanceRequest(amount: Amount), configResponse.Data.Metadata.ConfigAssignmentHash);
                _logger.LogInformation("Incremented balance for playerId {playerId} by {amount}", score.PlayerId, Amount);
            }
            catch (ApiException e)
            {
                _logger.LogError(e, "Failed to increment balance for playerId {playerId}. Error: {Error}", score.PlayerId, e.Message);
                throw;
            }
        }
    }


    public async Task<List<LeaderboardEntry>> GetTop5(IExecutionContext ctx, IGameApiClient gameApiClient, string leaderboardId, string leaderboardVersionId)
    {
        try
        {
            var results = await gameApiClient.Leaderboards.GetLeaderboardVersionScoresAsync(ctx, ctx.ServiceToken, new Guid(ctx.ProjectId),
                leaderboardId, leaderboardVersionId, 0, 5);
            return results.Data.Results;

        }
        catch (ApiException e)
        {
            _logger.LogError(e, "Failed to get top players for leaderboard: {LeaderboardId}. Error: {Error}", leaderboardId, e.Message);
            throw new Exception($"Failed to get top players for leaderboard: {leaderboardId}. Error: {e.Message}");
        }
    }

    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(GameApiClient.Create());
        }
    }
}
```

#### Cloud Code JavaScript 스크립트##cloud-code-javascript-script

Cloud Code 스크립트를 생성해서도 똑같은 결과물을 얻을 수 있습니다.

UGS CLI를 사용하여 Cloud Code 스크립트를 새로 만듭니다.

```bash
ugs cc s new-file RewardTopPlayers.js
```

그런 다음 샘플 스크립트 파일에 다음 콘텐츠를 추가합니다.

##### JavaScript##javascript

```javascript
const axios = require("axios");
const { CurrenciesApi, ConfigurationApi } = require("@unity-services/economy-2.4");
const { LeaderboardsApi } = require("@unity-services/leaderboards-1.1");


module.exports = async ({ params, context, logger }) => {
  // Get top 5 players from the Leaderboard
  const leaderboardsApi = new LeaderboardsApi(context);
  const cointAmount = 10;

  let result;

  try {
    result = await leaderboardsApi.getLeaderboardVersionScores(context.projectId, params.leaderboardId, params.leaderboardVersionId, 0, 5);
  } catch (err) {
    logger.error("Failed to retrieve players from the leaderboard", { "error.message": err.message }, { "leaderboardId": params.leaderboardId });
    throw err;
  }

  const currencyId = "COIN";
  const currenciesApi = new CurrenciesApi(context);
  const configApi = new ConfigurationApi(context);


  // Reward currency to every player
  const promises = result.data.results.map(async (score) => {
    try {
      // Retrieve the player config to get the configAssignmentHash.
      // This is needed to ensure the currency is synced
      var config = await configApi.getPlayerConfiguration({
        playerId: score.playerId,
        projectId: context.projectId,
      });


      await currenciesApi.incrementPlayerCurrencyBalance({
        currencyId,
        playerId: score.playerId,
        projectId: context.projectId,
        configAssignmentHash: config.data.metadata.configAssignmentHash,
        currencyModifyBalanceRequest: { amount: cointAmount },
      });
    } catch (err) {
      logger.error("Failed to increment currency for player", { "error.message": err.message }, { "affectedPlayerId": score.playerId });
      return;
    }
  });

  await Promise.all(promises);
};
```

### Triggers 설정##set-up-triggers

Cloud Code 리소스를 일정에 연결하려면 트리거를 생성합니다. 리더보드가 초기화되는 등의 이벤트가 발생하면 트리거가 Cloud Code 스크립트나 모듈을 실행합니다.

```bash
ugs tr new-file triggers-config.tr
```

Cloud Code 모듈을 생성한 경우, 다음 구성을 사용합니다.

```json
{
  "$schema": "https://ugs-config-schemas.unity3d.com/v1/triggers.schema.json",
  "Configs": [
    {
      "Name": "reward-leaderboard",
      "EventType": "com.unity.services.leaderboards.reset.v1",
      "ActionUrn": "urn:ugs:cloud-code:RewardTopPlayers/IssueReward",
      "ActionType": "cloud-code"
    }
  ]
}
```

Cloud Code 스크립트를 생성한 경우, 다음 구성을 사용합니다.

```json
{
  "$schema": "https://ugs-config-schemas.unity3d.com/v1/triggers.schema.json",
  "Configs": [
    {
      "Name": "reward-leaderboard",
      "EventType": "com.unity.services.leaderboards.reset.v1",
      "ActionUrn": "urn:ugs:cloud-code:RewardTopPlayers",
      "ActionType": "cloud-code"
    }
  ]
}
```

## 구성 파일 배포##deploy-the-configuration-files

`reward-top-players` 폴더에 다음 파일이 포함되어 있어야 합니다.

* `COIN.ecc` - Economy 재화 구성이 포함되어 있습니다.
* `leaderboard.lb` - Leaderboards 구성이 포함되어 있습니다.
* `triggers-config.tr` - Trigger 구성이 포함되어 있습니다.

다음 중 하나가 필요합니다.

* `RewardTopPlayers.sln` 폴더 - Cloud Code 모듈이 포함되어 있습니다.
* `RewardTopPlayers.js` 폴더 - Cloud Code 스크립트가 포함되어 있습니다.

UGS CLI를 사용하여 한번에 전부 배포할 수 있습니다.

```bash
ugs deploy .
```

## 선택 사항: 리더보드에 점수 추가##optional:-add-scores-to-the-leaderboard

다음 Cloud Code 스크립트를 실행하여 점수를 [Unity Dashboard](https://dashboard.unity3d.com/)의 리더보드에 추가할 수 있습니다. 새 플레이어를 추가하려면 모든 테스트 실행에서 플레이어 ID 토큰을 재생성해야 합니다.


**Frame:**
![](/api/media?file=/cloud-code/media/images/cloud-code-generate-button.png)

### JavaScript##javascript

```javascript
const { LeaderboardsApi } = require("@unity-services/leaderboards-1.1");
const _ = require("lodash-4.17");

module.exports = async ({ params, context, logger }) => {

  const leaderboardsApi = new LeaderboardsApi({ accessToken: context.accessToken });
  const leaderboardID = "leaderboard";

  var randomScore = _.random(1, 100);
  try {
    await leaderboardsApi.addLeaderboardPlayerScore(context.projectId, leaderboardID, context.playerId, { score: randomScore });

  } catch (err) {
    logger.error("Failed to add score to the leaderboard", { "error.message": err.message });
  }
};
```

이렇게 하면 리더보드에 점수를 추가하고 사용 사례 샘플을 테스트할 수 있습니다.

## 결과 확인##validate-the-result

결과를 확인하려면 리더보드가 초기화되기 전에 리더보드에서 플레이어 ID를 적어 두어야 합니다.

1. [Unity Dashboard](https://cloud.unity.com)로 이동합니다.
2. **Products** > **Leaderboards**를 선택합니다.
3. **Overview** 섹션을 선택합니다.
4. `leaderboard` 리더보드를 선택합니다.
5. **Entries** 탭을 선택합니다.
6. 상위 플레이어 ID 중 하나를 기록해 둡니다.

그런 다음 리더보드를 초기화합니다.

1. [Unity Dashboard](https://cloud.unity.com)로 이동합니다.
2. **Products** > **Leaderboards**를 선택합니다.
3. **Overview** 섹션을 선택합니다.
4. `leaderboard` 리더보드를 선택합니다.
5. **Reset Leaderboard**를 선택합니다.
6. **Archive current scores**를 활성화합니다.

아래 단계를 따라 플레이어가 보상으로 10코인을 받았는지 확인합니다.

1. **Products** > **Player Management**를 선택합니다.
2. 검색 필드에 앞서 기록해 둔 플레이어 ID를 입력하고 **Find Player**를 선택합니다.
3. **Economy** > **Currencies** 섹션으로 이동합니다. 플레이어가 보상으로 10코인을 받았음을 확인할 수 있으면 성공적으로 완료된 것입니다.

게임 로직에 맞게 리더보드를 초기화하도록 구성하는 방법을 자세히 알아보려면 [초기화](/leaderboards/concepts/resets.md)를 참고하십시오.
