본문으로 건너뛰기

ChatManager

채팅 시스템의 싱글톤 매니저 클래스입니다. 채팅 서버 연결 및 메시지 송수신을 관리합니다.

URL 확인

이 API는 service-api.playnanoo.com 도메인을 사용합니다.

API 정보

  • 서버 목록 URL: https://service-api.playnanoo.com/chat/v20211101/server
  • 필터 URL: https://service-api.playnanoo.com/chat/v20211101/filter
  • Method: PUT
  • 인증 필요: 아니오

주요 기능

메서드설명
Connect채팅 서버에 연결
IsConnected연결 상태 확인
Subscribe채널 구독 (이전 메시지 개수 설정 가능)
Unsubscribe채널 구독 해제
GetChannels채널 목록 조회
GetPlayersOnline플레이어 온라인 상태 조회
SendPublicMessage공개 메시지 전송
SendPrivateMessage비공개 메시지 전송
FetchFilterWords금칙어 목록 가져오기
Filter메시지에 금칙어 필터 적용

Unity C# 구현

using System.Collections;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Networking;

public class ChatManager : MonoBehaviour
{
public static ChatManager Instance
{
get
{
if (_instance == null)
{
var go = new GameObject("ChatManager");
_instance = go.AddComponent<ChatManager>();
}
return _instance;
}
}
private static ChatManager _instance;

private const string CHAT_SERVER_URL = "https://service-api.playnanoo.com/chat/v20211101/server";
private const string CHAT_FILTER_URL = "https://service-api.playnanoo.com/chat/v20211101/filter";

private ChatClient _chatClient;
private IChatListener _listener;
private string[] _filterWords;

void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
_instance = this;
DontDestroyOnLoad(gameObject);
}

public void Connect(IChatListener listener)
{
_listener = listener;
_chatClient = new ChatClient(listener, DataManager.Instance.Nickname);
StartCoroutine(FetchServerListAndConnect());
}

void Update()
{
// 실시간 메시지를 수신하기 위해선 지속적으로 이벤트를 발생 시켜야 합니다.
// 메시지 수신 이벤트가 실행 되지 않는 경우 사용자의 메시지가 수신 되지 않습니다.
_chatClient?.Service();
}

void OnDestroy()
{
if (_instance == this)
_instance = null;
_chatClient?.Disconnect();
}

#region Server Connection

private IEnumerator FetchServerListAndConnect()
{
bool done = false;
ChatServerResponse response = null;
string error = null;

yield return HttpClient.Send<object, ChatServerResponse>(
UnityWebRequest.kHttpVerbPUT, CHAT_SERVER_URL, false, null,
res => { response = res; done = true; },
err => { error = err?.Message; done = true; });

while (!done) yield return null;

if (error != null || response?.Servers == null || response.Servers.Length == 0)
{
_listener?.OnError("SERVER_LIST_FAILED", "Failed to fetch server list");
yield break;
}

var server = response.Servers[Random.Range(0, response.Servers.Length)];
_chatClient.Connect(server);
}

#endregion

#region Filter

public void FetchFilterWords()
{
StartCoroutine(FetchFilterWordsCoroutine());
}

private IEnumerator FetchFilterWordsCoroutine()
{
bool done = false;
ChatFilterResponse response = null;

yield return HttpClient.Send<object, ChatFilterResponse>(
UnityWebRequest.kHttpVerbPUT, CHAT_FILTER_URL, false, null,
res => { response = res; done = true; },
err => { done = true; });

while (!done) yield return null;

if (response?.FilterWords != null)
_filterWords = response.FilterWords;
}

public string Filter(string message, char separator = '*')
{
if (_filterWords == null || _filterWords.Length == 0)
return message;

foreach (var word in _filterWords)
{
if (!string.IsNullOrEmpty(word))
message = Regex.Replace(message, word, new string(separator, word.Length), RegexOptions.IgnoreCase);
}
return message;
}

#endregion

#region Public API

public bool IsConnected() => _chatClient?.IsConnected ?? false;
public void Subscribe(string channel, int prevMessageCount = 0) => _chatClient?.Subscribe(channel, prevMessageCount);
public void Unsubscribe(string channel) => _chatClient?.Unsubscribe(channel);
public void GetChannels() => _chatClient?.GetChannels();
public void GetPlayersOnline(string[] userIds) => _chatClient?.GetPlayersOnline(userIds);
public void SendPublicMessage(string channel, string text) => _chatClient?.SendPublicMessage(channel, text);
public void SendPrivateMessage(string targetUserId, string text) => _chatClient?.SendPrivateMessage(targetUserId, text);

#endregion
}

사용 예제

public class ChatExample : MonoBehaviour, IChatListener
{
void Start()
{
ChatManager.Instance.Connect(this);
}

public void OnConnected()
{
Debug.Log("채팅 서버 연결 성공");
// 금칙어 목록 가져오기
ChatManager.Instance.FetchFilterWords();
// 이전 채팅 내역 10개 가져오기
ChatManager.Instance.Subscribe("global", 10);
}

public void OnDisconnected()
{
Debug.Log("채팅 서버 연결 해제");
}

public void OnError(string code, string message)
{
Debug.LogError($"채팅 오류: [{code}] {message}");
}

public void OnChannels(ChatChannelInfo[] channels)
{
foreach (var ch in channels)
{
Debug.Log($"채널: {ch.channel}, 인원: {ch.count}");
}
}

public void OnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName}님이 입장했습니다.");
}

public void OnUnSubscribed(ChatUserInfo user)
{
Debug.Log($"{user.visitorName}님이 퇴장했습니다.");
}

public void OnPublicMessage(ChatUserInfo sender, string message)
{
// 금칙어 필터 적용
Debug.Log($"[{sender.visitorName}]: {ChatManager.Instance.Filter(message)}");
}

public void OnPrivateMessage(ChatUserInfo sender, string message)
{
// 금칙어 필터 적용
Debug.Log($"[귓속말][{sender.visitorName}]: {ChatManager.Instance.Filter(message)}");
}

public void OnNotifyMessage(ChatUserInfo sender, string message)
{
Debug.Log($"[알림]: {ChatManager.Instance.Filter(message)}");
}

public void OnPlayerOnline(ChatPlayerInfo[] players)
{
foreach (var p in players)
{
Debug.Log($"플레이어: {p.userUniqueId}, 온라인: {p.online}");
}
}

void SendMessage()
{
// 메시지 전송 (서버에 원본 그대로 전송)
ChatManager.Instance.SendPublicMessage("global", "안녕하세요!");
}
}