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

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

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



## 모델

### EzRoom

룸<br>

룸은 채팅 메시지를 전달할 수 있는 범위를 나타냅니다.<br>
GS2-Chat의 룸에는 참가라는 개념이 없습니다.<br>
따라서 메시지를 수신하려면 룸의 이름만 알고 있으면 되며, 룸에 참가하거나 멤버로 등록할 필요가 없습니다.<br>

룸의 메시지를 열람할 수 있는 게임 플레이어를 제한하고 싶은 경우 두 가지 방법이 있습니다.<br>
첫 번째는 룸에 비밀번호를 설정하는 것입니다.<br>
두 번째는 룸에 설정 가능한 화이트리스트에 게임 플레이어의 사용자 ID를 설정하여 제한하는 것입니다.<br>

비밀번호를 설정한 경우, 비밀번호를 모르면 게임 관리자라도 메시지를 가져올 수 없게 된다는 점에 주의하십시오.<br>
이는 일본국 헌법에서 정한 `통신의 비밀`에 해당할 가능성이 있기 때문입니다.<br>

룸을 구독하면, 룸에 새로운 메시지가 전송되었을 때 GS2-Gateway의 푸시 알림을 받을 수 있습니다.<br>
이 알림 기능을 이용함으로써, 룸에 대해 폴링하지 않고도 새로운 메시지의 유무를 알 수 있게 됩니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ | UUID |  ~ 128자 | 룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| metadata | string |  |  |  |  ~ 1024자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| whiteListUserIds | List&lt;string&gt; |  |  | [] | 0 ~ 1000 items | 룸에 액세스 가능한 사용자 ID 리스트<br>설정하면, 리스트에 포함된 사용자만 룸의 메시지를 가져오거나 게시할 수 있게 됩니다. 비어 있는 경우, 사용자 ID에 의한 액세스 제한은 적용되지 않습니다(단, 비밀번호가 별도로 필요할 수 있습니다). |

**관련 메서드:**
createRoom - 채팅룸을 작성한다
deleteRoom - 채팅룸을 삭제한다
getRoom - 채팅룸 정보를 취득한다


---

### EzMessage

메시지<br>

메시지는 룸에 게시된 데이터입니다.<br>

카테고리라는 필드를 가지고 있어 메시지를 분류할 수 있습니다.<br>
예를 들어, 카테고리가 0인 경우는 일반적인 텍스트 메시지로 해석하고, 1인 경우는 스탬프(스티커)로 처리하도록 클라이언트를 구현할 수 있습니다.<br>

게시된 메시지는 게시 후, Chat Namespace의 messageLifeTimeDays에서 설정한 메시지 보관 기간이 경과하면 자동으로 삭제됩니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| name | string |  | ✓ | UUID |  ~ 36자 | 메시지 이름<br>메시지의 고유한 이름을 유지합니다.<br>이름은 UUID(Universally Unique Identifier) 형식으로 자동 생성되며, 각 메시지를 식별하는 데 사용됩니다. |
| roomName | string |  | ✓ | UUID |  ~ 128자 | 룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| userId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| category | int |  |  | 0 | 0 ~ 2147483645 | 메시지 분류용 카테고리 번호<br>메시지를 분류하기 위한 숫자 값입니다. 예를 들어, 0은 텍스트 메시지, 1은 스탬프(스티커), 그 외의 값은 커스텀 메시지 타입으로 사용할 수 있습니다. 카테고리에 따라 메시지에 적용되는 CategoryModel의 규칙이 결정됩니다. |
| metadata | string |  | ✓ |  |  ~ 1024자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| createdAt | long |  | ※ | 현재 시각 |  | 생성일시<br>UNIX 시간・밀리초<br>※ 서버가 자동으로 설정 |

**관련 메서드:**
getMessage - 메시지 이름을 지정하여 메시지를 1건 취득한다
listLatestMessages - 채팅룸의 최신 메시지를 취득한다
listMessages - 채팅룸의 메시지를 취득한다(지정 시각 이후)
post - 채팅룸에 메시지를 게시한다


---

### EzSubscribe

룸 구독<br>

룸을 구독하면 해당 룸에 대한 신규 메시지의 존재를 즉시 알 수 있게 됩니다.<br>
구독할 때 메시지의 카테고리를 지정할 수 있습니다.<br>
이 기능을 잘 활용하면 중요도가 높은 메시지만 수신하는 설정도 가능합니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| userId | string |  | ✓ |  |  ~ 128자 | 사용자ID |
| roomName | string |  | ✓ |  |  ~ 128자 | 구독할 룸 이름<br>구독할 채팅 룸의 이름입니다. 구독하면, 이 룸에 새로운 메시지가 게시될 때마다 GS2-Gateway를 통해 푸시 알림이 전송됩니다. |
| notificationTypes | [List&lt;EzNotificationType&gt;](#eznotificationtype) |  |  | [] | 0 ~ 100 items | 신규 메시지 알림을 받을 카테고리 리스트<br>푸시 알림을 트리거하는 메시지 카테고리를 필터링합니다. 비어 있는 경우, 모든 카테고리에 대해 알림이 전송됩니다. 각 항목은 카테고리 번호와 선택적인 모바일 푸시 알림 전달 여부를 지정합니다. |

**관련 메서드:**
listSubscribeRooms - 플레이어가 구독 중인 룸 목록을 취득한다
subscribe - 채팅룸을 구독한다
unsubscribe - 채팅룸의 구독을 해제한다
updateSubscribeSetting - 구독 중인 룸의 알림 설정을 변경한다


---

### EzCategoryModel

메시지 카테고리 모델<br>

메시지 카테고리 모델은 채팅 룸에 게시되는 메시지를 분류하기 위한 카테고리를 정의합니다.<br>
각 카테고리는 숫자로 식별되며, 카테고리별로 플레이어의 액세스 토큰을 사용한 게시를 허용할지 거부할지 설정할 수 있습니다.<br>
이를 통해 서버만 게시할 수 있는 시스템 안내 카테고리 등의 사용 사례를 구현할 수 있습니다.

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| category | int |  | ✓ |  | 0 ~ 2147483645 | 카테고리<br>메시지 카테고리의 숫자 식별자입니다. 이 카테고리 번호로 게시된 메시지는, 플레이어의 게시를 허용할지 여부 등 이 모델에서 정의된 규칙을 따릅니다. |
| rejectAccessTokenPost | 문자열 열거형<br>enum {<br>"Enabled",<br>"Disabled"<br>}<br> |  |  |  |  | 플레이어의 액세스 토큰을 이용한 게시를 거부한다<br>활성화하면, 이 카테고리에서는 서버 사이드 API 호출(사용자 ID 지정)만으로 메시지를 게시할 수 있습니다. 플레이어가 직접 게시해서는 안 되는 시스템 안내나 서버 생성 메시지에 유용합니다.Enabled: 액세스 토큰을 이용한 게시를 거부한다 / Disabled: 액세스 토큰을 이용한 게시를 허용한다 /  |

**관련 메서드:**
getCategoryModel - 카테고리 번호를 지정하여 메시지 카테고리 정의를 취득한다
listCategoryModels - 메시지 카테고리 모델 목록을 취득한다


---

### EzNotificationType

알림 타입<br>

신규 메시지 알림을 받을 카테고리 설정

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| category | int |  |  | 0 | 0 ~ 2147483646 | 신규 메시지 알림을 받을 카테고리<br>알림을 필터링하기 위한 카테고리의 숫자 식별자입니다. 이 카테고리와 일치하는 메시지만 구독에 대한 푸시 알림을 트리거합니다. |
| enableTransferMobilePushNotification | bool |  |  | false |  | 오프라인 상태였을 때 모바일 푸시 알림으로 전달할지 여부<br>활성화하면, 알림 시점에 수신 대상 기기가 오프라인 상태였던 경우 모바일 푸시 알림 서비스로 전달됩니다. 이를 통해 게임이 실행되고 있지 않은 상태에서도 플레이어에게 신규 메시지를 알릴 수 있습니다. |

**관련 메서드:**
subscribe - 채팅룸을 구독한다
updateSubscribeSetting - 구독 중인 룸의 알림 설정을 변경한다


**관련 모델:**
EzSubscribe - 룸 구독



---

## 메서드

### createRoom

채팅룸을 작성한다<br>

플레이어가 메시지를 주고받을 수 있는 새로운 채팅룸을 작성합니다.<br>
네임스페이스 설정에서 플레이어에 의한 룸 작성이 허용되어 있어야 합니다. 허용되어 있지 않은 경우 실패합니다.<br>

룸에 비밀번호를 설정할 수도 있습니다. 비밀번호를 설정한 경우, 메시지 게시·취득 시 비밀번호 입력이 필요합니다.<br>
화이트리스트에 사용자 ID를 설정하여 접근할 수 있는 플레이어를 제한하는 것도 가능합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| name | string |  | | UUID |  ~ 128자 | 룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | |  |  | GameSession<br>룸 오너의 사용자 ID입니다. 설정하면, 오너만 룸을 삭제할 수 있게 됩니다. 오너 설정은 선택 사항입니다. |
| metadata | string |  | |  |  ~ 1024자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| password | string |  | |  |  ~ 128자 | 룸에 액세스하기 위해 필요한 비밀번호<br>설정하면, 메시지를 가져오거나 게시할 때 비밀번호 입력이 필요해집니다. 설정한 비밀번호는 다시 확인할 수 없습니다. 통신의 비밀에 해당할 가능성이 있어, 게임 관리자라도 비밀번호를 모르면 메시지에 액세스할 수 없게 됩니다. |
| whiteListUserIds | List&lt;string&gt; |  | | [] | 0 ~ 1000 items | 룸에 액세스 가능한 사용자 ID 리스트<br>설정하면, 리스트에 포함된 사용자만 룸의 메시지를 가져오거나 게시할 수 있게 됩니다. 비어 있는 경우, 사용자 ID에 의한 액세스 제한은 적용되지 않습니다(단, 비밀번호가 별도로 필요할 수 있습니다). |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzRoom](#ezroom) | 생성한 룸|

#### Error

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

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| NoAccessPrivilegesException | BadRequestException | 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다 |

#### 구현 예제




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var result = await domain.CreateRoomAsync(
        name: "room-0001",
        metadata: null,
        password: null,
        whiteListUserIds: null
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    );
    var future = domain.CreateRoomFuture(
        name: "room-0001",
        metadata: null,
        password: null,
        whiteListUserIds: null
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Chat.Exception.NoAccessPrivilegesException)
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        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->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    );
    const auto Future = Domain->CreateRoom(
        "room-0001" // name
        // metadata
        // password
        // whiteListUserIds
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Chat::Error::FNoAccessPrivilegesError::Class))
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        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.chat.namespace_(
        "namespace-0001"
    ).me(game_session)

var async_result = await domain.create_room(
    "room-0001", # name
    null, # metadata
    null, # password
    null # white_list_user_ids
)
if async_result.error != null:
    if async_result.error is Gs2ChatNoAccessPrivilegesException:
        # 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### deleteRoom

채팅룸을 삭제한다<br>

플레이어가 작성한 채팅룸을 삭제합니다.<br>
룸을 작성한 플레이어(오너)만 삭제할 수 있습니다.<br>
룸 안의 모든 메시지도 함께 삭제됩니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzRoom](#ezroom) | 삭제한 룸|

#### Error

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

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| NoAccessPrivilegesException | BadRequestException | 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다 |

#### 구현 예제




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var result = await domain.DeleteRoomAsync(
    );
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var future = domain.DeleteRoomFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Chat.Exception.NoAccessPrivilegesException)
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        onError.Invoke(future.Error, null);
        yield break;
    }

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto Future = Domain->DeleteRoom(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Chat::Error::FNoAccessPrivilegesError::Class))
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        return false;
    }
    const auto Result = Future->GetTask().Result();

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).me(game_session).room(
        "room-0001",
        null
    )

var async_result = await domain.delete_room(
)
if async_result.error != null:
    if async_result.error is Gs2ChatNoAccessPrivilegesException:
        # 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### getRoom

채팅룸 정보를 취득한다<br>

지정한 채팅룸의 메타데이터나 작성 일시 등의 정보를 취득합니다.<br>
룸에 비밀번호가 설정되어 있어도 비밀번호 없이 취득할 수 있습니다.<br>
플레이어가 룸에 입장하기 전에, 룸의 상세 정보(룸 이름이나 주제 등)를 표시할 때 사용합니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzRoom](#ezroom) | 룸|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: null
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: null
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->User(
        nullptr // userId
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).user(
        null
    ).room(
        "room-0001",
        null
    )

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.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: null
    ).Room(
        roomName: "room-0001",
        password: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).User(
        userId: null
    ).Room(
        roomName: "room-0001",
        password: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->User(
        nullptr // userId
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    
    // 이벤트 핸들링 시작
    const auto CallbackId = Domain->Subscribe(
        [](TSharedPtr<Gs2::Chat::Model::FRoom> value) {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).user(
        null
    ).room(
        "room-0001",
        null
    )

# 이벤트 핸들링 시작
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의 실행에 의해 변화한 것만이 대상이 됩니다.

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

---

### getMessage

메시지 이름을 지정하여 메시지를 1건 취득한다<br>

메시지 이름(ID)을 지정하여 채팅룸 내의 메시지를 1건 취득합니다.<br>
알림을 탭했을 때 등, 특정 메시지의 상세 정보를 표시할 때 사용합니다.<br>

룸에 비밀번호가 설정되어 있는 경우 비밀번호 입력이 필요합니다.

#### Request

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

#### Result

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

#### Error

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

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| NoAccessPrivilegesException | BadRequestException | 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다 |
| PasswordRequiredException | BadRequestException | 룸에 접근하려면 비밀번호 지정이 필요합니다 |
| PasswordIncorrectException | BadRequestException | 룸에 설정된 비밀번호와 지정된 비밀번호가 일치하지 않습니다 |

#### 구현 예제




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    ).Message(
        messageName: "message-0001"
    );
    var item = await domain.ModelAsync();
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
} catch(Gs2.Gs2Chat.Exception.PasswordRequiredException e) {
    // A password must be specified to access the room.
} catch(Gs2.Gs2Chat.Exception.PasswordIncorrectException e) {
    // The password set for the room does not match the password specified.
}

```

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

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Room(
        "room-0001", // roomName
        nullptr // password
    )->Message(
        "message-0001" // messageName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Chat::Error::FNoAccessPrivilegesError::Class))
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        if (e->IsChildOf(Gs2::Chat::Error::FPasswordRequiredError::Class))
        {
            // A password must be specified to access the room.
        }
        if (e->IsChildOf(Gs2::Chat::Error::FPasswordIncorrectError::Class))
        {
            // The password set for the room does not match the password specified.
        }
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).me(game_session).room(
        "room-0001",
        null
    ).message(
        "message-0001"
    )

var async_result = await domain.model()
if async_result.error != null:
    if async_result.error is Gs2ChatNoAccessPrivilegesException:
        # 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다
        pass
    if async_result.error is Gs2ChatPasswordRequiredException:
        # 룸에 접근하려면 비밀번호 지정이 필요합니다
        pass
    if async_result.error is Gs2ChatPasswordIncorrectException:
        # 룸에 설정된 비밀번호와 지정된 비밀번호가 일치하지 않습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


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




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    ).Message(
        messageName: "message-0001"
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

    // 이벤트 핸들링 정지
    domain.Unsubscribe(callbackId);
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
} catch(Gs2.Gs2Chat.Exception.PasswordRequiredException e) {
    // A password must be specified to access the room.
} catch(Gs2.Gs2Chat.Exception.PasswordIncorrectException e) {
    // The password set for the room does not match the password specified.
}

```

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

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

```

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

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

```

**Godot**
```gdscript

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

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

---

### listLatestMessages

채팅룸의 최신 메시지를 취득한다<br>

채팅룸 내의 최신 메시지를 새로운 순으로 취득합니다.<br>
플레이어가 채팅 화면을 열었을 때 최근 대화 내용을 표시할 때 사용합니다.<br>

룸에 비밀번호가 설정되어 있는 경우 비밀번호 입력이 필요합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| roomName | string |  | ✓| UUID |  ~ 128자 | 룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |
| password | string |  | |  |  ~ 128자 | 룸에 액세스하기 위해 필요한 비밀번호<br>설정하면, 메시지를 가져오거나 게시할 때 비밀번호 입력이 필요해집니다. 설정한 비밀번호는 다시 확인할 수 없습니다. 통신의 비밀에 해당할 가능성이 있어, 게임 관리자라도 비밀번호를 모르면 메시지에 액세스할 수 없게 됩니다. |

#### Result

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

#### Error

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

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| NoAccessPrivilegesException | BadRequestException | 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다 |
| PasswordRequiredException | BadRequestException | 룸에 접근하려면 비밀번호 지정이 필요합니다 |
| PasswordIncorrectException | BadRequestException | 룸에 설정된 비밀번호와 지정된 비밀번호가 일치하지 않습니다 |

#### 구현 예제




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var items = await domain.LatestMessagesAsync(
    ).ToListAsync();
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
} catch(Gs2.Gs2Chat.Exception.PasswordRequiredException e) {
    // A password must be specified to access the room.
} catch(Gs2.Gs2Chat.Exception.PasswordIncorrectException e) {
    // The password set for the room does not match the password specified.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var it = domain.LatestMessages(
    );
    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->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto It = Domain->LatestMessages(
    );
    TArray<Gs2::UE5::Chat::Model::FEzMessagePtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


---

### listMessages

채팅룸의 메시지를 취득한다(지정 시각 이후)<br>

`startAt`에 지정한 시각 이후에 게시된 메시지를 오래된 순으로 취득합니다.<br>
이전 확인 이후의 신규 메시지를 취득하고 싶을 때(재접속 시나 채팅 화면을 열었을 때 등)에 사용합니다.<br>

네임스페이스 설정에서 지정된 보존 기간 내의 메시지만 취득할 수 있습니다.<br>
룸에 비밀번호가 설정되어 있는 경우 비밀번호 입력이 필요합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| roomName | string |  | ✓| UUID |  ~ 128자 | 룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| startAt | long |  | | 현재 시각으로부터 1시간 전의 절대 시각 |  | 메시지 취득을 시작하는 시각<br>UNIX 시간·밀리초 |
| limit | int |  | | 30 | 1 ~ 1000 | 취득할 데이터 건수 |
| password | string |  | |  |  ~ 128자 | 룸에 액세스하기 위해 필요한 비밀번호<br>설정하면, 메시지를 가져오거나 게시할 때 비밀번호 입력이 필요해집니다. 설정한 비밀번호는 다시 확인할 수 없습니다. 통신의 비밀에 해당할 가능성이 있어, 게임 관리자라도 비밀번호를 모르면 메시지에 액세스할 수 없게 됩니다. |

#### Result

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

#### Error

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

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| NoAccessPrivilegesException | BadRequestException | 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다 |
| PasswordRequiredException | BadRequestException | 룸에 접근하려면 비밀번호 지정이 필요합니다 |
| PasswordIncorrectException | BadRequestException | 룸에 설정된 비밀번호와 지정된 비밀번호가 일치하지 않습니다 |

#### 구현 예제




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var items = await domain.MessagesAsync(
    ).ToListAsync();
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
} catch(Gs2.Gs2Chat.Exception.PasswordRequiredException e) {
    // A password must be specified to access the room.
} catch(Gs2.Gs2Chat.Exception.PasswordIncorrectException e) {
    // The password set for the room does not match the password specified.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    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->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto It = Domain->Messages(
    );
    TArray<Gs2::UE5::Chat::Model::FEzMessagePtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }

```


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




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.SubscribeMessages(
        () => {
            // 리스트의 요소가 변화했을 때 호출됨
        }
    );

    // 이벤트 핸들링 정지
    domain.UnsubscribeMessages(callbackId);
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
} catch(Gs2.Gs2Chat.Exception.PasswordRequiredException e) {
    // A password must be specified to access the room.
} catch(Gs2.Gs2Chat.Exception.PasswordIncorrectException e) {
    // The password set for the room does not match the password specified.
}

```

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

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

```

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

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

```


**⚠️ Warning**

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

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

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

---

### post

채팅룸에 메시지를 게시한다<br>

지정한 채팅룸에 메시지를 전송합니다.<br>
`카테고리` 숫자를 지정하여 메시지의 종류를 구분할 수 있습니다(예: 0은 텍스트, 1은 스탬프).<br>
메시지의 내용은 `metadata`에 자유로운 형식의 문자열로 저장됩니다. 텍스트, JSON 등 게임에 맞는 데이터를 저장할 수 있습니다.<br>
룸에 비밀번호가 설정되어 있는 경우 비밀번호 입력이 필요합니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| roomName | string |  | ✓|  |  ~ 128자 | 룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| category | int |  | | 0 | 0 ~ 2147483645 | 메시지 분류용 카테고리 번호<br>메시지를 분류하기 위한 숫자 값입니다. 예를 들어, 0은 텍스트 메시지, 1은 스탬프(스티커), 그 외의 값은 커스텀 메시지 타입으로 사용할 수 있습니다. 카테고리에 따라 메시지에 적용되는 CategoryModel의 규칙이 결정됩니다. |
| metadata | string |  | ✓|  |  ~ 1024자 | 메타데이터<br>메타데이터에는 임의의 값을 설정할 수 있습니다.<br>이 값들은 GS2의 동작에는 영향을 주지 않으므로, 게임 내에서 사용하는 정보를 저장하는 용도로 사용할 수 있습니다. |
| password | string |  | |  |  ~ 128자 | 비밀번호 |

#### Result

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

#### Error

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

| 타입 | 베이스 클래스 | 설명 |
| --- | --- | --- |
| NoAccessPrivilegesException | BadRequestException | 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다 |
| PasswordRequiredException | BadRequestException | 룸에 접근하려면 비밀번호 지정이 필요합니다 |
| PasswordIncorrectException | BadRequestException | 룸에 설정된 비밀번호와 지정된 비밀번호가 일치하지 않습니다 |

#### 구현 예제




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

try {
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var result = await domain.PostAsync(
        metadata: "MESSAGE_0001",
        category: null
    );
    var item = await result.ModelAsync();
} catch(Gs2.Gs2Chat.Exception.NoAccessPrivilegesException e) {
    // The whitelist configured for the room does not contain any currently logged in user.
} catch(Gs2.Gs2Chat.Exception.PasswordRequiredException e) {
    // A password must be specified to access the room.
} catch(Gs2.Gs2Chat.Exception.PasswordIncorrectException e) {
    // The password set for the room does not match the password specified.
}

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    );
    var future = domain.PostFuture(
        metadata: "MESSAGE_0001",
        category: null
    );
    yield return future;
    if (future.Error != null)
    {
        if (future.Error is Gs2.Gs2Chat.Exception.NoAccessPrivilegesException)
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        if (future.Error is Gs2.Gs2Chat.Exception.PasswordRequiredException)
        {
            // A password must be specified to access the room.
        }
        if (future.Error is Gs2.Gs2Chat.Exception.PasswordIncorrectException)
        {
            // The password set for the room does not match the password specified.
        }
        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->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto Future = Domain->Post(
        "MESSAGE_0001" // metadata
        // category
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        auto e = Future->GetTask().Error();
        if (e->IsChildOf(Gs2::Chat::Error::FNoAccessPrivilegesError::Class))
        {
            // The whitelist configured for the room does not contain any currently logged in user.
        }
        if (e->IsChildOf(Gs2::Chat::Error::FPasswordRequiredError::Class))
        {
            // A password must be specified to access the room.
        }
        if (e->IsChildOf(Gs2::Chat::Error::FPasswordIncorrectError::Class))
        {
            // The password set for the room does not match the password specified.
        }
        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.chat.namespace_(
        "namespace-0001"
    ).me(game_session).room(
        "room-0001",
        null
    )

var async_result = await domain.post(
    "MESSAGE_0001", # metadata
    null # category
)
if async_result.error != null:
    if async_result.error is Gs2ChatNoAccessPrivilegesException:
        # 룸에 설정된 화이트리스트에 로그인 중인 사용자가 포함되어 있지 않습니다
        pass
    if async_result.error is Gs2ChatPasswordRequiredException:
        # 룸에 접근하려면 비밀번호 지정이 필요합니다
        pass
    if async_result.error is Gs2ChatPasswordIncorrectException:
        # 룸에 설정된 비밀번호와 지정된 비밀번호가 일치하지 않습니다
        pass
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### listSubscribeRooms

플레이어가 구독 중인 룸 목록을 취득한다<br>

신규 메시지 알림을 받기 위해 구독 중인 채팅룸의 목록을 취득합니다.<br>
채팅 UI에서 「구독 중인 룸」 목록을 표시하여 플레이어가 관심 있는 룸에 바로 접근할 수 있도록 할 때 사용합니다.

#### Request

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

#### Result

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

#### 구현 예제




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

```

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

```


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




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

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

```

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

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

```

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

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

```


**⚠️ Warning**

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

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

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

---

### subscribe

채팅룸을 구독한다<br>

채팅룸을 구독하여 새로운 메시지가 게시되었을 때 알림을 받을 수 있도록 합니다.<br>
카테고리 조건을 설정함으로써 어떤 메시지에서 알림을 받을지 필터링할 수 있습니다.<br>
예를 들어 「카테고리 1(스탬프)일 때만 알림」 「모든 카테고리에서 알림」과 같은 설정이 가능합니다.<br>
알림을 받을 때 플레이어가 오프라인 상태인 경우, 모바일 푸시 알림으로 전달할 수도 있습니다(네임스페이스 설정에 따라 다름).

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| roomName | string |  | ✓|  |  ~ 128자 | 구독할 룸 이름<br>구독할 채팅 룸의 이름입니다. 구독하면, 이 룸에 새로운 메시지가 게시될 때마다 GS2-Gateway를 통해 푸시 알림이 전송됩니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| notificationTypes | [List&lt;EzNotificationType&gt;](#eznotificationtype) |  | | [] | 0 ~ 100 items | 신규 메시지 알림을 받을 카테고리 리스트<br>푸시 알림을 트리거하는 메시지 카테고리를 필터링합니다. 비어 있는 경우, 모든 카테고리에 대해 알림이 전송됩니다. 각 항목은 카테고리 번호와 선택적인 모바일 푸시 알림 전달 여부를 지정합니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzSubscribe](#ezsubscribe) | 룸 구독|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    );
    var result = await domain.SubscribeAsync(
        notificationTypes: new List<Gs2.Unity.Gs2Chat.Model.EzNotificationType> {
            new Gs2.Unity.Gs2Chat.Model.EzNotificationType {
            },
        }
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    );
    var future = domain.SubscribeFuture(
        notificationTypes: new List<Gs2.Unity.Gs2Chat.Model.EzNotificationType> {
            new Gs2.Unity.Gs2Chat.Model.EzNotificationType {
            },
        }
    );
    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->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Subscribe(
        "room-0001" // roomName
    );
    const auto Future = Domain->Subscribe(
        []
        {
            auto v = MakeShared<TArray<TSharedPtr<Gs2::UE5::Chat::Model::FEzNotificationType>>>();
            v->Add(
                MakeShared<Gs2::UE5::Chat::Model::FEzNotificationType>());
            return v;
        }() // notificationTypes
    );
    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.chat.namespace_(
        "namespace-0001"
    ).me(game_session).subscribe(
        "room-0001"
    )

var async_result = await domain.subscribe(
    [
        (Gs2ChatEzNotificationType.new()),
    ] # notification_types
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### unsubscribe

채팅룸의 구독을 해제한다<br>

지정한 채팅룸의 신규 메시지 알림 수신을 중지합니다.<br>
그룹에서 나갔을 때나 설정 화면에서 알림을 껐을 때 등, 플레이어가 룸 구독을 해제할 때 사용합니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzSubscribe](#ezsubscribe) | 해제한 룸 구독|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    );
    var result = await domain.UnsubscribeAsync(
    );

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    );
    var future = domain.UnsubscribeFuture(
    );
    yield return future;
    if (future.Error != null)
    {
        onError.Invoke(future.Error, null);
        yield break;
    }

```

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

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).me(game_session).subscribe(
        "room-0001"
    )

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

var result = async_result.result

```


---

### updateSubscribeSetting

구독 중인 룸의 알림 설정을 변경한다<br>

이미 구독 중인 룸에서 어떤 카테고리의 메시지로 알림을 받을지 변경합니다.<br>
예를 들어, 처음에는 모든 카테고리로 구독했던 것을 설정 화면에서 「스탬프만」으로 변경하는 방식으로 사용할 수 있습니다.

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| roomName | string |  | ✓|  |  ~ 128자 | 구독 중인 룸 이름<br>알림 설정을 변경할 대상 구독 중 채팅 룸 이름입니다. |
| gameSession | GameSession | | ✓|  |  | GameSession |
| notificationTypes | [List&lt;EzNotificationType&gt;](#eznotificationtype) |  | | [] | 0 ~ 100 items | 신규 메시지 알림을 받을 카테고리 리스트<br>푸시 알림을 트리거하는 메시지 카테고리를 필터링합니다. 비어 있는 경우, 모든 카테고리에 대해 알림이 전송됩니다. 각 항목은 카테고리 번호와 선택적인 모바일 푸시 알림 전달 여부를 지정합니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzSubscribe](#ezsubscribe) | 갱신한 룸 구독|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    );
    var result = await domain.UpdateSubscribeSettingAsync(
        notificationTypes: new List<Gs2.Unity.Gs2Chat.Model.EzNotificationType> {
            new Gs2.Unity.Gs2Chat.Model.EzNotificationType() {},
        }
    );
    var item = await result.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    );
    var future = domain.UpdateSubscribeSettingFuture(
        notificationTypes: new List<Gs2.Unity.Gs2Chat.Model.EzNotificationType> {
            new Gs2.Unity.Gs2Chat.Model.EzNotificationType() {},
        }
    );
    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->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        GameSession
    )->Subscribe(
        "room-0001" // roomName
    );
    const auto Future = Domain->UpdateSubscribeSetting(
        []
        {
            auto v = MakeShared<TArray<TSharedPtr<Gs2::UE5::Chat::Model::FEzNotificationType>>>();
            v->Add(
                MakeShared<Gs2::UE5::Chat::Model::FEzNotificationType>() {});
            return v;
        }() // notificationTypes
    );
    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.chat.namespace_(
        "namespace-0001"
    ).me(game_session).subscribe(
        "room-0001"
    )

var async_result = await domain.update_subscribe_setting(
    [
        Gs2ChatEzNotificationType.new(),
    ] # notification_types
)
if async_result.error != null:
    push_error(str(async_result.error))
    return

var result = async_result.result

```


---

### getCategoryModel

카테고리 번호를 지정하여 메시지 카테고리 정의를 취득한다<br>

카테고리 번호를 지정하여 메시지 카테고리 정의를 1건 취득합니다.<br>
취득할 수 있는 정보에는 플레이어가 이 카테고리로의 게시를 제한받고 있는지 여부가 포함됩니다.<br>
(서버 측의 시스템 메시지 전용 카테고리 등에 이용할 수 있습니다).

#### Request

|  | 타입 | 활성화 조건 | 필수 | 기본값 | 값 제한 | 설명 |
| --- | --- | --- | --- | --- | --- | --- |
| namespaceName | string |  | ✓|  |  ~ 128자 | 네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다. |
| category | int |  | ✓|  | 0 ~ 2147483645 | 카테고리<br>메시지 카테고리의 숫자 식별자입니다. 이 카테고리 번호로 게시된 메시지는, 플레이어의 게시를 허용할지 여부 등 이 모델에서 정의된 규칙을 따릅니다. |

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| item | [EzCategoryModel](#ezcategorymodel) | 메시지 카테고리 모델|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        category: 0
    );
    var item = await domain.ModelAsync();

```

**Unity (Vanilla)**
```cs
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        category: 0
    );
    var future = domain.ModelFuture();
    yield return future;
    var item = future.Result;

```

**Unreal Engine 5**
```cpp
    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->CategoryModel(
        0 // category
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError())
    {
        return false;
    }

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).category_model(
        0
    )

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.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).CategoryModel(
        category: 0
    );
    
    // 이벤트 핸들링 시작
    var callbackId = domain.Subscribe(
        value => {
            // 값이 변화했을 때 호출됨
            // value에는 변경 후의 값이 전달됨
        }
    );

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

```

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

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

```

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

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

```

**Godot**
```gdscript

var domain = ez.chat.namespace_(
        "namespace-0001"
    ).category_model(
        0
    )

# 이벤트 핸들링 시작
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의 실행에 의해 변화한 것만이 대상이 됩니다.

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

---

### listCategoryModels

메시지 카테고리 모델 목록을 취득한다<br>

이 네임스페이스에 등록되어 있는 모든 메시지 카테고리 모델을 취득합니다.<br>
카테고리를 사용하면 메시지의 종류를 분류할 수 있습니다. 예를 들어 카테고리 0을 일반 텍스트, 카테고리 1을 스탬프로 지정하는 방식입니다.<br>
카테고리별로 게시 권한을 제어할 수도 있습니다(예: 시스템 공지는 서버에서만 게시 가능).<br>
채팅 UI에서 카테고리 선택을 표시하거나 사용 가능한 메시지 타입을 확인할 때 사용합니다.

#### Request

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

#### Result

|  | 타입 | 설명 |
| --- | --- | --- |
| items | [List&lt;EzCategoryModel&gt;](#ezcategorymodel) | 메시지 카테고리 모델 목록|

#### 구현 예제




**Unity (UniTask)**
```csharp
    var domain = gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    );
    var items = await domain.CategoryModelsAsync(
    ).ToListAsync();

```

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

```


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




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

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

```

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

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

```

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

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

```


**⚠️ Warning**

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

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

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

---

## 이벤트 핸들러

### OnPostNotification

구독 중인 룸에 새로운 게시물이 있을 때의 알림

 | 이름 | 타입 | 설명 |
| --- | --- | --- |
| namespaceName | string |네임스페이스 이름<br>네임스페이스 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.|
| roomName | string |룸 이름<br>룸 고유의 이름입니다. 영숫자 및 -(하이픈) _(언더스코어) .(마침표)로 지정합니다.|
| userId | string |사용자ID|
| category | int |메시지 분류용 카테고리 번호<br>메시지를 분류하기 위한 숫자 값입니다. 예를 들어, 0은 텍스트 메시지, 1은 스탬프(스티커), 그 외의 값은 커스텀 메시지 타입으로 사용할 수 있습니다. 카테고리에 따라 메시지에 적용되는 CategoryModel의 규칙이 결정됩니다.|
| createdAt | long |생성일시<br>UNIX 시간・밀리초<br>※ 서버가 자동으로 설정|

#### 구현 예제





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

    gs2.Chat.OnPostNotification += notification =>
    {
        var namespaceName = notification.NamespaceName;
        var roomName = notification.RoomName;
        var userId = notification.UserId;
        var category = notification.Category;
        var createdAt = notification.CreatedAt;
    };
```

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

    gs2.Chat.OnPostNotification += notification =>
    {
        var namespaceName = notification.NamespaceName;
        var roomName = notification.RoomName;
        var userId = notification.UserId;
        var category = notification.Category;
        var createdAt = notification.CreatedAt;
    };
```

**Unreal Engine 5**
```cpp

    Gs2->Chat->OnPostNotification().AddLambda([](const auto Notification)
    {
        const auto NamespaceName = Notification->NamespaceNameValue;
        const auto RoomName = Notification->RoomNameValue;
        const auto UserId = Notification->UserIdValue;
        const auto Category = Notification->CategoryValue;
        const auto CreatedAt = Notification->CreatedAtValue;
    });
```

**Godot**
```gdscript

    ez.chat.post_notification.connect(func(notification):
        var namespace_name = notification.namespace_name
        var room_name = notification.room_name
        var user_id = notification.user_id
        var category = notification.category
        var created_at = notification.created_at
    )
```


---



