본문으로 건너뛰기

ChatModels

채팅 시스템에서 사용하는 데이터 모델 및 인터페이스 정의입니다.

IChatListener 인터페이스

채팅 이벤트를 수신하기 위한 리스너 인터페이스입니다.

메서드설명
OnConnected서버 연결 성공 시 호출
OnDisconnected서버 연결 해제 시 호출
OnError오류 발생 시 호출
OnChannels채널 목록 수신 시 호출
OnSubscribed채널 구독 시 호출
OnUnSubscribed채널 구독 해제 시 호출
OnPublicMessage공개 메시지 수신 시 호출
OnPrivateMessage비공개 메시지 수신 시 호출
OnNotifyMessage알림 메시지 수신 시 호출
OnPlayerOnline플레이어 온라인 상태 수신 시 호출

데이터 모델

FChatServerResponse

필드타입설명
ErrorCodeFString에러 코드
MessageFString메시지
ServersTArray<FChatServerItem>서버 목록

FChatServerItem

필드타입설명
AddrFString서버 주소
Portint32서버 포트
SecureFStringSSL 사용 여부 (Y/N)

FChatMessageModel

필드타입설명
midFString메시지 ID
typeuint8이벤트 타입
errorint32에러 코드
gameIdFString게임 ID
serviceKeyFString서비스 키
channelIdFString채널 ID
userUniqueIdFString사용자 고유 ID
userNameFString사용자 이름
privateToUserUniqueIdFString비공개 메시지 대상 ID
messageFString메시지 내용
channelsTArray<FChatChannelInfo>채널 정보 배열
playersTArray<FChatPlayerInfo>플레이어 정보 배열
onlinePlayersTArray<FString>온라인 플레이어 ID 배열
prevMessageCountint32이전 메시지 가져올 개수
ipAddrFStringIP 주소
createdAtFString생성 일시

FChatUserInfo

필드타입설명
visitorIdFString방문자 ID
visitorNameFString방문자 이름

FChatChannelInfo

필드타입설명
channelFString채널 이름
countint32접속자 수

FChatPlayerInfo

필드타입설명
userUniqueIdFString사용자 고유 ID
onlineFString온라인 상태

FChatFilterResponse

필드타입설명
ErrorCodeFString에러 코드
MessageFString메시지
FilterWordsTArray<FString>필터링된 단어 목록

Unreal C++ 구현

헤더 파일 (ChatModels.h)

// ChatModels.h
#pragma once

#include "CoreMinimal.h"
#include "Json.h"
#include "JsonUtilities.h"
#include "ChatModels.generated.h"

// 채팅 이벤트 리스너 인터페이스
class IChatListener
{
public:
virtual ~IChatListener() = default;
virtual void OnConnected() = 0;
virtual void OnDisconnected() = 0;
virtual void OnError(const FString& Code, const FString& Message) = 0;
virtual void OnChannels(const TArray<struct FChatChannelInfo>& Channels) = 0;
virtual void OnSubscribed(const struct FChatUserInfo& User) = 0;
virtual void OnUnSubscribed(const struct FChatUserInfo& User) = 0;
virtual void OnPublicMessage(const struct FChatUserInfo& Sender, const FString& Message) = 0;
virtual void OnPrivateMessage(const struct FChatUserInfo& Sender, const FString& Message) = 0;
virtual void OnNotifyMessage(const struct FChatUserInfo& Sender, const FString& Message) = 0;
virtual void OnPlayerOnline(const TArray<struct FChatPlayerInfo>& Players) = 0;
};

// 서버 응답 구조체
USTRUCT(BlueprintType)
struct FChatServerItem
{
GENERATED_BODY()

UPROPERTY()
FString Addr;

UPROPERTY()
int32 Port;

UPROPERTY()
FString Secure;

void FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
JsonObject->TryGetStringField(TEXT("Addr"), Addr);
JsonObject->TryGetNumberField(TEXT("Port"), Port);
JsonObject->TryGetStringField(TEXT("Secure"), Secure);
}
};

USTRUCT(BlueprintType)
struct FChatServerResponse
{
GENERATED_BODY()

UPROPERTY()
FString ErrorCode;

UPROPERTY()
FString Message;

UPROPERTY()
TArray<FChatServerItem> Servers;

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("ErrorCode"), ErrorCode);
JsonObject->TryGetStringField(TEXT("Message"), Message);

const TArray<TSharedPtr<FJsonValue>>* ServersArray;
if (JsonObject->TryGetArrayField(TEXT("Servers"), ServersArray))
{
for (const auto& ServerValue : *ServersArray)
{
FChatServerItem Item;
Item.FromJson(ServerValue->AsObject());
Servers.Add(Item);
}
}
return true;
}
return false;
}
};

// 채팅 사용자 정보
USTRUCT(BlueprintType)
struct FChatUserInfo
{
GENERATED_BODY()

UPROPERTY()
FString visitorId;

UPROPERTY()
FString visitorName;
};

// 채널 정보
USTRUCT(BlueprintType)
struct FChatChannelInfo
{
GENERATED_BODY()

UPROPERTY()
FString channel;

UPROPERTY()
int32 count;

void FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
JsonObject->TryGetStringField(TEXT("channel"), channel);
JsonObject->TryGetNumberField(TEXT("count"), count);
}
};

// 플레이어 온라인 정보
USTRUCT(BlueprintType)
struct FChatPlayerInfo
{
GENERATED_BODY()

UPROPERTY()
FString userUniqueId;

UPROPERTY()
FString online;

void FromJson(const TSharedPtr<FJsonObject>& JsonObject)
{
JsonObject->TryGetStringField(TEXT("userUniqueId"), userUniqueId);
JsonObject->TryGetStringField(TEXT("online"), online);
}
};

// 채팅 메시지 모델
USTRUCT(BlueprintType)
struct FChatMessageModel
{
GENERATED_BODY()

UPROPERTY()
FString mid;

UPROPERTY()
uint8 type;

UPROPERTY()
int32 error;

UPROPERTY()
FString gameId;

UPROPERTY()
FString serviceKey;

UPROPERTY()
FString channelId;

UPROPERTY()
FString userUniqueId;

UPROPERTY()
FString userName;

UPROPERTY()
FString privateToUserUniqueId;

UPROPERTY()
FString message;

UPROPERTY()
TArray<FChatChannelInfo> channels;

UPROPERTY()
TArray<FChatPlayerInfo> players;

UPROPERTY()
TArray<FString> onlinePlayers;

UPROPERTY()
int32 prevMessageCount;

UPROPERTY()
FString ipAddr;

UPROPERTY()
FString createdAt;

FString ToJson() const
{
TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject);

JsonObject->SetStringField(TEXT("mid"), mid);
JsonObject->SetNumberField(TEXT("type"), type);
JsonObject->SetStringField(TEXT("gameId"), gameId);
JsonObject->SetStringField(TEXT("serviceKey"), serviceKey);
JsonObject->SetStringField(TEXT("channelId"), channelId);
JsonObject->SetStringField(TEXT("userUniqueId"), userUniqueId);
JsonObject->SetStringField(TEXT("userName"), userName);
if (!privateToUserUniqueId.IsEmpty())
JsonObject->SetStringField(TEXT("privateToUserUniqueId"), privateToUserUniqueId);
if (!message.IsEmpty())
JsonObject->SetStringField(TEXT("message"), message);
JsonObject->SetNumberField(TEXT("prevMessageCount"), prevMessageCount);
JsonObject->SetStringField(TEXT("ipAddr"), ipAddr);
JsonObject->SetStringField(TEXT("createdAt"), createdAt);

if (onlinePlayers.Num() > 0)
{
TArray<TSharedPtr<FJsonValue>> PlayersArray;
for (const FString& PlayerId : onlinePlayers)
{
PlayersArray.Add(MakeShareable(new FJsonValueString(PlayerId)));
}
JsonObject->SetArrayField(TEXT("onlinePlayers"), PlayersArray);
}

FString OutputString;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
return OutputString;
}

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("mid"), mid);
int32 TypeInt;
if (JsonObject->TryGetNumberField(TEXT("type"), TypeInt))
type = static_cast<uint8>(TypeInt);
JsonObject->TryGetNumberField(TEXT("error"), error);
JsonObject->TryGetStringField(TEXT("gameId"), gameId);
JsonObject->TryGetStringField(TEXT("serviceKey"), serviceKey);
JsonObject->TryGetStringField(TEXT("channelId"), channelId);
JsonObject->TryGetStringField(TEXT("userUniqueId"), userUniqueId);
JsonObject->TryGetStringField(TEXT("userName"), userName);
JsonObject->TryGetStringField(TEXT("privateToUserUniqueId"), privateToUserUniqueId);
JsonObject->TryGetStringField(TEXT("message"), message);
JsonObject->TryGetNumberField(TEXT("prevMessageCount"), prevMessageCount);
JsonObject->TryGetStringField(TEXT("ipAddr"), ipAddr);
JsonObject->TryGetStringField(TEXT("createdAt"), createdAt);

const TArray<TSharedPtr<FJsonValue>>* ChannelsArray;
if (JsonObject->TryGetArrayField(TEXT("channels"), ChannelsArray))
{
for (const auto& ChannelValue : *ChannelsArray)
{
FChatChannelInfo Info;
Info.FromJson(ChannelValue->AsObject());
channels.Add(Info);
}
}

const TArray<TSharedPtr<FJsonValue>>* PlayersArray;
if (JsonObject->TryGetArrayField(TEXT("players"), PlayersArray))
{
for (const auto& PlayerValue : *PlayersArray)
{
FChatPlayerInfo Info;
Info.FromJson(PlayerValue->AsObject());
players.Add(Info);
}
}

return true;
}
return false;
}
};

// 금칙어 필터 응답
USTRUCT(BlueprintType)
struct FChatFilterResponse
{
GENERATED_BODY()

UPROPERTY()
FString ErrorCode;

UPROPERTY()
FString Message;

UPROPERTY()
TArray<FString> FilterWords;

bool FromJson(const FString& JsonString)
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(JsonString);

if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
JsonObject->TryGetStringField(TEXT("ErrorCode"), ErrorCode);
JsonObject->TryGetStringField(TEXT("Message"), Message);

const TArray<TSharedPtr<FJsonValue>>* WordsArray;
if (JsonObject->TryGetArrayField(TEXT("FilterWords"), WordsArray))
{
for (const auto& WordValue : *WordsArray)
{
FilterWords.Add(WordValue->AsString());
}
}
return true;
}
return false;
}
};