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

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

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



## 모델

### EzShowcase

진열대<br>

`진열대`에는 진열할 상품을 정의할 수 있습니다.<br>
또한 `진열대`의 상품 판매 기간을 설정할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ |  |  ~ 128자 | 진열대 이름<br>진열대 고유의 이름. 영숫자 및 -(하이픈), _(언더스코어), .(마침표)로 지정합니다. |
| metadata | string |  |  |  |  ~ 2048자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| displayItems | [List&lt;EzDisplayItem&gt;](#ezdisplayitem) |  |  | [] | 1 ~ 1000 items | 진열할 상품 리스트<br>이 진열대에 진열되는 상품의 리스트입니다. 각 진열 상품은 단일 상품 또는 상품 그룹 중 하나입니다. 판매 기간 이벤트가 종료되었거나 유효하지 않은 상품은 진열대 조회 시 자동으로 필터링됩니다. |
| salesPeriodEventId | string |  |  |  |  ~ 1024자 | 진열대의 판매 기간을 설정한 GS2-Schedule 이벤트 GRN<br>이 진열대 전체의 판매 기간을 제어합니다. 지정한 경우, 연관된 GS2-Schedule 이벤트 기간 중에만 진열대를 이용할 수 있습니다. 이벤트가 유효하지 않은 경우, 진열대는 비어 있는 상태로 반환됩니다. |

**관련 메서드:**
getShowcase - 상점 쇼케이스와 상품 가져오기


---

### EzDisplayItem

진열 상품<br>

진열대에 표시되는 상품입니다. 단일 상품 또는 상품 그룹 중 하나를 참조할 수 있습니다. 각 진열 상품에는 진열대 전체의 판매 기간과는 독립적으로 GS2-Schedule 이벤트에 의한 개별 판매 기간을 설정할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| displayItemId | string |  | ✓ | UUID |  ~ 128자 | 진열 상품 ID<br>진열 상품의 고유한 이름을 유지합니다.<br>생략하면 시스템에 의해 UUID(Universally Unique Identifier) 형식으로 자동 할당됩니다. |
| type | 문자열 열거형<br>enum {<br>"salesItem",<br>"salesItemGroup"<br>}<br> |  | ✓ |  |  | 종류<br>표시할 상품의 종류입니다. "salesItem"은 고정된 대가와 보상을 가진 단일 상품입니다. "salesItemGroup"은 여러 상품을 순서대로 평가하는 상품 그룹으로, 단계별 가격 인상이나 초회 한정 할인 등에 사용됩니다.salesItem: 상품 / salesItemGroup: 상품 그룹 /  |
| salesItem | [EzSalesItem](#ezsalesitem) | {type} == "salesItem" | ✓※ |  |  | 상품<br><br>※ type이(가) "salesItem" 이면 필수 |
| salesItemGroup | [EzSalesItemGroup](#ezsalesitemgroup) | {type} == "salesItemGroup" | ✓※ |  |  | 상품 그룹<br><br>※ type이(가) "salesItemGroup" 이면 필수 |


**관련 모델:**
EzShowcase - 진열대



---

### EzRandomDisplayItem

랜덤 진열대에 진열된 상품<br>

특정 사용자에게 랜덤 진열대에서 추첨·표시된 상품을 나타냅니다. 현재 로테이션 기간에서의 상품 대가, 보상 및 구매 횟수 추적 정보를 포함합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ | UUID |  ~ 128자 | 랜덤 진열 상품명<br>랜덤 진열 상품의 고유한 이름을 유지합니다.<br>생략하면 시스템에 의해 UUID(Universally Unique Identifier) 형식으로 자동 할당됩니다. |
| metadata | string |  |  |  |  ~ 2048자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| verifyActions | [List&lt;EzVerifyAction&gt;](#ezverifyaction) |  |  | [] | 0 ~ 10 items | 검증 액션 목록 |
| consumeActions | [List&lt;EzConsumeAction&gt;](#ezconsumeaction) |  |  | [] | 0 ~ 10 items | 소비 액션 목록 |
| acquireActions | [List&lt;EzAcquireAction&gt;](#ezacquireaction) |  |  | [] | 1 ~ 100 items | 입수 액션 목록 |
| currentPurchaseCount | int |  | ✓ |  | 1 ~ 2147483646 | 현재 구매 횟수<br>현재 로테이션 기간 중에 이 상품이 구매된 횟수입니다. 구매할 때마다 증가하며, 로테이션 기간이 종료되면 리셋됩니다. |
| maximumPurchaseCount | int |  | ✓ |  | 1 ~ 2147483646 | 최대 구매 횟수<br>현재 로테이션 기간 중에 이 상품을 구매할 수 있는 최대 횟수입니다. currentPurchaseCount가 이 값에 도달하면 다음 로테이션까지 구매할 수 없게 됩니다. |

**관련 메서드:**
getRandomShowcaseDisplayItem - 랜덤 진열대의 특정 상품 조회
listRandomShowcaseDisplayItems - 랜덤 쇼케이스에 현재 표시되고 있는 상품 목록 가져오기
randomShowcaseBuy - 랜덤 진열대의 상품 구매


---

### EzSalesItem

상품<br>

상품을 구매하기 위해 필요한 대가와, 상품을 구매했을 때 얻을 수 있는 보상을 설정합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ |  |  ~ 128자 | 상품 이름<br>상품 고유의 이름. 영숫자 및 -(하이픈), _(언더스코어), .(마침표)로 지정합니다. |
| metadata | string |  |  |  |  ~ 2048자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| verifyActions | [List&lt;EzVerifyAction&gt;](#ezverifyaction) |  |  | [] | 0 ~ 10 items | 검증 액션 리스트<br>구매 전에 실행되는 전제 조건 체크입니다. 모든 검증 액션이 성공한 후에 소비·획득 액션이 처리됩니다. 구매 자격 조건 체크에 사용할 수 있습니다. |
| consumeActions | [List&lt;EzConsumeAction&gt;](#ezconsumeaction) |  |  | [] | 0 ~ 10 items | 소비 액션 리스트<br>구매 대가로 리소스를 소비하는 액션입니다. 상품 그룹의 구매 횟수 제어를 위해 GS2-Limit의 CountUp 액션을 포함할 수 있습니다. |
| acquireActions | [List&lt;EzAcquireAction&gt;](#ezacquireaction) |  |  | [] | 1 ~ 100 items | 획득 액션 리스트<br>구매 보상으로 리소스를 부여하는 액션입니다. 모든 소비 액션이 정상적으로 완료된 후에 실행됩니다. |

**관련 메서드:**
buy - 쇼케이스의 상품 구매하기


**관련 모델:**
EzDisplayItem - 진열 상품
EzSalesItemGroup - 상품 그룹



---

### EzSalesItemGroup

상품 그룹<br>

상품 그룹은 진열대에 진열하기 위한 엔티티입니다.<br>
상품 그룹에는 여러 상품을 소속시킬 수 있으며, 소속된 상품의 앞에서부터 순서대로 구매 가능한지를 판정하여 가장 먼저 구매 가능하다고 판정된 상품이 실제로 진열됩니다.<br>
최초 1회만 할인되는 상품이나, 스텝업 가챠처럼 구매 횟수에 따라 상품 내용이 변화하는 구조에 사용할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ |  |  ~ 128자 | 상품 그룹 이름<br>상품 그룹 고유의 이름. 영숫자 및 -(하이픈), _(언더스코어), .(마침표)로 지정합니다. |
| metadata | string |  |  |  |  ~ 2048자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| salesItems | [List&lt;EzSalesItem&gt;](#ezsalesitem) |  |  | [] | 2 ~ 10 items | 상품 그룹에 포함할 상품<br>이 그룹 내 상품의 순서가 지정된 리스트입니다. GS2-Limit 카운터를 사용하여 앞에서부터 순서대로 구매 가능한지를 판정하고, 가장 먼저 구매 가능하다고 판정된 상품이 표시됩니다. 어느 것도 해당하지 않는 경우, 리스트의 마지막 상품이 폴백으로 사용됩니다. |


**관련 모델:**
EzDisplayItem - 진열 상품



---

### EzConfig

컨피그 설정<br>

트랜잭션의 변수에 적용하는 설정 값

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| key | string |  | ✓ |  |  ~ 64자 | 이름<br>트랜잭션 템플릿에서 치환되는 변수명입니다. 획득 액션 파라미터 내의 플레이스홀더에 대응합니다. |
| value | string |  |  |  |  ~ 51200자 | 값<br>트랜잭션 템플릿 내의 대응하는 변수명에 치환되는 값입니다. |

**관련 메서드:**
buy - 쇼케이스의 상품 구매하기
randomShowcaseBuy - 랜덤 진열대의 상품 구매


---

### EzConsumeAction

소비 액션

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 소비 액션에서 실행할 액션의 종류 |
| request | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |


**관련 모델:**
EzRandomDisplayItem - 랜덤 진열대에 진열된 상품
EzSalesItem - 상품



---

### EzVerifyAction

검증 액션

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 검증 액션에서 실행할 액션의 종류 |
| request | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |


**관련 모델:**
EzRandomDisplayItem - 랜덤 진열대에 진열된 상품
EzSalesItem - 상품



---

### EzAcquireAction

입수 액션

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 입수 액션에서 실행할 액션의 종류 |
| request | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |


**관련 모델:**
EzRandomDisplayItem - 랜덤 진열대에 진열된 상품
EzSalesItem - 상품



---

### EzVerifyActionResult

검증 액션 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 검증 액션에서 실행할 액션의 종류 |
| verifyRequest | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |
| statusCode | int |  |  |  | 0 ~ 999 | 상태 코드 |
| verifyResult | string |  |  |  |  ~ 1048576자 | 결과 내용 |


**관련 모델:**
EzTransactionResult - 트랜잭션 실행 결과



---

### EzConsumeActionResult

소비 액션 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 소비 액션에서 실행할 액션의 종류 |
| consumeRequest | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |
| statusCode | int |  |  |  | 0 ~ 999 | 상태 코드 |
| consumeResult | string |  |  |  |  ~ 1048576자 | 결과 내용 |


**관련 모델:**
EzTransactionResult - 트랜잭션 실행 결과



---

### EzAcquireActionResult

획득 액션 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| action | 문자열 열거형<br>enum {<br>}<br> |  | ✓ |  |  | 입수 액션에서 실행할 액션의 종류 |
| acquireRequest | string |  | ✓ |  |  ~ 524288자 | 액션 실행 시 사용되는 요청의 JSON 문자열 |
| statusCode | int |  |  |  | 0 ~ 999 | 상태 코드 |
| acquireResult | string |  |  |  |  ~ 1048576자 | 결과 내용 |


**관련 모델:**
EzTransactionResult - 트랜잭션 실행 결과



---

### EzTransactionResult

트랜잭션 실행 결과<br>

서버 사이드에서 트랜잭션 자동 실행 기능을 이용하여 실행된 트랜잭션의 실행 결과

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| transactionId | string |  | ✓ |  | 36 ~ 36자 | 트랜잭션 ID |
| verifyResults | [List&lt;EzVerifyActionResult&gt;](#ezverifyactionresult) |  |  |  | 0 ~ 10 items | 검증 액션의 실행 결과 목록 |
| consumeResults | [List&lt;EzConsumeActionResult&gt;](#ezconsumeactionresult) |  |  | [] | 0 ~ 10 items | 소비 액션의 실행 결과 목록 |
| acquireResults | [List&lt;EzAcquireActionResult&gt;](#ezacquireactionresult) |  |  | [] | 0 ~ 100 items | 획득 액션 실행 결과 리스트 |

**관련 메서드:**
buy - 쇼케이스의 상품 구매하기
randomShowcaseBuy - 랜덤 진열대의 상품 구매


---

## 메서드

### buy

쇼케이스의 상품 구매하기<br>

쇼케이스에서 특정 상품을 구매합니다.<br>
플레이어가 상품을 구매하면 시스템이 구매 흐름 전체를 자동으로 처리합니다. 조건 확인(예: 플레이어 레벨), 비용 소비(예: 젬 100개), 보상 부여(예: 레어 검)가 모두 자동으로 이루어집니다.<br>
이 모든 처리는 하나의 트랜잭션에서 이루어지므로, 결제만 이루어지고 아이템이 지급되지 않는 상황은 발생하지 않습니다.<br>
플레이어가 상점 상품의 "구매" 버튼을 탭했을 때 사용합니다. 예를 들어 "스타터 팩"을 젬 500개로 구매하여 스태미나 포션 10개와 골드 상자 1개를 받는 처리에 유용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| showcaseName | string |  | ✓|  |  ~ 128자 | 진열대 이름<br>진열대 고유의 이름. 영숫자 및 -(하이픈), _(언더스코어), .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| displayItemId | string |  | ✓| UUID |  ~ 128자 | 진열 상품 ID<br>진열 상품의 고유한 이름을 유지합니다.<br>생략하면 시스템에 의해 UUID(Universally Unique Identifier) 형식으로 자동 할당됩니다. |
| quantity | int |  | | 1 | 1 ~ 1000 | 구매 수량 |
| config | [List&lt;EzConfig&gt;](#ezconfig) |  | | [] | 0 ~ 32 items | 트랜잭션 변수에 적용할 설정값 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzSalesItem](#ezsalesitem) | 상품|
| transactionId | string | 발행된 트랜잭션 ID|
| stampSheet | string | 구매 처리를 실행하는 데 사용하는 스탬프 시트|
| stampSheetEncryptionKeyId | string | 스탬프 시트의 서명 계산에 사용한 암호화 키 GRN|
| autoRunStampSheet | bool | 트랜잭션 자동 실행이 활성화되어 있는지 여부|
| atomicCommit | bool | 트랜잭션을 원자적으로 커밋할지 여부|
| transaction | string | 발행된 트랜잭션|
| transactionResult | [EzTransactionResult](#eztransactionresult) | 트랜잭션 실행 결과|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    ).DisplayItem(
        displayItemId: "display-item-0001"
    );
    var result = await domain.BuyAsync(
        quantity: null,
        config: null
    );
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    ).DisplayItem(
        displayItemId: "display-item-0001"
    );
    var future = domain.BuyFuture(
        quantity: null,
        config: null
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Showcase(
        "showcase-0001" // showcaseName
    )->DisplayItem(
        "display-item-0001" // displayItemId
    );
    const auto Future = Domain->Buy(
        // quantity
        // config
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.showcase.namespace_(
        "namespace-0001"
    ).me(game_session).showcase(
        "showcase-0001"
    ).display_item(
        "display-item-0001"
    )

var async_result = await domain.buy(
    null, # quantity
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### getShowcase

상점 쇼케이스와 상품 가져오기<br>

특정 쇼케이스(상점 페이지)와 해당 쇼케이스에 표시된 상품 목록을 가져옵니다.<br>
쇼케이스는 게임 내 상점 화면과 같은 것입니다. 예를 들어 "젬 상점", "데일리 세일", "스페셜 오퍼" 같은 페이지에 해당합니다.<br>
각 쇼케이스에는 상품이 진열되어 있으며, 각 상품에는 플레이어가 지불하는 것(비용)과 받는 것(보상)이 정의되어 있습니다.<br>
쇼케이스는 스케줄 이벤트와 연결하여 특정 기간에만 표시되게 할 수도 있습니다. 예를 들어 크리스마스 기간에만 표시되는 "홀리데이 상점" 같은 방식으로 사용할 수 있습니다.<br>
상점 UI를 구축할 때 사용합니다. 예를 들어 플레이어가 상점 화면을 열었을 때 상품 목록을 가격과 "구매" 버튼과 함께 표시하는 데 유용합니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzShowcase](#ezshowcase) | 진열대|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Showcase(
        "showcase-0001" // showcaseName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.showcase.namespace_(
        "namespace-0001"
    ).me(game_session).showcase(
        "showcase-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.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Showcase(
        showcaseName: "showcase-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

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

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

```

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

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

```

**Godot**
```gdscript

var domain = ez.showcase.namespace_(
        "namespace-0001"
    ).me(game_session).showcase(
        "showcase-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의 실행에 의해 변화한 것만이 대상이 됩니다.

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

---

### getRandomShowcaseDisplayItem

랜덤 진열대의 특정 상품 조회<br>

랜덤 진열대에 현재 표시되고 있는 특정 상품의 상세 정보를 조회합니다.<br>
플레이어가 랜덤 상점의 특정 상품을 탭했을 때 상세 화면을 표시하는 데 사용합니다. 예를 들어, 상세 팝업에서 아이템의 설명, 가격, "구매" 버튼을 표시하는 데 유용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| showcaseName | string |  | ✓|  |  ~ 128자 | 랜덤 진열대 이름<br>랜덤 진열대 고유의 이름. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| displayItemName | string |  | ✓| UUID |  ~ 128자 | 랜덤 진열 상품명<br>랜덤 진열 상품의 고유한 이름을 유지합니다.<br>생략하면 시스템에 의해 UUID(Universally Unique Identifier) 형식으로 자동 할당됩니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzRandomDisplayItem](#ezrandomdisplayitem) | 상품|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RandomShowcase(
        "showcase-0001" // showcaseName
    )->RandomDisplayItem(
        "display-item-0001" // displayItemName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.showcase.namespace_(
        "namespace-0001"
    ).me(game_session).random_showcase(
        "showcase-0001"
    ).random_display_item(
        "display-item-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.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RandomShowcase(
        "showcase-0001" // showcaseName
    )->RandomDisplayItem(
        "display-item-0001" // displayItemName
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Showcase::Model::FRandomDisplayItem> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

**Godot**
```gdscript

var domain = ez.showcase.namespace_(
        "namespace-0001"
    ).me(game_session).random_showcase(
        "showcase-0001"
    ).random_display_item(
        "display-item-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의 실행에 의해 변화한 것만이 대상이 됩니다.

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

---

### listRandomShowcaseDisplayItems

랜덤 쇼케이스에 현재 표시되고 있는 상품 목록 가져오기<br>

플레이어를 대상으로 랜덤 쇼케이스에 현재 표시되고 있는 상품을 가져옵니다.<br>
랜덤 쇼케이스는 상품 풀에서 무작위로 선택된 상품이 표시되는 상점입니다. "데일리 세일"이나 "미스터리 상점"처럼 플레이어마다 다른 상품이 표시되거나 주기적으로 라인업이 갱신됩니다.<br>
예를 들어 50종류의 상품 풀에서 매일 5개가 무작위로 선택되어 자정에 리프레시되는 방식입니다.<br>
표시되는 상품은 플레이어마다 다르며, 같은 시점이라도 다른 플레이어에게는 다른 상품이 표시될 수 있습니다.<br>
랜덤 상점 UI 구축에 사용합니다. 예를 들어 "오늘의 세일"로 5개의 랜덤 상품과 다음 리프레시까지의 카운트다운 타이머를 표시하는 데 유용합니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzRandomDisplayItem&gt;](#ezrandomdisplayitem) | 랜덤 쇼케이스의 상품 목록|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    );
    var items = await domain.RandomDisplayItemsAsync(
    ).ToListAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    );
    var it = domain.RandomDisplayItems(
    );
    List<EzRandomDisplayItem> items = new List<EzRandomDisplayItem>();
    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->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RandomShowcase(
        "showcase-0001" // showcaseName
    );
    const auto It = Domain->RandomDisplayItems(
    );
    TArray<Gs2::UE5::Showcase::Model::FEzRandomDisplayItemPtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


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




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

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

```

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

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

```

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

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

```


**⚠️ Warning**

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

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

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

---

### randomShowcaseBuy

랜덤 진열대의 상품 구매<br>

랜덤 진열대에서 특정 상품을 구매합니다.<br>
일반 진열대에서의 구매와 마찬가지로 조건 확인, 비용 소비, 보상 지급이 모두 하나의 트랜잭션에서 자동으로 처리됩니다.<br>
랜덤 진열대에서 상품을 구매하면 설정에 따라 해당 상품이 품절 상태가 되어, 다음 새로고침까지 새로운 상품이 표시되지 않을 수 있습니다.<br>
플레이어가 랜덤 상점의 상품에서 "구매" 버튼을 탭했을 때 사용합니다. 예를 들어, 데일리 세일에서 새로고침 전에 할인된 "레전더리 무기 상자"를 구매하는 처리에 유용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| showcaseName | string |  | ✓|  |  ~ 128자 | 랜덤 진열대 이름<br>랜덤 진열대 고유의 이름. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| displayItemName | string |  | ✓| UUID |  ~ 128자 | 랜덤 진열 상품명<br>랜덤 진열 상품의 고유한 이름을 유지합니다.<br>생략하면 시스템에 의해 UUID(Universally Unique Identifier) 형식으로 자동 할당됩니다. |
| quantity | int |  | | 1 | 1 ~ 1000 | 구매 수량 |
| config | [List&lt;EzConfig&gt;](#ezconfig) |  | | [] | 0 ~ 32 items | 트랜잭션 변수에 적용할 설정값 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzRandomDisplayItem](#ezrandomdisplayitem) | 구매한 상품|
| transactionId | string | 발행된 트랜잭션 ID|
| stampSheet | string | 구매 처리를 실행하는 데 사용하는 스탬프 시트|
| stampSheetEncryptionKeyId | string | 스탬프 시트의 서명 계산에 사용한 암호화 키 GRN|
| autoRunStampSheet | bool | 트랜잭션 자동 실행이 활성화되어 있는지 여부|
| atomicCommit | bool | 트랜잭션을 원자적으로 커밋할지 여부|
| transaction | string | 발행된 트랜잭션|
| transactionResult | [EzTransactionResult](#eztransactionresult) | 트랜잭션 실행 결과|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    );
    var result = await domain.RandomShowcaseBuyAsync(
        quantity: 1,
        config: null
    );
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Showcase.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).RandomShowcase(
        showcaseName: "showcase-0001"
    ).RandomDisplayItem(
        displayItemName: "display-item-0001"
    );
    var future = domain.RandomShowcaseBuyFuture(
        quantity: 1,
        config: null
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Showcase->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->RandomShowcase(
        "showcase-0001" // showcaseName
    )->RandomDisplayItem(
        "display-item-0001" // displayItemName
    );
    const auto Future = Domain->RandomShowcaseBuy(
        1 // quantity
        // config
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.showcase.namespace_(
        "namespace-0001"
    ).me(game_session).random_showcase(
        "showcase-0001"
    ).random_display_item(
        "display-item-0001"
    )

var async_result = await domain.random_showcase_buy(
    1, # quantity
    null # config
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---



