> ## 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.

# 제작 게임 3부 - 코딩

> PlayFab Economy V2 제작 게임 튜토리얼의 3부입니다. Economy V2 API를 호출해 인벤토리, 레시피, 제작 아이템을 관리하는 C# 코드를 작성합니다.

# 3부 - 코딩 + 예제

이제 환경이 준비되고 Game Manager에 익숙해졌으므로 게임 코딩을 시작할 수 있습니다.

## 필수 구성 요소

1. [1부 - 환경 설정](/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/crafting-game-environment)
2. [2부 - Game Manager 사용](/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/crafting-game-game-manager)

## 1단계 - 환경 설정 구성

코딩을 시작하자마자 가장 먼저 할 일로 권장하는 것은 환경 설정을 구성하는 것입니다. 이렇게 하면 사용자가 하는 모든 호출이 타이틀에 연결되고 특정 개발자 시크릿 키를 사용하도록 안심할 수 있습니다.

이 단계를 완료하려면 코드 어디에서든(찾기 쉬운 곳이 좋습니다) TitleId와 DeveloperSecretKey 변수를 지정하고 값을 할당하세요.

다음과 같이 두 개의 별도 코드 줄을 추가하여 이 작업을 수행합니다.

```csharp theme={null}
PlayFabSettings.staticSettings.TitleId = "{Your Title ID}";
PlayFabSettings.staticSettings.DeveloperSecretKey = "{Your Developer Secret Key}";
```

## 2단계 - 인증 받기

환경이 설정되고, PlayFab NuGet 패키지가 프로젝트에 설치되고 구성되었으며, 스튜디오와 타이틀이 모두 생성되었으면, 인증 코딩을 시작할 수 있습니다.

<Note>
  사용자가 인증을 받는 방법에는 여러 가지가 있습니다. 이 예제에서는 **LoginWithCustomId** 메서드를 사용합니다. 플랫폼별을 포함하여 다른 인증 방법도 있습니다. 자세한 내용은 [로그인 기본 사항](/services/playfab/identity/player-identity/login/login-basics-best-practices)을 참조하세요.
</Note>

다음은 익명 로그인으로 분류되는 **LoginWithCustomID**를 사용해 사용자를 인증하는 C# 샘플 코드입니다. 인증을 위해 API를 호출하기 전에, 로그인 프로세스의 일부로 생성되는 **EntityKey**를 저장할 전역 변수를 먼저 선언해야 합니다.

**EntityKey**는 API에 대한 모든 종류의 호출을 만드는 데 사용되며, 모든 요청과 응답을 로그인한 사용자에 연결하는 것입니다.

전역 EntityKey 변수의 선언은 다음과 같습니다.

```csharp theme={null}
private static PlayFab.ClientModels.EntityKey entityKey;
```

***

그런 다음 사용자 인증을 위한 로직은 다음과 같습니다.

### C# SDK

```csharp theme={null}
var request = new LoginWithCustomIDRequest { CustomId = username };
var loginTask = PlayFabClientAPI.LoginWithCustomIDAsync(request);
entityKey = loginTask.Result.Result.EntityToken.Entity;
```

### API

```json theme={null}
{
  "CustomId": "{{Username}}",
  "CreateAccount": false,
  "TitleId": "{{TitleId}}"
}
```

***

위 코드는 게임 타이틀에 연결된 기존 플레이어를 찾습니다. 하지만 일치하는 사용자 이름을 가진 사용자가 없으면 실패합니다. 이를 우회하기 위해 요청 본문에 "CreateAccount = true" 매개 변수를 추가할 수 있습니다. 이렇게 하면 사용자가 사용자 이름으로 보내는 것과 일치하는 것이 없을 때 PlayFab이 새 플레이어를 만들 수 있습니다. 결과적으로 코드는 다음과 같습니다.

### C# SDK

```csharp theme={null}
var request = new LoginWithCustomIDRequest { CustomId = username, CreateAccount = true };
var loginTask = PlayFabClientAPI.LoginWithCustomIDAsync(request);
entityKey = loginTask.Result.Result.EntityToken.Entity;
```

### API

```json theme={null}
{
  "CustomId": "{{Username}}",
  "CreateAccount": true,
  "TitleId": "{{TitleId}}"
}
```

***

로그인이 성공하면 API는 **SessionTicket**, 사용자의 **PlayFab ID**, **EntityToken** 등 몇 가지 데이터 집합을 반환합니다. 이는 아래에 자세히 설명된 API의 직접 응답에서 볼 수 있습니다.

```json theme={null}
{
    "code": 200,
    "status": "OK",
    "data": {
        "SessionTicket": "{{SessionTicket}}",
        "PlayFabId": "{{PlayFabID}}",
        "NewlyCreated": false,
        "SettingsForUser": {
            "NeedsAttribution": false,
            "GatherDeviceInfo": true,
            "GatherFocusInfo": true
        },
        "LastLoginTime": "2023-08-01T17:09:54.508Z",
        "EntityToken": {
            "EntityToken": "{{EntityToken}}",
            "TokenExpiration": "2023-08-04T21:20:35Z",
            "Entity": {
                "Id": "{{Player ID}}",
                "Type": "title_player_account",
                "TypeString": "title_player_account"
            }
        },
        "TreatmentAssignment": {
            "Variants": [],
            "Variables": []
        }
    }
}
```

***

API에서 `"code":200`이 반환되거나 위의 C# 예제에서 `loginTask`에 정보를 받으면 인증되었으며 다음 단계로 진행할 준비가 된 것입니다!

## 3단계 - 시작 인벤토리 설정

유효한 플레이어로 로그인했거나 새 플레이어를 만들었으므로 첫 번째 단계는 해당 플레이어의 시작 인벤토리를 설정하는 것입니다. 이를 위해 [ExecuteInventoryOperations](https://learn.microsoft.com/en-us/rest/api/playfab/economy/inventory/execute-inventory-operations) API 호출을 사용합니다.

<Note>
  ExecuteInventoryOperations 호출을 사용하면 플레이어의 인벤토리에 대해 여러/배치 [InventoryOperations](https://learn.microsoft.com/en-us/rest/api/playfab/economy/inventory/execute-inventory-operations)을 실행할 수 있습니다. 지원되는 작업 유형은 다음과 같습니다: - Add - Delete - Purchase - Subtract - Transfer - Update
</Note>

이 예에서는 게임에 해당하는 아이템을 추가합니다. Stone 3개, Cream 1개, Gold 1개입니다(더 많은 아이템을 만들었다고 가정합니다. 그렇지 않다면 계속하기 전에 만들어주세요). 그러나 단순히 **Add** 작업을 사용하는 대신 **Purchase** 작업을 사용하고 필요한 인스턴스를 무료로 구매합니다.

<Note>
  이 단계가 작동하려면 먼저 Game Manager에서 타이틀에 아이템을 생성해야 합니다.
</Note>

아래 스니펫은 C#과 PlayFab API를 사용해 이러한 호출을 일괄적으로 만드는 방법을 보여줍니다.

### C# SDK

이 C# 예제에서 볼 수 있듯이, 모든 트랜잭션에 대한 표준 값으로 purchasePrice를 설정합니다. 이는 모두 가격을 0으로 만들기 때문입니다.

한 아이템은 여러 개의 가격을 가질 수 있으므로 `purchasePrice` 변수는 `List<PurchasePriceAmount>` 형식입니다.

다음으로 눈치챌 수 있는 것은 요청이 `InventoryOperation` 목록 외에 Entity도 가진다는 점입니다. 이를 위해 이전에 로그인에서 얻은 값을 사용해 새로 만듭니다.

마지막으로, 각 `InventoryOperation`은 `Purchase` 값을 갖지만, 이는 **ExecuteInventoryOperations** 호출에서 사용할 수 있는 각 값(Add, Delete, Purchase, Subtract, Transfer, Update) 중 하나일 수 있습니다. 이 경우 `Purchase` 식별자를 사용하므로 `new PurchaseInventoryItemsOperation`이 필요합니다.

구매하려는 아이템이 무엇인지 명확히 해야 합니다. 이는 `InventoryItemReference`를 사용해 수행하며, 이는 아이템의 ID를 유일한 매개 변수로 받습니다.

코드에는 \*\*SearchItem(\{itemName})\*\*이라는 메서드가 있지만, 이를 아이템의 ID를 나타내는 문자열로 쉽게 대체할 수 있습니다. 각 아이템의 이름(또는 ID)이 해당 수량과 일치하는지 확인하세요.

```csharp theme={null}
var purchasePrice = new List<PurchasePriceAmount> { new PurchasePriceAmount { ItemId = freeItemId, Amount = 0 } };
var request = new ExecuteInventoryOperationsRequest 
{ 
    Entity = new PlayFab.EconomyModels.EntityKey { Id = entityKey.Id, Type = entityKey.Type },
    Operations = new List<InventoryOperation> {
        new InventoryOperation
        {
            Purchase = new PurchaseInventoryItemsOperation { 
                Item = new InventoryItemReference {AlternateId = new AlternateId { Type = "FriendlyId", Value = "Stone" } }, 
                Amount = 3, 
                PriceAmounts = purchasePrice
            }
        },
        new InventoryOperation
        {
            Purchase = new PurchaseInventoryItemsOperation {
                Item = new InventoryItemReference { AlternateId = new AlternateId { Type = "FriendlyId", Value = "Gold" } },
                Amount = 1,
                PriceAmounts = purchasePrice
            }
        },``
        new InventoryOperation
        {
            Purchase = new PurchaseInventoryItemsOperation {
                Item = new InventoryItemReference {AlternateId = new AlternateId { Type = "FriendlyId", Value = "Cream" }},
                Amount = 1,
                PriceAmounts = purchasePrice
            }
        }
    }
};
await PlayFabEconomyAPI.ExecuteInventoryOperationsAsync(request);
```

위 코드에서 `AlternateId`와 `FriendlyId`를 성공적으로 사용하려면 아이템이 Game Manager에서 해당 값이 구성되어 있어야 합니다. 이를 위해 자세한 단계를 안내하는 [2부 - Game Manager 사용](/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/crafting-game-game-manager)을 참조하세요.

### API

아래에 표시된 요청 본문과 함께 **ExecuteInventoryOperations** API 엔드포인트를 호출합니다. ID를 해당 값으로 바꿉니다. `Operations`는 배열이며, 동시에 여러 유형의 작업으로 채울 수 있습니다.

여러분이 만드는 각 API 호출에서 활성 세션 또는 로그인한 사용자에 특정한 **EntityToken**을 사용해 `X-EntityToken` 헤더를 설정해야 함을 이해하는 것이 중요합니다. 즉, API 호출을 하기 전에 먼저 (이 예에서) **LoginWithCustomID**를 수행하고 반환 메시지에서 **EntityToken**을 얻어 모든 API 호출의 헤더로 사용해야 합니다.

```json theme={null}
{
  "Operations": [
    {
      "Purchase": {
            "Item": {
                "Id": {Stone ID}
            },
            "Amount": 3,
            "PriceAmounts": [
                {
                "ItemId": {Free Item ID},
                "Amount": 0
                }
            ]
        }
    },
    {
      "Purchase": {
            "Item": {
                "Id": {Gold ID}
            },
            "Amount": 1,
            "PriceAmounts": [
                {
                "ItemId": {Free Item ID},
                "Amount": 0
                }
            ]
        }
    },
    {
      "Purchase": {
            "Item": {
                "Id": {Cream ID}
            },
            "Amount": 1,
            "PriceAmounts": [
                {
                "ItemId": {Free Item ID},
                "Amount": 0
                }
            ]
        }
    }
  ]
}
```

다음 JSON은 위 호출 이후의 성공적인 반환 메시지의 예입니다. ID는 수정별로 부여되므로 세 개의 서로 다른 트랜잭션 ID가 반환됨을 볼 수 있지만, 이는 세 개의 별개 트랜잭션이 있다는 뜻은 아닙니다. 오히려 반대로, 하나의 단일 트랜잭션으로 처리되지만 세 개의 수정이 있는 것이므로, 수정 개수와 관계없이 전체 트랜잭션에 대해 성공 또는 실패 하나만 반환합니다.

```json theme={null}
{
    "code": 200,
    "status": "OK",
    "data": {
        "IdempotencyId": {Idempotency ID},
        "TransactionIds": [
            "200",
            "201",
            "202"
        ],
        "ETag": "1/MjAy"
    }
}
```

***

## 4단계 - 번들 만들기

번들을 사용하면 여러 아이템을 하나의 아이템으로 그룹화할 수 있습니다. 번들에 대한 자세한 내용은 [이 링크](/services/playfab/economy-monetization/economy-v2/catalog/bundles)의 **번들 문서**를 참조하세요.

이 예에서는 **번들**을 사용해 **Kitchen**에서 반환되는 아이템을 그룹화합니다. 이는 **Icebox** 아이템을 유지하는 데 도움이 됩니다. Icebox의 아이디어는 아이스크림을 구매할 때 소모되지 않는 아이템으로 유지되는 것입니다. 비록 Icebox가 필요한 재료 중 하나임에도 불구하고요.

이것이 작동하는 방식의 상위 수준 개요는 다음과 같습니다. 인벤토리에 Cream 1개와 Icebox 1개를 가진 플레이어를 상상해 보세요. Kitchen 스토어에 접근하면 이 플레이어는 아이스크림 1개를 구매/만들기 위해 두 아이템 모두를 사용해야 합니다. Icebox가 소모되지 않는다는 점을 감안할 때, 예상되는 결과는 인벤토리에 아이스크림 1개와 Icebox 1개가 남고, Cream 아이템 1개만 소비되는 것입니다.

내부적으로 이를 처리하는 방법은 Icebox 1개와 Cream 1개의 가격이 매겨지고 반환 아이템이 아이스크림 1개와 Icebox 1개인 번들을 사용하는 것입니다. 이는 플레이어의 인벤토리에 있는 Icebox가 소비되지만, 이를 소비하는 동일한 트랜잭션이 다른 하나를 반환하므로, 플레이어에게는 Icebox가 항상 인벤토리에 있다는 인상을 주는 반면, 실제로는 Icebox 인스턴스는 다르지만 플레이어에게는 보이지 않는다는 것을 의미합니다.

번들을 만들려면 새 아이템을 만들 때와 마찬가지로 타이틀의 **Economy** 섹션으로 이동해야 합니다. **Items**, **Currency** 등과 함께 **Bundles**라는 탭이 있습니다. 선택하면 화면 오른쪽 상단에 **New bundle**이라는 파란색 버튼이 표시됩니다.

그러면 새 아이템을 만들 때와 유사한 양식이 표시됩니다. 주요 차이점은 아래로 스크롤하면 **Items**라는 섹션을 볼 수 있다는 것입니다. 여기서 번들에 포함하고자 하는 아이템을 추가할 수 있으며, 이는 구매 시 플레이어의 인벤토리로 이전됩니다.

**Add** 버튼을 클릭한 후 카탈로그의 모든 아이템의 검색 가능한 목록이 표시됩니다. 여기서 번들에 포함할 아이템을 선택할 수 있습니다. 아이템을 선택하고 추가한 즉시 **Save and publish**를 진행할 수 있으며, 번들이 이제 활성화되어 스토어에서 구매 가격으로 선택할 수 있게 됩니다.

## 5단계 - 스토어 만들기

제작 게임 예제를 따라, 플레이어가 스토어에서 아이템을 구매하도록 해야 합니다. **Science Machine**, **Alchemy Engine**, **Kitchen**이라는 세 개의 서로 다른 스토어(이 경우 위치)가 있을 것입니다.

**Kitchen**에 초점을 맞춥니다. 여기서 플레이어는 **Cream 1개**와 **Icebox 1개**(만들어야 할 새 아이템)의 가격으로 **아이스크림**(만들어야 할 새 아이템)을 구매할 수 있습니다. 이 특정 사례는 스토어 및 트랜잭션과 함께 **번들**이 작동하는 방식을 보여주기 때문에 선택되었습니다.

스토어는 [Game Manager](https://developer.playfab.com)에서만 만들 수 있으며, 왼쪽 탐색 표시줄의 **Economy** 섹션으로 이동한 다음 다양한 탭 옵션 중에서 **Stores**를 선택하고 파란색 **New store** 버튼을 선택합니다.

이는 새 아이템을 만들 때 사용한 것과 유사한 양식을 표시하며, 여기서 유일한 필수 필드는 시작 날짜와 스토어에 부여할 제목입니다. 아래로 스크롤하면...

## 6단계 - 스토어에 번들 추가

이제 번들과 스토어를 만들고 게시했으므로, 번들을 스토어에서 반환 가능한 아이템으로 추가할 수 있습니다. 이를 수행하려면 스토어로 이동해 아래로 스크롤하여 **Items** 제목이 보일 때까지 이동한 다음 **Add** 버튼을 선택합니다.

이는 카탈로그의 아이템 목록을 표시합니다. 검색 표시줄 왼쪽에는 **Items**, **UGC Items**, **Bundles**, **Subscription**과 같은 다양한 객체 유형이 있는 드롭다운 메뉴가 있습니다. **Bundles**를 선택하면 목록이 번들 유형 아이템만 반영하도록 필터링됩니다. 여기에서 이전에 만든 번들을 볼 수 있어야 합니다. 번들 이름 옆의 **Add** 버튼을 선택한 다음 창 끝의 **Add** 버튼을 선택하면 번들이 해당 특정 스토어의 가능한 트랜잭션 아이템으로 추가됩니다.

계속하기 전에 마지막 단계가 하나 있습니다. 번들의 가격을 설정하는 것입니다. 이 가격은 다른 아이템 가격 책정과 동일한 방식으로 작동하며, 플레이어가 구매를 수락하기 전에 필요한 가격 아이템을 인벤토리에 가지고 있어야 함을 의미합니다. 가격을 설정하려면 번들 오른쪽의 **Add new price** 버튼을 선택하고 아이템 목록에서 가격으로 사용하려는 아이템을 선택할 수 있습니다. 여기서 가격은 **Icebox 1개**와 **Cream 1개**로 하려고 합니다.

이제 번들이 스토어에 추가되고 그에 따라 가격이 책정되었으므로 다음 단계로 넘어갈 수 있습니다.

## 7단계 - 번들 구매

번들과 스토어가 만들어지고 번들이 스토어에 연결되면 이제 해당 번들을 구매하고 결과 아이템을 인벤토리로 가져올 수 있습니다.

플레이어 관점에서 번들을 구매하려면 PlayFab API의 **PurchaseInventoryItems**를 사용할 수 있습니다. 이는 번들뿐만 아니라 단일 아이템에도 작동합니다.

### API

다음 경우에는 **Icebox 1개**와 **Cream 1개** 가격으로 **번들 1개**를 구매하려고 합니다. **PriceAmount**의 배열 속성을 사용해 1개 이상의 아이템을 가격으로 설정합니다. 번들에 **FriendlyId**를 부여했는지 확인하세요.

```json theme={null}
{
  "Item": {
    "AlternateId": {
        "Type": "FriendlyId",
        "Value": "Ice Cream and Ice Box"
    }
  },
  "Amount": 1,
  "PriceAmounts": [
    {
        "ItemId": "0d2ab329-2fc5-4ae1-9e3c-19f9fc9bbf86",
        "Amount": 1
    },
    {
        "ItemId": "e5276289-f839-4bde-acce-309ea7959e45",
        "Amount": 1
    }
  ],
  "DeleteEmptyStacks": true,
  "StoreId": "f116f1d4-64f9-4be7-9660-a3c1f021100b"
}
```

다음 스니펫은 성공적인 API 호출 이후 예상되는 반환입니다.

```json theme={null}
{
    "code": 200,
    "status": "OK",
    "data": {
        "ETag": "1/MjEz",
        "IdempotencyId": "d710508f-defc-4382-bbd1-a3576417b3f7",
        "TransactionIds": [
            "212",
            "213"
        ]
    }
}
```

<Note>
  응답에서 두 개의 서로 다른 `TransactionIds`를 얻는 것을 볼 수 있습니다. 이는 `DeleteEmptyStacks` 속성이 추가 트랜잭션으로 처리되기 때문입니다. 이전 예에서 트랜잭션 212는 번들 구매에 해당하고, 트랜잭션 213은 빈 Cream 스택 삭제에 해당합니다(인벤토리에 Cream 1개만 있었다고 가정).
</Note>

### C# SDK

C# 코드가 어떻게 보여야 하는지에 대해서는 아래에서 예제의 스니펫을 볼 수 있습니다.

```csharp theme={null}
var purchaseRequest = new PurchaseInventoryItemsRequest
{
    Entity = new PlayFab.EconomyModels.EntityKey { Id = entityKey.Id, Type = entityKey.Type },
    Item = new InventoryItemReference { Id = "34167d9f-c8d7-4e17-9a87-b6af1fc389b2" }, //bundle id
    Amount = 1,
    PriceAmounts = new List<PurchasePriceAmount>() { 
        new PurchasePriceAmount { 
            ItemId = "e5276289-f839-4bde-acce-309ea7959e45", 
            Amount = 1 
        }, //cream id and quantity
        new PurchasePriceAmount { 
            ItemId =  "0d2ab329-2fc5-4ae1-9e3c-19f9fc9bbf86", 
            Amount = 1 
        } //icebox id and quantity
    }, 
    StoreId = "f116f1d4-64f9-4be7-9660-a3c1f021100b",
    DeleteEmptyStacks = true
};

var result = await PlayFabEconomyAPI.PurchaseInventoryItemsAsync(purchaseRequest);
```

***

## 참고 항목

* [Economy v2 개요](/services/playfab/economy-monetization/economy-v2/overview)
* [설정](/services/playfab/economy-monetization/economy-v2/settings)
* [Economy v2 스토어](/services/playfab/economy-monetization/economy-v2/catalog/stores#creating-a-store)


## Related topics

- [제작 게임 1부 - 설정](/ko/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/crafting-game-environment.md)
- [제작 게임 2부 - Game Manager](/ko/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/crafting-game-game-manager.md)
- [제작 게임 - 컨텍스트](/ko/services/playfab/economy-monetization/economy-v2/tutorials/craftingGame/game-context.md)
- [XBOX 게임 개발 문서](/ko/index.md)
- [PlayFab Economy v2 문서](/ko/services/playfab/economy-monetization/index.md)
