Skip to content

Commit bbd52d8

Browse files
committed
feat: websocket 리팩토링
1 parent e66cfe2 commit bbd52d8

File tree

12 files changed

+319
-232
lines changed

12 files changed

+319
-232
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using Microsoft.AspNetCore.Http;
2+
using System.Net.WebSockets;
3+
using ProjectVG.Application.Services.WebSocket;
4+
using ProjectVG.Application.Services.Session;
5+
using ProjectVG.Common.Models.Session;
6+
7+
namespace ProjectVG.Api.Middleware
8+
{
9+
public class WebSocketMiddleware
10+
{
11+
private readonly RequestDelegate _next;
12+
private readonly ILogger<WebSocketMiddleware> _logger;
13+
private readonly IWebSocketManager _webSocketService;
14+
private readonly IConnectionRegistry _connectionRegistry;
15+
private readonly IClientConnectionFactory _connectionFactory;
16+
17+
public WebSocketMiddleware(
18+
RequestDelegate next,
19+
ILogger<WebSocketMiddleware> logger,
20+
IWebSocketManager webSocketService,
21+
IConnectionRegistry connectionRegistry,
22+
IClientConnectionFactory connectionFactory)
23+
{
24+
_next = next;
25+
_logger = logger;
26+
_webSocketService = webSocketService;
27+
_connectionRegistry = connectionRegistry;
28+
_connectionFactory = connectionFactory;
29+
}
30+
31+
public async Task InvokeAsync(HttpContext context)
32+
{
33+
if (context.Request.Path == "/ws") {
34+
if (!context.WebSockets.IsWebSocketRequest) {
35+
_logger.LogWarning("WebSocket 요청이 아님");
36+
context.Response.StatusCode = 400;
37+
return;
38+
}
39+
40+
var sessionId = context.Request.Query["sessionId"].ToString();
41+
var socket = await context.WebSockets.AcceptWebSocketAsync();
42+
43+
// 1. 세션 ID 생성 (연결 등록 없이)
44+
var actualSessionId = string.IsNullOrWhiteSpace(sessionId) ?
45+
$"session_{DateTime.UtcNow.Ticks}_{Guid.NewGuid().ToString("N")[..8]}" : sessionId;
46+
47+
// 2. 기존 연결이 있다면 정리
48+
if (_connectionRegistry.TryGet(actualSessionId, out var existingConnection) && existingConnection != null) {
49+
_logger.LogInformation("기존 연결을 정리합니다: {SessionId}", actualSessionId);
50+
await _webSocketService.DisconnectAsync(actualSessionId);
51+
}
52+
53+
// 3. 연결 생성 및 등록
54+
var connection = _connectionFactory.Create(actualSessionId, socket, userId: null);
55+
_connectionRegistry.Register(actualSessionId, connection);
56+
57+
// 4. 세션 생성 (이제 연결이 등록된 상태)
58+
await _webSocketService.ConnectAsync(actualSessionId);
59+
60+
await HandleWebSocketConnection(socket, actualSessionId);
61+
return;
62+
}
63+
64+
await _next(context);
65+
}
66+
67+
private async Task HandleWebSocketConnection(WebSocket socket, string sessionId)
68+
{
69+
var buffer = new byte[1024];
70+
try {
71+
while (socket.State == WebSocketState.Open) {
72+
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
73+
74+
if (result.MessageType == WebSocketMessageType.Close) {
75+
_logger.LogInformation("WebSocket 연결 종료 요청: {SessionId}", sessionId);
76+
break;
77+
}
78+
else if (result.MessageType == WebSocketMessageType.Text) {
79+
var message = System.Text.Encoding.UTF8.GetString(buffer, 0, result.Count);
80+
await _webSocketService.HandleMessageAsync(sessionId, message);
81+
}
82+
else if (result.MessageType == WebSocketMessageType.Binary) {
83+
var data = new byte[result.Count];
84+
Array.Copy(buffer, data, result.Count);
85+
await _webSocketService.HandleBinaryMessageAsync(sessionId, data);
86+
}
87+
}
88+
}
89+
catch (Exception ex) {
90+
_logger.LogError(ex, "WebSocket 연결 유지 중 오류: {SessionId}", sessionId);
91+
}
92+
finally {
93+
_logger.LogInformation("WebSocket 연결 해제: {SessionId}", sessionId);
94+
await _webSocketService.DisconnectAsync(sessionId);
95+
}
96+
}
97+
}
98+
}

ProjectVG.Api/Program.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
using ProjectVG.Application.Middlewares;
21
using Microsoft.AspNetCore.Authentication.Negotiate;
32
using ProjectVG.Infrastructure.Integrations.LLMClient;
43
using ProjectVG.Infrastructure.Integrations.MemoryClient;
54
using ProjectVG.Application.Services.Chat;
65
using ProjectVG.Application.Services;
7-
using ProjectVG.Application.Services.Character;
86
using ProjectVG.Application.Services.Conversation;
97
using ProjectVG.Application.Services.Session;
10-
using ProjectVG.Application.Services.User;
118
using ProjectVG.Infrastructure.Persistence.Session;
129
using ProjectVG.Infrastructure.Integrations.TextToSpeechClient;
1310

@@ -16,7 +13,7 @@
1613
using ProjectVG.Api.Middleware;
1714
using ProjectVG.Api.Filters;
1815
using ProjectVG.Infrastructure.Realtime.WebSocketConnection;
19-
using ProjectVG.Application.Services.Messaging;
16+
2017
using ProjectVG.Common.Models.Session;
2118
using ProjectVG.Infrastructure.Persistence.EfCore;
2219
using Microsoft.EntityFrameworkCore;
@@ -96,7 +93,6 @@
9693

9794
builder.Services.AddSingleton<IConnectionRegistry, ConnectionRegistry>();
9895
builder.Services.AddSingleton<IClientConnectionFactory, WebSocketClientConnectionFactory>();
99-
builder.Services.AddSingleton<IMessageBroker, MessageBroker>();
10096

10197
// Infrastructure Repositories
10298
builder.Services.AddScoped<ICharacterRepository, SqlServerCharacterRepository>();
@@ -138,7 +134,7 @@
138134

139135
app.UseWebSockets();
140136

141-
// WebSocket 미들웨어를 특정 경로에만 적용
137+
// WebSocket 미들웨어 등록
142138
app.UseMiddleware<WebSocketMiddleware>();
143139

144140
app.Use(async (ctx, next) => {

ProjectVG.Application/Middlewares/WebSocketMiddleware.cs

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

ProjectVG.Application/Services/ApplicationServiceCollectionExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using ProjectVG.Application.Services.Chat.Preprocessors;
66
using ProjectVG.Application.Services.Chat.Processors;
77
using ProjectVG.Application.Services.Chat.Validators;
8+
using ProjectVG.Application.Services.WebSocket;
89

910
namespace ProjectVG.Application.Services
1011
{
@@ -28,6 +29,8 @@ public static IServiceCollection AddApplicationServices(this IServiceCollection
2829
services.AddScoped<UserInputActionProcessor>();
2930
services.AddScoped<MemoryContextPreprocessor>();
3031

32+
services.AddScoped<IWebSocketManager, WebSocketManager>();
33+
3134
return services;
3235
}
3336
}

ProjectVG.Application/Services/Chat/Processors/ChatResultProcessor.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using ProjectVG.Application.Models.Chat;
22
using ProjectVG.Application.Models.WebSocket;
33
using ProjectVG.Application.Services.Conversation;
4-
using ProjectVG.Application.Services.Messaging;
4+
using ProjectVG.Application.Services.WebSocket;
55
using ProjectVG.Infrastructure.Integrations.MemoryClient;
66
using ProjectVG.Domain.Enums;
77

@@ -12,18 +12,18 @@ public class ChatResultProcessor
1212
private readonly ILogger<ChatResultProcessor> _logger;
1313
private readonly IConversationService _conversationService;
1414
private readonly IMemoryClient _memoryClient;
15-
private readonly IMessageBroker _messageBroker;
15+
private readonly IWebSocketManager _webSocketService;
1616

1717
public ChatResultProcessor(
1818
ILogger<ChatResultProcessor> logger,
1919
IConversationService conversationService,
2020
IMemoryClient memoryClient,
21-
IMessageBroker messageBroker)
21+
IWebSocketManager webSocketService)
2222
{
2323
_logger = logger;
2424
_conversationService = conversationService;
2525
_memoryClient = memoryClient;
26-
_messageBroker = messageBroker;
26+
_webSocketService = webSocketService;
2727
}
2828

2929
public async Task PersistResultsAsync(ChatPreprocessContext context, ChatProcessResult result)
@@ -51,7 +51,7 @@ public async Task SendResultsAsync(ChatPreprocessContext context, ChatProcessRes
5151
integratedMessage.SetAudioData(segment.AudioData);
5252

5353
var wsMessage = new WebSocketMessage("chat", integratedMessage);
54-
await _messageBroker.SendWebSocketMessageAsync(context.SessionId, wsMessage);
54+
await _webSocketService.SendAsync(context.SessionId, wsMessage);
5555
}
5656

5757
_logger.LogDebug("채팅 결과 전송 완료: 세션 {SessionId}, 세그먼트 {SegmentCount}개",

ProjectVG.Application/Services/Messaging/IMessageBroker.cs

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

ProjectVG.Application/Services/Messaging/MessageBroker.cs

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

0 commit comments

Comments
 (0)