> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 플레이어 게시자 데이터를 사용하여 다중 타이틀 플레이 보상 부여

> PlayFab 플레이어 게시자 데이터와 CloudScript를 사용하여 크로스 타이틀 로그인을 추적하고 여러 타이틀을 플레이하는 플레이어에게 가상 통화 보상을 부여합니다.

보상은 일반적으로 플레이어 데이터 외부의 *다른* 시스템을 포함하므로, 이 예제에서는 간단히 하기 위해 가상 통화를 수여하는 방법을 보여줍니다.

## 요구 사항

* [PlayFab 개발자 계정](https://developer.playfab.com).
* 플레이어는 동일한 자격 증명을 사용하여 *두* 타이틀 모두에 로그인해야 합니다. 한 가지 방법은 [로그인 기본 사항 및 모범 사례](/services/playfab/identity/player-identity/login/login-basics-best-practices) 튜토리얼에서 설명한 **복구 가능한 자격 증명**을 사용하는 것입니다. 익명 계정에 **복구 가능한** 로그인을 추가하려면 [계정 연결](/services/playfab/identity/player-identity/login/quickstart) 튜토리얼을 참조하세요.
* 이 예제에서는 [CloudScript](/services/playfab/live-service-management/service-gateway/automation/cloudscript/writing-custom-cloudscript)에 대한 실무 지식이 필요합니다.
  * 이 예제에서는 플레이어의 부정 행위를 방지하기 위한 기본적인 데이터 보안을 보여줍니다. 마찬가지로 타이틀에서 서버 API를 사용하는 경우 사용자 지정 게임 서버에서도 서버 API를 사용할 수 있습니다.
* PlayFab을 통해 트리거되는 보상은 적절한 PlayFab 기능의 사용을 필요로 합니다. PlayFab 보상은 [**가상 통화**](/services/playfab/economy-monetization/economy-v2/tutorials/currencies), [**인벤토리 항목**](/services/playfab/player-progression/player-data/player-inventory), [**사용자 지정 플레이어 데이터**](/services/playfab/player-progression/player-data/quickstart), [**통계**](/services/playfab/community/leaderboards/tournaments-leaderboards/using-player-statistics) 등의 형태일 수 있습니다. PlayFab 시스템 외부에서 보상을 배포하는 것은 고급 주제이며, 이 튜토리얼에서는 다루지 않습니다.

또한 개발자는 CloudScript에서 이루어지는 모든 서버 API 호출에 대해 우수한 오류 처리를 사용할 것을 권장합니다.

## 각 게임이 게시자 데이터에 로그인을 보고

각 게임은 로그인이 발생했음을 보고해야 합니다. 간단히 하기 위해 이 예제에서는 플레이한 각 다른 게시자 타이틀에 대해 각 타이틀당 *하나*의 보상만 제공합니다.

카운터, 타임스탬프 또는 기타 메커니즘을 사용해 이 아이디어를 확장하여 점진적 또는 순차적 보상을 제공할 수 있습니다.

다음 CloudScript는 스튜디오의 *모든* 게임에 있어야 합니다.

```javascript theme={null}
// CloudScript/Javascript

var PUBLISHER_USED_TITLES_KEY = "playedTitleIds";
handlers.TrackTitleUsage = function () {
    // Get the User Publisher Data for this player, and convert it into our expected format
    var getRequest = { Keys: [PUBLISHER_USED_TITLES_KEY], PlayFabId: currentPlayerId };
    var getResult = server.GetUserPublisherInternalData(getRequest);
    var playedTitlesList = JSON.parse(getResult.Data[PUBLISHER_USED_TITLES_KEY].Value); // format is arbitrary, but this example assumes Array<string>
    if (!playedTitlesList)
        playedTitlesList = [];
    // Check if we've played this title already
    var alreadyPlayed = false;
    for (var i = 0; i < playedTitlesList.length; i++)
        alreadyPlayed = alreadyPlayed || playedTitlesList[i] === script.titleId;
    if (alreadyPlayed)
        return; // Nothing to do
    // Update the list of played titles, and re-save
    playedTitlesList.push(script.titleId);
    var setRequest = { PlayFabId: currentPlayerId, Data: { PUBLISHER_USED_TITLES_KEY: JSON.stringify(playedTitlesList) } };
    server.UpdateUserPublisherInternalData(setRequest);
}
```

특히 이 예제는 다음을 사용하는 방법을 보여줍니다.

* server.[GetUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.getuserpublisherinternaldata)
* server.[UpdateUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.updateuserpublisherinternaldata).

## 각 게임이 상환 가능한 보상 확인

어떤 타이틀을 플레이하고 있는지 추적한 후에는 보상을 추적하고 부여해야 합니다. 이 CloudScript 함수는 다른 타이틀을 플레이한 것을 기반으로 사용 가능한 보상을 확인하고 부여합니다.

```javascript theme={null}
// CloudScript/Javascript

var PUBLISHER_REDEEMED_TITLES_KEY = "redeemedTitleIds";
var MY_CROSS_TITLE_REWARDS = { "AU": 10 };
handlers.CheckCrossTitleRewards = function () {
    // Get the publisher data concerning cross-title rewards for this player
    var getRequest = { Keys: [PUBLISHER_USED_TITLES_KEY, PUBLISHER_REDEEMED_TITLES_KEY], PlayFabId: currentPlayerId };
    var getResult = server.GetUserPublisherInternalData(getRequest);
    var redeemedTitleRewards = JSON.parse(getResult.Data[PUBLISHER_REDEEMED_TITLES_KEY].Value); // format is arbitrary, but this example assumes { [key: string]: Array<string> }
    if (!redeemedTitleRewards)
        redeemedTitleRewards = {};
    var playedTitlesList = JSON.parse(getResult.Data[PUBLISHER_USED_TITLES_KEY].Value); // format is arbitrary, but this example assumes Array<string>
    if (!playedTitlesList)
        playedTitlesList = [];

    // Determine which titles are un-redeemed
    var unredeemedTitleIds = [];
    for (var i = 0; i < playedTitlesList.length; i++) {
        if (!redeemedTitleRewards.hasOwnProperty(playedTitlesList[i]) && playedTitlesList[i] !== script.titleId)
            unredeemedTitleIds.push(playedTitlesList[i]);
    }
    if (unredeemedTitleIds.length === 0)
        return null; // Nothing to redeem

    // Give the cross title rewards
    var multiplier = unredeemedTitleIds.length;
    var actualRewards = {}; // MY_CROSS_TITLE_REWARDS is a global constant, so don't modify it or you'll mess up future calls
    // Please note that the number of API calls that may be made from CloudScript, as well as the total available processing time is limited,
    // and so the number of rewards should be as small as possible (only one VC, in this case)
    for (var eachKey in MY_CROSS_TITLE_REWARDS) {
        actualRewards[eachKey] = MY_CROSS_TITLE_REWARDS[eachKey] * multiplier;
        server.AddUserVirtualCurrency({ PlayFabId: currentPlayerId, VirtualCurrency: eachKey, Amount: MY_CROSS_TITLE_REWARDS[eachKey] }); // Can only add 1 VC at a time
    }

    // Save the Publisher data indicating rewards are claimed
    redeemedTitleRewards[script.titleId] = playedTitlesList;
    var setRequest = { PlayFabId: currentPlayerId, Data: { PUBLISHER_REDEEMED_TITLES_KEY: JSON.stringify(redeemedTitleRewards) } };
    server.UpdateUserPublisherInternalData(setRequest);

    // Tell the client the reward
    return actualRewards;
};
```

<Note>
  이 예제는 특히 server.[GetUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.getuserpublisherinternaldata)(여러 키 요청), server.[UpdateUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.updateuserpublisherinternaldata), server.[AddUserVirtualCurrency](xref:titleid.playfabapi.com.server.playeritemmanagement.adduservirtualcurrency)의 사용을 보여줍니다.
</Note>

코드 블록은 다음 단계를 나타냅니다.

* PlayFab에서 데이터를 가져와 로컬 데이터 구조로 파싱합니다([GetUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.getuserpublisherinternaldata) 호출).
* 데이터를 검사하고 보상되지 않은 타이틀 ID를 검색합니다.
* 보상을 부여합니다([AddUserVirtualCurrency](xref:titleid.playfabapi.com.server.playeritemmanagement.adduservirtualcurrency) 호출).
* 청구된 보상을 업데이트합니다([UpdateUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.updateuserpublisherinternaldata) 호출).
* 보상된 금액을 클라이언트에서 사용할 수 있도록 합니다(CloudScript의 return 문을 통해).

## 결론

플레이어 게시자 데이터와 플레이어 데이터는 구조적으로 동일합니다.

플레이어 데이터는 *타이틀별*이어야 하며, 플레이어 게시자 데이터는 모든 타이틀에 걸쳐 관련된 정보만 포함해야 합니다.

## 참고 항목

[게임 내 간단한 크로스 프로모션](https://github.com/PlayFab/PlayFab-Samples/tree/master/Recipes/SimpleCrossPromotion): 하나 이상의 게임에 참여하는 플레이어에게 보상을 부여합니다.


## Related topics

- [엔터티 오브젝트를 사용하여 플레이어 데이터 저장](/ko/services/playfab/live-service-management/game-configuration/entities/entity-objects.md)
- [Player Data](/ko/services/playfab/player-progression/player-data/index.md)
- [플레이어 데이터 빠른 시작](/ko/services/playfab/player-progression/player-data/quickstart.md)
- [XBOX 플레이어 데이터 개요](/ko/services/xbox-services/player-data/live-playerdata-overview.md)
- [PlayFab GDPR - 플레이어 데이터 삭제 및 내보내기](/ko/services/playfab/data-analytics/privacy-compliance/playfab-gdpr-deleting-and-exporting-player-data.md)
