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

# GS2-Inventory

소지품 관리 기능




플레이어가 소지한 아이템 정보를 관리합니다.

아이템 관리에는 3가지 방식이 있습니다.
첫 번째는 스탠다드 인벤토리, 두 번째는 심플 인벤토리, 세 번째는 거대 인벤토리입니다.

스탠다드 인벤토리는 다음과 같은 기능을 제공합니다
- 인벤토리 용량 제한
- 동일한 아이템을 일정 수량마다 여러 스택으로 분할
- 아이템에 유효기간 설정

심플 인벤토리는 스탠다드 인벤토리가 가진 기능을 갖고 있지 않습니다.
그 대신 여러 아이템의 증감 처리를 한 번에 수행할 수 있습니다.
심플 인벤토리를 잘 활용하면 API 호출 횟수를 줄일 수 있습니다.

거대 인벤토리는 심플 인벤토리와 마찬가지로 스탠다드 인벤토리가 가진 기능을 갖고 있지 않습니다.
심플 인벤토리처럼 여러 아이템의 증감 처리를 한 번에 수행할 수도 없습니다.
그 대신 아이템 소지 수량을 64bit 정수 범위를 초과하여 보유할 수 있습니다.

## 스탠다드 인벤토리

### 인벤토리

플레이어가 소지하는 가방에 해당하는 엔티티입니다.
가방에는 용량을 설정할 수 있으며, 그 용량을 초과하는 아이템은 저장할 수 없습니다.

### 아이템

아이템은 소지품의 종류를 정의합니다.
아이템에는 스택 가능한 최대 수량을 지정할 수 있습니다.

예를 들어 포션이라는 아이템 종류가 존재하고 최대 99개까지 스택 가능하도록 설정한 경우,
인벤토리 용량 소비량 1로 최대 99개까지 포션을 소지할 수 있습니다.

또한 아이템에는 여러 스택을 소지할 수 있는지 여부를 지정할 수 있습니다.
여러 스택 소지를 가능하도록 설정하면 100개 이상의 포션을 소지할 수 있게 되며,
예를 들어 150개의 포션을 소지하고 있는 경우 99개 스택된 포션과 51개 스택된 포션이라는 2개의 엔트리가 생성되어
인벤토리 용량이 2 소비됩니다.

여러 스택 소지를 불가능하도록 설정한 경우, 99개를 초과하는 포션은 소지할 수 없게 되며
그 이상 포션을 입수하더라도 폐기됩니다.

#### 아이템의 유효기간

아이템에는 유효기간을 설정할 수 있습니다.
유효기간을 설정한 아이템은 설정 시각이 지나면 자동으로 인벤토리에서 삭제됩니다.

유효기간이 설정된 아이템은 유효기간마다 서로 다른 스택이 생성되며, 각각 인벤토리 용량을 소비합니다.
따라서 여러 스택을 소지할 수 없는 아이템에 유효기간을 붙여 배포하면, 가장 먼저 입수한 유효기간의 아이템(또는 가장 먼저 입수한 유효기간이 없는 아이템)을 제외하고는 폐기됩니다.

아이템을 사용할 때 유효기간(아이템 세트 이름)을 명시적으로 지정하지 않으면 유효기간이 가까운 아이템부터 우선적으로 소비됩니다.

#### 아이템 세트

한 종류의 아이템을 여러 스택으로 관리할 수 있도록, 아이템 종류를 묶은 아이템 세트라는 엔티티가 존재합니다.
이 엔티티는 스택마다 아이템 종류를 나타내는 ID와는 별도로 스택 고유의 ID를 갖습니다.
이 스택 고유 ID를 사용하면 "포션 x 99"와 "포션 x 51"이라는 각각의 스택을 명확히 구분할 수 있습니다.

#### 참조원 (Reference Of)

아이템 세트에는 "참조원" 정보를 부여할 수 있습니다. 이는 특정 아이템이 다른 엔티티(예: 캐릭터 장비, 파티 편성, 마켓 출품 등)에서 사용되고 있음을 나타내기 위해 사용됩니다.
참조원이 하나 이상 설정된 아이템 세트는 소지 수량이 충분하더라도 소비하거나 삭제할 수 없게 됩니다. 이를 통해 장비 중인 아이템을 실수로 판매하거나 강화 소재로 사용해 버리는 실수를 시스템 수준에서 방지할 수 있습니다.

포션을 소비할 때 포션을 나타내는 아이템 ID에 더해 스택 고유 ID를 지정함으로써 어느 스택에서 소비할지 명시할 수 있습니다.
스택 고유 ID는 생략 가능하며, 생략한 경우에는 수량이 가장 적은 스택부터 우선적으로 사용됩니다.

## 심플 인벤토리

### 심플 인벤토리

플레이어가 소지하는 가방에 해당하는 엔티티입니다.
여러 심플 아이템을 묶는 존재로, 심플 인벤토리는 특별한 프로퍼티를 갖지 않습니다.

### 심플 아이템

아이템은 소지품의 종류를 정의합니다.

## 거대 인벤토리

### 거대 인벤토리

플레이어가 소지하는 가방에 해당하는 엔티티입니다.
여러 거대 아이템을 묶는 존재로, 거대 인벤토리는 특별한 프로퍼티를 갖지 않습니다.

### 거대 아이템

아이템은 소지품의 종류를 정의합니다.

## 스크립트 트리거

네임스페이스에 `acquireScript`, `overflowScript`, `consumeScript`, `simpleItemAcquireScript`, `simpleItemConsumeScript`, `bigItemAcquireScript`, `bigItemConsumeScript`를 설정하면 아이템 입수·소비 처리 전후에 커스텀 스크립트를 호출할 수 있습니다. 스크립트는 동기·비동기 실행 방식을 선택할 수 있으며, 비동기의 경우 GS2-Script나 Amazon EventBridge를 통한 외부 처리에도 대응합니다.

설정할 수 있는 주요 이벤트 트리거와 스크립트 설정 이름은 다음과 같습니다.

- `acquireScript`(완료 통지: `acquireDone`): 스탠다드 아이템 입수 전후
- `overflowScript`(완료 통지: `overflowDone`): 용량 초과 시 전후
- `consumeScript`(완료 통지: `consumeDone`): 스탠다드 아이템 소비 전후
- `simpleItemAcquireScript`(완료 통지: `simpleItemAcquireDone`): 심플 아이템 입수 전후
- `simpleItemConsumeScript`(완료 통지: `simpleItemConsumeDone`): 심플 아이템 소비 전후
- `bigItemAcquireScript`(완료 통지: `bigItemAcquireDone`): 거대 아이템 입수 전후
- `bigItemConsumeScript`(완료 통지: `bigItemConsumeDone`): 거대 아이템 소비 전후

## 마스터 데이터 운용
마스터 데이터를 등록하면 마이크로서비스에서 사용 가능한 데이터와 동작을 설정할 수 있습니다.

마스터 데이터의 종류에는 다음과 같은 것들이 있습니다.

- `InventoryModel`: 인벤토리 용량이나 종류를 정의
- `ItemModel`: 스택 상한이나 유효기간을 정의

마스터 데이터 등록은 관리 콘솔에서 등록하는 것 외에도, GitHub에서 데이터를 반영하거나 GS2-Deploy를 사용하여 CI에서 등록하는 워크플로우를 구성할 수 있습니다.

## 버프에 의한 보정

GS2-Buff와 연동하면 인벤토리의 `currentInventoryMaxCapacity`나 `AcquireItemSetByUserId`, `ConsumeItemSetByUserId`, `AcquireSimpleItemsByUserId`, `ConsumeSimpleItemsByUserId`, `AcquireBigItemByUserId`, `ConsumeBigItemByUserId`의 입수량·소비량을 버프로 보정할 수 있습니다. 이벤트나 캠페인에 맞춰 용량이나 입수·소비량을 유연하게 조정할 수 있습니다.

## 트랜잭션 액션

GS2-Inventory에서는 다음과 같은 트랜잭션 액션을 제공합니다.

### 스탠다드 인벤토리
- 검증 액션: 인벤토리 최대 용량 검증, 아이템 소지 수량 검증, 참조원 검증
- 소비 액션: 아이템 세트 소비
- 입수 액션: 용량 가산·설정, 아이템 세트 입수(등급 지정 포함), 참조원 추가·삭제

"참조원 추가·삭제"를 입수 액션으로 활용하면 장비 변경이나 편성 갱신을 트랜잭션 내에서 안전하게 수행함과 동시에 아이템의 보호 상태를 전환할 수 있습니다.

### 심플 인벤토리
- 검증 액션: 심플 아이템 소지 수량 검증
- 소비 액션: 심플 아이템 소비
- 입수 액션: 심플 아이템 입수·설정

### 거대 인벤토리
- 검증 액션: 거대 아이템 소지 수량 검증
- 소비 액션: 거대 아이템 소비
- 입수 액션: 거대 아이템 입수·설정

## 구현 예제

### 아이템 입수

아이템 입수는 게임 엔진용 SDK에서는 처리할 수 없습니다.

### 아이템 소비

이 API로 아이템 소비 처리를 수행하는 것은 권장하지 않습니다.

GS2-Exchange / GS2-Showcase / GS2-Quest와 같은 서비스를 통해 아이템 소비를 수행하는 대신, 어떠한 처리를 실행하는 것을 권장합니다.

#### 스탠다드



**Unity**
```csharp

    var result = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Inventory(
        inventoryName: "inventory-0001"
    ).ItemSet(
        itemName: "item-0001",
        itemSetName: null
    ).ConsumeAsync(
        consumeCount: 1L
    );
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Inventory(
        "inventory-0001" // inventoryName
    )->ItemSet(
        "item-0001", // itemName
        nullptr // itemSetName
    )->Consume(
        1L
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.inventory.namespace_(
        "namespace-0001"
    ).me(game_session).inventory(
        "inventory-0001"
    ).item_set(
        "item-0001",
        null
    )

var async_result = await domain.consume(
    1 # consume_count
)
if async_result.error != null:
    if async_result.error.type == "ConflictException":
        # 아이템 조작 처리가 충돌했습니다. 재시도가 필요합니다
        pass
    if async_result.error.type == "InsufficientException":
        # 아이템 소지 수량이 부족합니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 심플



**Unity**
```csharp

    var result = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SimpleInventory(
        inventoryName: "inventory-0001"
    ).SimpleItem(
        itemName: "item-0001"
    ).ConsumeAsync(
        consumeCount: 1L
    );
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->SimpleInventory(
        "inventory-0001" // inventoryName
    )->SimpleItem(
        "item-0001" // itemName
    )->Consume(
        1L
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.inventory.namespace_(
        "namespace-0001"
    ).me(game_session).simple_inventory(
        "inventory-0001"
    )

var async_result = await domain.consume_simple_items(
    [
        Gs2InventoryEzConsumeCount.new()
            .with_item_name("item-0001")
            .with_count(5),
        Gs2InventoryEzConsumeCount.new()
            .with_item_name("item-0002")
            .with_count(3),
    ] # consume_counts
)
if async_result.error != null:
    if async_result.error.type == "ConflictException":
        # 아이템 조작 처리가 충돌했습니다. 재시도가 필요합니다
        pass
    if async_result.error.type == "InsufficientException":
        # 아이템 소지 수량이 부족합니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 거대



**Unity**
```csharp

    var result = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).BigInventory(
        inventoryName: "inventory-0001"
    ).BigItem(
        itemName: "item-0001"
    ).ConsumeAsync(
        consumeCount: "1"
    );
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->BigInventory(
        "inventory-0001" // inventoryName
    )->BigItem(
        "item-0001" // itemName
    )->Consume(
        "1"
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
```
**Godot**
```gdscript

var domain = ez.inventory.namespace_(
        "namespace-0001"
    ).me(game_session).big_inventory(
        "inventory-0001"
    ).big_item(
        "item-0001"
    )

var async_result = await domain.consume_big_item(
    "1234567890123456789012345678901234567890" # consume_count
)
if async_result.error != null:
    if async_result.error.type == "ConflictException":
        # 아이템 조작 처리가 충돌했습니다. 재시도가 필요합니다
        pass
    if async_result.error.type == "InsufficientException":
        # 아이템 소지 수량이 부족합니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


### 인벤토리 정보 취득

#### 스탠다드



**Unity**
```csharp

    var item = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Inventory(
        inventoryName: "inventory-0001"
    ).ModelAsync();
```
**Unreal Engine**
```cpp

    const auto item = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Inventory(
        "inventory-0001" // inventoryName
    )->Model();
```
**Godot**
```gdscript

var domain = ez.inventory.namespace_(
        "namespace-0001"
    ).me(game_session).inventory(
        "inventory-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**
```csharp

    var items = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Inventory(
        inventoryName: "item"
    ).ItemSetsAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto It = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Inventory(
        "item" // inventoryName
    )->ItemSets(
    );
    TArray<Gs2::UE5::Inventory::Model::FEzItemSetPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```
**Godot**
```gdscript

var iterator = ez.inventory.namespace_(
        "namespace-0001"
    ).me(
        game_session
    ).inventory(
        "item"
    ).item_sets(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


#### 심플



**Unity**
```csharp

    var items = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SimpleInventory(
        inventoryName: "item"
    ).SimpleItemsAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto It = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->SimpleInventory(
        "item" // inventoryName
    )->SimpleItems(
    );
    TArray<Gs2::UE5::Inventory::Model::FEzSimpleItemPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```
**Godot**
```gdscript

var iterator = ez.inventory.namespace_(
        "namespace-0001"
    ).me(
        game_session
    ).simple_inventory(
        "item"
    ).simple_items(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


#### 거대



**Unity**
```csharp

    var items = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).BigInventory(
        inventoryName: "item"
    ).BigItemsAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto It = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->BigInventory(
        "item" // inventoryName
    )->BigItems(
    );
    TArray<Gs2::UE5::Inventory::Model::FEzBigItemPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```
**Godot**
```gdscript

var iterator = ez.inventory.namespace_(
        "namespace-0001"
    ).me(
        game_session
    ).big_inventory(
        "item"
    ).big_items(
    )

var async_result = await iterator.load()
if async_result.error != null:
    # 오류 처리
    push_error(str(async_result.error))
    return

var items = async_result.result

```


### 인벤토리 용량 확대

인벤토리 용량 확대는 게임 엔진용 SDK에서는 처리할 수 없습니다.

### 소지 증명 서명 취득

GS2 내 다른 마이크로서비스와 연동할 때, 실제로 GS2-Inventory에서 아이템을 소유하고 있음을 보증하는 데이터가 요구되는 경우가 있습니다.

예를 들어 GS2-Inventory에서 캐릭터의 소지 상태를 관리하고, GS2-Formation에서 파티 편성 상태를 관리한다고 가정합니다.
GS2-Formation에 파티 멤버를 설정할 때 "character-0001"이라는 캐릭터를 설정하도록 API 요청을 보내게 되는데,
GS2-Formation은 소지 증명 서명과 함께 "character-0001"을 지정하도록 요구합니다.

이를 통해 GS2-Formation은 뒤에서 GS2-Inventory와 통신하여 실제로 소지하고 있는 캐릭터인지 판단할 필요가 없어집니다.

#### 스탠다드



**Unity**
```csharp

    var result = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Inventory(
        inventoryName: "inventory-0001"
    ).ItemSet(
        itemName: "item-0001",
        itemSetName: "item-set-0001"
    ).GetItemWithSignatureAsync(
        keyId: "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
    );
    var item = await result.ModelAsync();
    var body = result.Body;
    var signature = result.Signature;
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Inventory(
        "inventory-0001" // inventoryName
    )->ItemSet(
        "item-0001", // itemName
        "item-set-0001" // itemSetName
    )->GetItemWithSignature(
        "key-0001"
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();
    const auto Body = Result->Body;
    const auto Signature = Result->Signature;
```
**Godot**
```gdscript

var domain = ez.inventory.namespace_(
        "namespace-0001"
    ).me(game_session).inventory(
        "inventory-0001"
    ).item_set(
        "item-0001",
        null
    )

var async_result = await domain.get_item_with_signature(
    "key-0001" # key_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 심플



**Unity**
```csharp

    var result = await gs2.Inventory.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).SimpleInventory(
        inventoryName: "inventory-0001"
    ).SimpleItem(
        itemName: "item-0001"
    ).GetSimpleItemWithSignatureAsync(
        keyId: "grn:gs2:{region}:{yourOwnerId}:key:namespace-0001:key:key-0001"
    );
    var item = await result.ModelAsync();
    var body = result.Body;
    var signature = result.Signature;
```
**Unreal Engine**
```cpp

    const auto Future = Gs2->Inventory->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->SimpleInventory(
        "inventory-0001" // inventoryName
    )->SimpleItem(
        "item-0001" // itemName
    )->GetSimpleItemWithSignature(
        "key-0001"
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();
    const auto Body = Result->Body;
    const auto Signature = Result->Signature;
```
**Godot**
```gdscript

var domain = ez.inventory.namespace_(
        "namespace-0001"
    ).me(game_session).simple_inventory(
        "inventory-0001"
    ).simple_item(
        "item-0001"
    )

var async_result = await domain.get_simple_item_with_signature(
    "key-0001" # key_id
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


#### 거대

거대 인벤토리에는 이 기능이 없습니다.

## 상세 레퍼런스

[GS2-Inventory API 레퍼런스](../../api_reference/inventory)



