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

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

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



## 모델

### EzMessage

메시지<br>

게임 플레이어마다 준비되는 메시지 박스에 전달된 메시지 데이터.<br>

메시지에는 개봉 상태가 있으며, 개봉 시 실행할 입수 액션을 설정할 수 있습니다.<br>
메시지에는 유효 기한을 설정할 수 있으며, 유효 기한이 지난 메시지는 미읽음 상태, 개봉 후 읽음 상태와 관계없이 자동으로 삭제됩니다.<br>
첨부된 보상을 받지 않은 경우에도 삭제됩니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| messageId | string |  | ※ |  |  ~ 1024자 | 메시지 GRN<br>※ 서버가 자동으로 설정 |
| name | string |  | ✓ | UUID |  ~ 36자 | 메시지 이름<br>메시지의 고유한 이름을 유지합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 메시지를 식별하는 데 사용됩니다. |
| metadata | string |  | ✓ |  |  ~ 4096자 | 메타데이터<br>메시지의 제목, 본문, 발신자 정보, 표시 파라미터 등을 포함하는 JSON 문자열 등, 메시지의 내용을 나타내는 임의의 데이터입니다. GS2는 이 값을 해석하지 않으며, 메시지 UI 렌더링을 위해 게임 클라이언트에 그대로 전달됩니다. 최대 4096자입니다. |
| isRead | bool |  |  | false |  | 읽음 상태<br>메시지가 사용자에 의해 개봉되었는지 여부를 나타냅니다. 메시지가 개봉되면 이 플래그가 true로 설정되고, readAcquireActions가 실행되어 첨부된 보상이 지급되며, readAt 타임스탬프가 기록됩니다. 네임스페이스에서 isAutomaticDeletingEnabled가 설정되어 있는 경우, 읽음 처리 후 메시지가 삭제됩니다. |
| readAcquireActions | [List&lt;EzAcquireAction&gt;](#ezacquireaction) |  |  | [] | 0 ~ 100 items | 개봉 시 입수 액션<br>사용자가 이 메시지를 개봉했을 때 실행되는 입수 액션 목록입니다. 아이템, 화폐, 리소스 등의 보상을 메시지에 첨부하기 위해 사용됩니다. 여러 액션을 조합하여 서로 다른 종류의 보상을 동시에 지급할 수 있습니다. 메시지당 최대 100개의 액션입니다. |
| receivedAt | long |  | ※ | 현재 시각 |  | 생성일시<br>UNIX 시간・밀리초<br>※ 서버가 자동으로 설정 |
| readAt | long |  |  | 0 |  | 개봉 일시<br>UNIX 시간·밀리초 |
| expiresAt | long |  |  |  |  | 유효기간 일시<br>UNIX 시간・밀리초 |

**관련 메서드:**
batchRead - 여러 메시지를 일괄 개봉하여 보상을 한꺼번에 받는다
delete - 선물 상자에서 메시지를 삭제한다
get - 특정 메시지의 상세 정보를 취득한다
list - 플레이어의 선물 상자에 있는 메시지 목록을 취득한다
read - 메시지를 개봉하여 보상을 받는다
receiveGlobalMessage - 전체 플레이어 대상 메시지를 수신한다


---

### EzConfig

컨피그 설정<br>

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

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| key | string |  | ✓ |  |  ~ 64자 | 이름 |
| value | string |  |  |  |  ~ 51200자 | 값 |


---

### EzAcquireAction

획득 액션<br>

보상으로 메시지에 첨부되는 단일 획득 액션을 나타냅니다. 액션 타입(예: 인벤토리에 아이템 추가, 통화 증가)과 그 요청 파라미터로 구성됩니다. 메시지가 개봉되면 이 액션들이 트랜잭션으로 조립되어 실행되고, 사용자에게 보상이 지급됩니다.

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


**관련 모델:**
EzMessage - 메시지



---

### 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 | 획득 액션 실행 결과 리스트 |

**관련 메서드:**
batchRead - 여러 메시지를 일괄 개봉하여 보상을 한꺼번에 받는다
read - 메시지를 개봉하여 보상을 받는다


---

## 메서드

### batchRead

여러 메시지를 일괄 개봉하여 보상을 한꺼번에 받는다<br>

최대 10건의 메시지를 한 번에 개봉하고, 첨부된 모든 보상을 한 번의 작업으로 지급합니다.<br>
지정한 메시지 중 하나라도 이미 개봉된 경우, 오류가 반환되며 어떤 메시지도 처리되지 않습니다. 이를 통해 보상의 이중 수령이 확실히 방지됩니다.<br>
선물 상자 화면의 "일괄 개봉", "모두 받기" 버튼에 사용합니다. 플레이어가 버튼 하나로 대기 중인 보상을 한꺼번에 받을 수 있습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| messageNames | List&lt;string&gt; |  | ✓|  | 1 ~ 10 items | 메시지 이름 목록 |
| gameSession | GameSession | | ✓|  |  | GameSession |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzMessage&gt;](#ezmessage) | 메시지 목록|
| transactionId | string | 발행된 트랜잭션 ID|
| stampSheet | string | 스탬프 시트|
| stampSheetEncryptionKeyId | string | 스탬프 시트의 서명 계산에 사용한 암호화 키 GRN|
| autoRunStampSheet | bool | 트랜잭션 자동 실행이 활성화되어 있는지 여부|
| atomicCommit | bool | 트랜잭션을 원자적으로 커밋할지 여부|
| transaction | string | 발행된 트랜잭션|
| transactionResult | [EzTransactionResult](#eztransactionresult) | 트랜잭션 실행 결과|

#### Error

이 API에는 특별한 예외가 정의되어 있습니다.<br>
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.<br>
일반적인 에러의 종류와 핸들링 방법은 [여기]() 문서를 참고해 주세요.

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| MessageExpiredException | NotFoundException | 메시지의 유효기간이 만료되었습니다 |

#### 구현 예제




**Unity (UniTask)**
```csharp

try {
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.BatchReadAsync(
        messageNames: new List<string> {
            "message-0001",
            "message-0002",
        }
    );
} catch(Gs2.Gs2Inbox.Exception.MessageExpiredException e) {
    // Message has expired
}
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.BatchReadFuture(
        messageNames: new List<string> {
            "message-0001",
            "message-0002",
        }
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Inbox.Exception.MessageExpiredException)
        {
            // Message has expired
        }
        onError.Invoke(future.Error, null);
        yield break;
    }
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Inbox->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto Future = Domain->BatchRead(
        []
        {
            auto v = TOptional<TArray<FString>>();
            v->Add("message-0001");
            v->Add("message-0002");
            return v;
        }() // messageNames
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Inbox::Error::FMessageExpiredError::Class))
        {
            // Message has expired
        }
        return false;
    }

```

**Godot**
```gdscript

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

var async_result = await domain.batch_read(
    [
        "message-0001",
        "message-0002",
    ] # message_names
)
if async_result.error != null:
    if async_result.error is Gs2InboxMessageExpiredException:
        # 메시지의 유효기간이 만료되었습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### delete

선물 상자에서 메시지를 삭제한다<br>

개봉 여부와 관계없이 플레이어의 선물 상자에서 메시지를 완전히 삭제합니다.<br>
선물 상자 설정에서 메시지 개봉 시 자동 삭제하도록 설정한 경우에는 수동으로 호출할 필요가 없습니다.<br>
다만 자동 삭제가 비활성화된 경우(개봉된 메시지가 선물 상자에 계속 남아 있는 설정)에는 이 API로 플레이어가 받은 편지함을 정리할 수 있도록 합니다.<br>
각 메시지의 "삭제", "닫기" 버튼에 사용합니다. 예를 들어 오래된 개봉 메시지를 불필요한 것으로 삭제하는 데 유용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| messageName | string |  | ✓| UUID |  ~ 36자 | 메시지 이름<br>메시지의 고유한 이름을 유지합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 메시지를 식별하는 데 사용됩니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzMessage](#ezmessage) | 삭제된 메시지|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Message(
        messageName: "message-0001"
    );
    var result = await domain.DeleteAsync(
    );

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Message(
        messageName: "message-0001"
    );
    var future = domain.DeleteFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Inbox->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Message(
        "message-0001" // messageName
    );
    const auto Future = Domain->Delete(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }
    const auto Result = Future->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.inbox.namespace_(
        "namespace-0001"
    ).me(game_session).message(
        "message-0001"
    )

var async_result = await domain.delete(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### get

특정 메시지의 상세 정보를 취득한다<br>

플레이어의 선물 상자에 있는 특정 메시지의 전체 상세 정보를 취득합니다. 메타데이터(제목, 본문 등), 읽음 상태, 첨부된 보상, 유효 기한이 포함됩니다.<br>
메시지 상세 화면을 표시하는 데 사용합니다. 예를 들어 "점검 보상 — 불편을 드려 죄송합니다! 사과의 의미로 잼 100개를 보내드립니다. [개봉하기]"와 같은 화면입니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| messageName | string |  | ✓| UUID |  ~ 36자 | 메시지 이름<br>메시지의 고유한 이름을 유지합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 메시지를 식별하는 데 사용됩니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzMessage](#ezmessage) | 메시지|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Message(
        messageName: "message-0001"
    );
    var item = await domain.ModelAsync();

```

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

```

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

```

**Godot**
```gdscript

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

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

```

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

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

```

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

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

```

**Godot**
```gdscript

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

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

---

### list

플레이어의 선물 상자에 있는 메시지 목록을 취득한다<br>

플레이어의 선물 상자(기프트 박스 / 받은 편지함)에 있는 모든 메시지를 최신순으로 취득합니다.<br>
각 메시지에는 플레이어가 개봉하여 받을 수 있는 보상(아이템, 화폐 등)을 포함할 수 있습니다.<br>
읽음 상태로 필터링할 수도 있습니다. 예를 들어 미개봉 메시지만, 또는 개봉한 메시지만 표시할 수 있습니다.<br>
"선물 상자", "기프트" 화면을 구성하는 데 사용합니다. 예를 들어 "점검 보상(잼 x100)", "데일리 로그인 보너스(골드 x500)"와 같은 목록에 "개봉" 버튼을 붙여 표시하는 화면에 유용합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| isRead | bool |  | |  |  | 읽음 상태 |
| pageToken | string |  | |  |  ~ 1024자 | 데이터 취득을 시작할 위치를 지정하는 토큰 |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzMessage&gt;](#ezmessage) | 메시지 목록|
| nextPageToken | string | 목록의 나머지를 취득하기 위한 페이지 토큰|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var items = await domain.MessagesAsync(
    ).ToListAsync();

```

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

```


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




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

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

```

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

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

```

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

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

```


**⚠️ Warning**

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

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

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

---

### read

메시지를 개봉하여 보상을 받는다<br>

지정한 메시지를 개봉하고, 첨부된 보상(아이템, 화폐 등)을 플레이어에게 지급합니다.<br>
메시지는 읽음 상태가 되며, 보상 지급이 한 번의 작업으로 이루어집니다. 이미 개봉된 경우에는 보상의 이중 수령을 방지하기 위해 오류가 반환됩니다.<br>
보상이 첨부되지 않은 메시지의 경우에는 단순히 읽음으로 표시됩니다.<br>
선물 상자의 각 메시지에 있는 "개봉", "받기" 버튼에 사용합니다. 예를 들어 플레이어가 "개봉"을 탭하면 "잼 x100"을 받는 것과 같은 동작입니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| messageName | string |  | ✓| UUID |  ~ 36자 | 메시지 이름<br>메시지의 고유한 이름을 유지합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 메시지를 식별하는 데 사용됩니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |

#### Result

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

#### Error

이 API에는 특별한 예외가 정의되어 있습니다.<br>
GS2-SDK for Game Engine에서는 게임 내에서 핸들링이 필요할 것으로 예상되는 에러를, 일반적인 예외에서 파생된 특수화된 예외로 제공하여 다루기 쉽게 하고 있습니다.<br>
일반적인 에러의 종류와 핸들링 방법은 [여기]() 문서를 참고해 주세요.

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| MessageExpiredException | NotFoundException | 메시지의 유효기간이 만료되었습니다 |

#### 구현 예제




**Unity (UniTask)**
```csharp

try {
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Message(
        messageName: "message-0001"
    );
    var result = await domain.ReadAsync(
    );
} catch(Gs2.Gs2Inbox.Exception.MessageExpiredException e) {
    // Message has expired
}
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Message(
        messageName: "message-0001"
    );
    var future = domain.ReadFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Inbox.Exception.MessageExpiredException)
        {
            // Message has expired
        }
        onError.Invoke(future.Error, null);
        yield break;
    }
    // New Experience에서는 스탬프 시트가 SDK 레벨에서 자동으로 실행됩니다.
    // 에러가 발생하면 TransactionException이 발생합니다.
    // TransactionException::Retry()로 재시도할 수 있습니다.

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Inbox->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Message(
        "message-0001" // messageName
    );
    const auto Future = Domain->Read(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Inbox::Error::FMessageExpiredError::Class))
        {
            // Message has expired
        }
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.inbox.namespace_(
        "namespace-0001"
    ).me(game_session).message(
        "message-0001"
    )

var async_result = await domain.read(
)
if async_result.error != null:
    if async_result.error is Gs2InboxMessageExpiredException:
        # 메시지의 유효기간이 만료되었습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### receiveGlobalMessage

전체 플레이어 대상 메시지를 수신한다<br>

글로벌 메시지(점검 보상이나 이벤트 보상 등, 전체 플레이어에게 일괄 전송된 메시지)를 확인하여, 아직 받지 않은 것을 플레이어의 선물 상자에 전달합니다.<br>
각 글로벌 메시지는 플레이어의 받은 편지함에 개별 메시지로 변환됩니다. 이미 수신한 메시지는 건너뛰므로 여러 번 호출해도 안전합니다.<br>
플레이어가 선물 상자 화면을 열 때나 로그인 시에 호출하여 모든 글로벌 메시지를 확실히 받을 수 있도록 해 주세요.<br>
선물 상자 초기화의 일부로 사용합니다. 메시지 목록을 취득하기 전에 호출하면 플레이어가 모든 기프트를 확인할 수 있게 됩니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [List&lt;EzMessage&gt;](#ezmessage) | 수신한 메시지 목록|

#### 구현 예제




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

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Inbox.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.ReceiveGlobalMessageFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }
    var future2 = future.Result.ModelFuture();
    yield return future2;
    if (future2.Error != null)
    {
        onError.Invoke(future2.Error, null);
        yield break;
    }
    var result = future2.Result;

```

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

    // 변경된 값 / 결과 값을 취득
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError())
    {
        return Future2->GetTask().Error();
    }
    const auto Result = Future2->GetTask().Result();

```

**Godot**
```gdscript

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

var async_result = await domain.receive_global_message(
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

## 이벤트 핸들러

### OnReceiveNotification

메시지를 수신했을 때 사용하는 푸시 알림

 | 이름 | 타입 | 설명 |
| --- | --- | --- |
| namespaceName | string |네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.|
| userId | string |사용자ID|
| messageName | string |메시지 이름<br>메시지의 고유한 이름을 유지합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 메시지를 식별하는 데 사용됩니다.|

#### 구현 예제





**Unity (UniTask)**
```csharp

    gs2.Inbox.OnReceiveNotification += notification =>
    {
        var namespaceName = notification.NamespaceName;
        var userId = notification.UserId;
        var messageName = notification.MessageName;
    };
```

**Unity (Vanilla)**
```cs

    gs2.Inbox.OnReceiveNotification += notification =>
    {
        var namespaceName = notification.NamespaceName;
        var userId = notification.UserId;
        var messageName = notification.MessageName;
    };
```

**Unreal Engine 5**
```cpp

    Gs2->Inbox->OnReceiveNotification().AddLambda([](const auto Notification)
    {
        const auto NamespaceName = Notification->NamespaceNameValue;
        const auto UserId = Notification->UserIdValue;
        const auto MessageName = Notification->MessageNameValue;
    });
```

**Godot**
```gdscript

    ez.inbox.receive_notification.connect(func(notification):
        var namespace_name = notification.namespace_name
        var user_id = notification.user_id
        var message_name = notification.message_name
    )
```


---



