Skip to content

Commit 00a629c

Browse files
committed
feat: web websocket 연결 구현 검증
1 parent cb4cee5 commit 00a629c

17 files changed

+184
-90
lines changed

Assets/Infrastructure/Network/Http/HttpApiClient.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,15 @@ private void SetupDefaultHeaders()
228228
{
229229
defaultHeaders.Clear();
230230
defaultHeaders["Content-Type"] = NetworkConfig.ContentType;
231+
232+
#if !UNITY_WEBGL || UNITY_EDITOR
231233
defaultHeaders["User-Agent"] = NetworkConfig.UserAgent;
234+
#else
235+
// WebGL에서는 커스텀 헤더로 클라이언트 식별
236+
defaultHeaders["X-Client-Type"] = "ProjectVG-WebGL";
237+
defaultHeaders["X-Client-Version"] = Application.version;
238+
#endif
239+
232240
defaultHeaders["Accept"] = ACCEPT_HEADER;
233241
}
234242

Assets/Infrastructure/Network/WebSocket/INativeWebSocket.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@ public interface INativeWebSocket : IDisposable
2525

2626
// 메시지 전송
2727
UniTask<bool> SendMessageAsync(string message, CancellationToken cancellationToken = default);
28+
29+
// 메시지 큐 처리 (NativeWebSocket 패키지용)
30+
void DispatchMessageQueue();
2831
}
2932
}

Assets/Infrastructure/Network/WebSocket/Platforms/DesktopWebSocket.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ private async Task ReceiveLoopAsync()
160160
}
161161
}
162162

163+
public void DispatchMessageQueue()
164+
{
165+
// DesktopWebSocket은 네이티브 소켓을 사용하므로 메시지 큐 처리 불필요
166+
}
167+
163168
public void Dispose()
164169
{
165170
if (_isDisposed)

Assets/Infrastructure/Network/WebSocket/Platforms/MobileWebSocket.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ private async Task ReceiveLoopAsync()
158158
}
159159
}
160160

161+
public void DispatchMessageQueue()
162+
{
163+
// MobileWebSocket은 네이티브 소켓을 사용하므로 메시지 큐 처리 불필요
164+
}
165+
161166
public void Dispose()
162167
{
163168
if (_isDisposed)

Assets/Infrastructure/Network/WebSocket/Platforms/WebGLWebSocket.cs

Lines changed: 112 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,25 @@
22
using System.Threading;
33
using UnityEngine;
44
using Cysharp.Threading.Tasks;
5-
using UnityEngine.Networking;
5+
using NativeWebSocket;
66

77
namespace ProjectVG.Infrastructure.Network.WebSocket.Platforms
88
{
99
/// <summary>
1010
/// WebGL 플랫폼용 WebSocket 구현체
11-
/// UnityWebRequest.WebSocket을 사용합니다.
11+
/// NativeWebSocket 패키지를 사용합니다.
1212
/// </summary>
1313
public class WebGLWebSocket : INativeWebSocket
1414
{
15-
public bool IsConnected { get; private set; }
16-
public bool IsConnecting { get; private set; }
15+
public bool IsConnected => _webSocket?.State == WebSocketState.Open;
16+
public bool IsConnecting => _webSocket?.State == WebSocketState.Connecting;
1717

1818
public event Action OnConnected;
1919
public event Action OnDisconnected;
2020
public event Action<string> OnError;
21-
#pragma warning disable CS0067
2221
public event Action<string> OnMessageReceived;
23-
#pragma warning restore CS0067
2422

25-
private UnityWebRequest _webRequest;
23+
private NativeWebSocket.WebSocket _webSocket;
2624
private CancellationTokenSource _cancellationTokenSource;
2725
private bool _isDisposed = false;
2826

@@ -38,42 +36,36 @@ public async UniTask<bool> ConnectAsync(string url, CancellationToken cancellati
3836
return IsConnected;
3937
}
4038

41-
IsConnecting = true;
39+
if (_isDisposed)
40+
{
41+
Debug.LogError("[WebGL WebSocket] Cannot connect: WebSocket is disposed");
42+
return false;
43+
}
4244

4345
try
4446
{
45-
var combinedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _cancellationTokenSource.Token).Token;
46-
47-
// UnityWebRequest.WebSocket 사용
48-
_webRequest = UnityWebRequest.Get(url);
49-
_webRequest.SetRequestHeader("Upgrade", "websocket");
50-
_webRequest.SetRequestHeader("Connection", "Upgrade");
51-
52-
var operation = _webRequest.SendWebRequest();
53-
await operation.WithCancellation(combinedCancellationToken);
47+
var wsUrl = url.Replace("http://", "ws://").Replace("https://", "wss://");
48+
Debug.Log($"[WebGL WebSocket] 연결 시도: {wsUrl}");
5449

55-
if (_webRequest.result == UnityWebRequest.Result.Success)
56-
{
57-
IsConnected = true;
58-
IsConnecting = false;
59-
OnConnected?.Invoke();
60-
61-
// 메시지 수신 루프 시작
62-
_ = ReceiveLoopAsync();
63-
64-
return true;
65-
}
66-
else
67-
{
68-
var error = $"WebGL WebSocket 연결 실패: {_webRequest.error}";
69-
Debug.LogError(error);
70-
OnError?.Invoke(error);
71-
return false;
72-
}
50+
_webSocket = new NativeWebSocket.WebSocket(wsUrl);
51+
52+
_webSocket.OnOpen += OnNativeConnected;
53+
_webSocket.OnMessage += OnNativeMessageReceived;
54+
_webSocket.OnError += OnNativeError;
55+
_webSocket.OnClose += OnNativeDisconnected;
56+
57+
await _webSocket.Connect();
58+
59+
Debug.Log("[WebGL WebSocket] 연결 성공");
60+
return true;
61+
}
62+
catch (OperationCanceledException)
63+
{
64+
Debug.Log("[WebGL WebSocket] 연결이 취소되었습니다.");
65+
return false;
7366
}
7467
catch (Exception ex)
7568
{
76-
IsConnecting = false;
7769
var error = $"WebGL WebSocket 연결 중 예외 발생: {ex.Message}";
7870
Debug.LogError(error);
7971
OnError?.Invoke(error);
@@ -83,88 +75,136 @@ public async UniTask<bool> ConnectAsync(string url, CancellationToken cancellati
8375

8476
public async UniTask DisconnectAsync()
8577
{
86-
if (!IsConnected)
78+
if (!IsConnected && !IsConnecting)
8779
{
8880
return;
8981
}
9082

9183
try
9284
{
93-
IsConnected = false;
94-
IsConnecting = false;
95-
96-
_webRequest?.Abort();
97-
_webRequest?.Dispose();
98-
_webRequest = null;
99-
100-
await UniTask.CompletedTask; // 비동기 작업 시뮬레이션
85+
if (_webSocket != null && _webSocket.State == WebSocketState.Open)
86+
{
87+
await _webSocket.Close();
88+
}
10189

102-
OnDisconnected?.Invoke();
90+
Debug.Log("[WebGL WebSocket] 연결 해제됨");
10391
}
10492
catch (Exception ex)
10593
{
106-
Debug.LogError($"WebGL WebSocket 연결 해제 중 오류: {ex.Message}");
94+
Debug.LogError($"[WebGL WebSocket] 연결 해제 중 오류: {ex.Message}");
10795
}
10896
}
10997

11098
public async UniTask<bool> SendMessageAsync(string message, CancellationToken cancellationToken = default)
11199
{
112100
if (!IsConnected)
113101
{
114-
Debug.LogWarning("WebGL WebSocket이 연결되지 않았습니다.");
102+
Debug.LogWarning("[WebGL WebSocket] 연결되지 않은 상태에서 메시지 전송 시도");
103+
return false;
104+
}
105+
106+
if (_isDisposed)
107+
{
108+
Debug.LogError("[WebGL WebSocket] WebSocket이 해제된 상태입니다.");
115109
return false;
116110
}
117111

118112
try
119113
{
120-
// TODO : WebGL에서는 WebSocket 메시지 전송을 위한 별도 구현 필요
121-
await UniTask.CompletedTask;
122-
return true;
114+
if (_webSocket != null && _webSocket.State == WebSocketState.Open)
115+
{
116+
await _webSocket.SendText(message);
117+
Debug.Log($"[WebGL WebSocket] 메시지 전송 성공: {message.Substring(0, Math.Min(message.Length, 50))}...");
118+
return true;
119+
}
120+
else
121+
{
122+
Debug.LogWarning("[WebGL WebSocket] WebSocket이 연결되지 않았습니다.");
123+
return false;
124+
}
123125
}
124126
catch (Exception ex)
125127
{
126-
Debug.LogError($"WebGL WebSocket 메시지 전송 실패: {ex.Message}");
128+
Debug.LogError($"[WebGL WebSocket] 메시지 전송 실패: {ex.Message}");
127129
return false;
128130
}
129131
}
130132

131-
private async UniTask ReceiveLoopAsync()
133+
private void OnNativeConnected()
132134
{
135+
if (_isDisposed) return;
136+
137+
Debug.Log($"[WebGL WebSocket] 연결 성공! Platform: {Application.platform}, IsWebGL: {Application.platform == RuntimePlatform.WebGLPlayer}");
138+
OnConnected?.Invoke();
139+
}
140+
141+
private void OnNativeMessageReceived(byte[] data)
142+
{
143+
if (_isDisposed) return;
144+
133145
try
134146
{
135-
while (IsConnected && !_isDisposed)
136-
{
137-
// TODO : WebGL에서는 WebSocket 메시지 수신을 위한 별도 구현 필요
138-
await UniTask.Delay(100);
139-
}
147+
var message = System.Text.Encoding.UTF8.GetString(data);
148+
Debug.Log($"[WebGL WebSocket] 메시지 수신! Platform: {Application.platform}");
149+
Debug.Log($"[WebGL WebSocket] 데이터 크기: {data.Length} bytes");
150+
Debug.Log($"[WebGL WebSocket] 메시지 내용: {message.Substring(0, Math.Min(message.Length, 200))}...");
151+
OnMessageReceived?.Invoke(message);
140152
}
141153
catch (Exception ex)
142154
{
143-
if (!_isDisposed)
144-
{
145-
Debug.LogError($"WebGL WebSocket 수신 루프 오류: {ex.Message}");
146-
OnError?.Invoke(ex.Message);
147-
}
148-
}
149-
finally
150-
{
151-
IsConnected = false;
152-
if (!_isDisposed)
153-
{
154-
OnDisconnected?.Invoke();
155-
}
155+
Debug.LogError($"[WebGL WebSocket] 메시지 처리 중 오류: {ex.Message}");
156156
}
157157
}
158158

159+
private void OnNativeError(string error)
160+
{
161+
if (_isDisposed) return;
162+
163+
Debug.LogError($"[WebGL WebSocket] 오류 발생: {error}");
164+
OnError?.Invoke(error);
165+
}
166+
167+
private void OnNativeDisconnected(WebSocketCloseCode closeCode)
168+
{
169+
if (_isDisposed) return;
170+
171+
Debug.Log($"[WebGL WebSocket] 연결 해제됨 - Code: {closeCode}");
172+
OnDisconnected?.Invoke();
173+
}
174+
175+
public void DispatchMessageQueue()
176+
{
177+
#if !UNITY_WEBGL || UNITY_EDITOR
178+
_webSocket?.DispatchMessageQueue();
179+
Debug.Log($"[WebGL WebSocket] DispatchMessageQueue 호출됨 - Platform: {Application.platform}");
180+
#else
181+
Debug.Log($"[WebGL WebSocket] WebGL에서는 DispatchMessageQueue 생략 - Platform: {Application.platform}");
182+
#endif
183+
}
184+
159185
public void Dispose()
160186
{
161187
if (_isDisposed)
162188
return;
163189

164190
_isDisposed = true;
191+
192+
if (_webSocket != null)
193+
{
194+
_webSocket.OnOpen -= OnNativeConnected;
195+
_webSocket.OnMessage -= OnNativeMessageReceived;
196+
_webSocket.OnError -= OnNativeError;
197+
_webSocket.OnClose -= OnNativeDisconnected;
198+
199+
if (_webSocket.State == WebSocketState.Open)
200+
{
201+
_webSocket.Close();
202+
}
203+
_webSocket = null;
204+
}
205+
165206
_cancellationTokenSource?.Cancel();
166207
_cancellationTokenSource?.Dispose();
167-
_webRequest?.Dispose();
168208
}
169209
}
170210
}

Assets/Infrastructure/Network/WebSocket/WebSocketFactory.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ public static INativeWebSocket CreateWebSocket()
3434
Debug.Log($"[WebSocketFactory] 데스크톱 플랫폼 ({Application.platform}): DesktopWebSocket 사용");
3535
return new DesktopWebSocket();
3636

37+
case RuntimePlatform.WebGLPlayer:
38+
Debug.Log($"[WebSocketFactory] WebGL 플랫폼: WebGLWebSocket 사용");
39+
return new WebGLWebSocket();
40+
3741
default:
3842
Debug.LogWarning($"[WebSocketFactory] 지원되지 않는 플랫폼 ({Application.platform}): DesktopWebSocket 사용");
3943
return new DesktopWebSocket();
@@ -51,6 +55,8 @@ public static INativeWebSocket CreateWebSocket(WebSocketType type)
5155
return new DesktopWebSocket();
5256
case WebSocketType.Mobile:
5357
return new MobileWebSocket();
58+
case WebSocketType.WebGL:
59+
return new WebGLWebSocket();
5460
default:
5561
return CreateWebSocket();
5662
}
@@ -63,7 +69,8 @@ public enum WebSocketType
6369
{
6470
Auto,
6571
Desktop,
66-
Mobile
72+
Mobile,
73+
WebGL
6774
}
6875
}
6976
}

Assets/Infrastructure/Network/WebSocket/WebSocketManager.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ protected override void Awake()
5555
_tokenRefreshService = TokenRefreshService.Instance;
5656
}
5757

58+
private void Update()
59+
{
60+
#if !UNITY_WEBGL || UNITY_EDITOR
61+
// NativeWebSocket의 메시지 큐 처리 (WebGL 제외)
62+
_nativeWebSocket?.DispatchMessageQueue();
63+
#endif
64+
}
65+
5866
private void OnDestroy()
5967
{
6068
Shutdown();

Assets/Resources/PerformanceTestRunInfo.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

Assets/Resources/PerformanceTestRunInfo.json.meta

Lines changed: 0 additions & 7 deletions
This file was deleted.

Assets/Resources/PerformanceTestRunSettings.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)