Skip to content

Commit 48f34c6

Browse files
committed
refactpory: 분산 시스템 리팩토링
1 parent 4a56676 commit 48f34c6

23 files changed

+671
-547
lines changed

ProjectVG.Api/Middleware/WebSocketMiddleware.cs

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using ProjectVG.Application.Services.Session;
2-
using ProjectVG.Application.Services.WebSocket;
32
using ProjectVG.Infrastructure.Auth;
43
using ProjectVG.Infrastructure.Realtime.WebSocketConnection;
54
using System.Net.WebSockets;
@@ -10,21 +9,21 @@ public class WebSocketMiddleware
109
{
1110
private readonly RequestDelegate _next;
1211
private readonly ILogger<WebSocketMiddleware> _logger;
13-
private readonly IWebSocketManager _webSocketService;
14-
private readonly IConnectionRegistry _connectionRegistry;
12+
private readonly ISessionManager _sessionManager;
13+
private readonly IWebSocketConnectionManager _connectionManager;
1514
private readonly IJwtProvider _jwtProvider;
1615

1716
public WebSocketMiddleware(
1817
RequestDelegate next,
1918
ILogger<WebSocketMiddleware> logger,
20-
IWebSocketManager webSocketService,
21-
IConnectionRegistry connectionRegistry,
19+
ISessionManager sessionManager,
20+
IWebSocketConnectionManager connectionManager,
2221
IJwtProvider jwtProvider)
2322
{
2423
_next = next;
2524
_logger = logger;
26-
_webSocketService = webSocketService;
27-
_connectionRegistry = connectionRegistry;
25+
_sessionManager = sessionManager;
26+
_connectionManager = connectionManager;
2827
_jwtProvider = jwtProvider;
2928
}
3029

@@ -88,19 +87,43 @@ private string ExtractToken(HttpContext context)
8887
return string.Empty;
8988
}
9089

91-
/// <summary>
92-
/// 기존 연결 정리 후 새 연결 등록
90+
/// <summary>
91+
/// 새 아키텍처: 세션 관리와 WebSocket 연결 관리 분리
9392
/// </summary>
9493
private async Task RegisterConnection(Guid userId, WebSocket socket)
9594
{
96-
if (_connectionRegistry.TryGet(userId.ToString(), out var existing) && existing != null) {
97-
_logger.LogInformation("기존 연결 정리: {UserId}", userId);
98-
await _webSocketService.DisconnectAsync(userId.ToString());
99-
}
95+
var userIdString = userId.ToString();
96+
_logger.LogInformation("[WebSocketMiddleware] 연결 등록 시작: UserId={UserId}", userId);
97+
98+
try
99+
{
100+
// 기존 로컬 연결이 있으면 정리
101+
if (_connectionManager.HasLocalConnection(userIdString))
102+
{
103+
_logger.LogInformation("[WebSocketMiddleware] 기존 로컬 연결 발견 - 정리 중: UserId={UserId}", userId);
104+
_connectionManager.UnregisterConnection(userIdString);
105+
}
106+
107+
// 1. 세션 관리자에 세션 생성 (Redis 저장)
108+
await _sessionManager.CreateSessionAsync(userId);
109+
_logger.LogInformation("[WebSocketMiddleware] 세션 관리자에 세션 저장 완료: UserId={UserId}", userId);
100110

101-
var connection = new WebSocketClientConnection(userId.ToString(), socket);
102-
_connectionRegistry.Register(userId.ToString(), connection);
103-
await _webSocketService.ConnectAsync(userId.ToString());
111+
// 2. WebSocket 연결 관리자에 로컬 연결 등록
112+
var connection = new WebSocketClientConnection(userIdString, socket);
113+
_connectionManager.RegisterConnection(userIdString, connection);
114+
_logger.LogInformation("[WebSocketMiddleware] 로컬 WebSocket 연결 등록 완료: UserId={UserId}", userId);
115+
116+
// [디버그] 등록 후 상태 확인
117+
var isSessionActive = await _sessionManager.IsSessionActiveAsync(userId);
118+
var hasLocalConnection = _connectionManager.HasLocalConnection(userIdString);
119+
_logger.LogInformation("[WebSocketMiddleware] 연결 등록 완료: UserId={UserId}, SessionActive={SessionActive}, LocalConnection={LocalConnection}",
120+
userId, isSessionActive, hasLocalConnection);
121+
}
122+
catch (Exception ex)
123+
{
124+
_logger.LogError(ex, "[WebSocketMiddleware] 연결 등록 실패: UserId={UserId}", userId);
125+
throw;
126+
}
104127
}
105128

106129
/// <summary>
@@ -163,7 +186,10 @@ await socket.SendAsync(
163186

164187
// 세션 하트비트 업데이트 (Redis TTL 갱신)
165188
try {
166-
await _webSocketService.UpdateSessionHeartbeatAsync(userId);
189+
if (Guid.TryParse(userId, out var userGuid))
190+
{
191+
await _sessionManager.UpdateSessionHeartbeatAsync(userGuid);
192+
}
167193
}
168194
catch (Exception heartbeatEx) {
169195
_logger.LogWarning(heartbeatEx, "세션 하트비트 업데이트 실패: {UserId}", userId);
@@ -185,14 +211,25 @@ await socket.SendAsync(
185211
_logger.LogInformation("WebSocket 연결 해제: {UserId}", userId);
186212

187213
try {
188-
await _webSocketService.DisconnectAsync(userId);
189-
_connectionRegistry.Unregister(userId);
214+
// 새 아키텍처: 세션과 로컬 연결 분리해서 정리
215+
if (Guid.TryParse(userId, out var userGuid))
216+
{
217+
// 1. 세션 관리자에서 세션 삭제 (Redis에서 제거)
218+
await _sessionManager.DeleteSessionAsync(userGuid);
219+
_logger.LogDebug("세션 관리자에서 세션 삭제 완료: {UserId}", userId);
220+
}
221+
222+
// 2. 로컬 WebSocket 연결 해제
223+
_connectionManager.UnregisterConnection(userId);
224+
_logger.LogDebug("로컬 WebSocket 연결 해제 완료: {UserId}", userId);
190225

226+
// 3. WebSocket 소켓 정리
191227
if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived) {
192228
await socket.CloseAsync(
193229
WebSocketCloseStatus.NormalClosure,
194230
"Connection closed",
195231
CancellationToken.None);
232+
_logger.LogDebug("WebSocket 소켓 정리 완료: {UserId}", userId);
196233
}
197234
}
198235
catch (Exception ex) {
Lines changed: 39 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,95 @@
1-
using Microsoft.Extensions.DependencyInjection;
21
using Microsoft.Extensions.Configuration;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Microsoft.Extensions.Logging;
34
using ProjectVG.Application.Services.Auth;
45
using ProjectVG.Application.Services.Character;
56
using ProjectVG.Application.Services.Chat;
67
using ProjectVG.Application.Services.Chat.CostTracking;
8+
using ProjectVG.Application.Services.Chat.Handlers;
79
using ProjectVG.Application.Services.Chat.Preprocessors;
810
using ProjectVG.Application.Services.Chat.Processors;
911
using ProjectVG.Application.Services.Chat.Validators;
10-
using ProjectVG.Application.Services.Chat.Handlers;
1112
using ProjectVG.Application.Services.Conversation;
12-
using ProjectVG.Application.Services.Session;
1313
using ProjectVG.Application.Services.Credit;
14-
using ProjectVG.Application.Services.Users;
15-
using ProjectVG.Application.Services.WebSocket;
1614
using ProjectVG.Application.Services.MessageBroker;
1715
using ProjectVG.Application.Services.Server;
16+
using ProjectVG.Application.Services.Session;
17+
using ProjectVG.Application.Services.Users;
18+
using ProjectVG.Infrastructure.Persistence.Session;
19+
using System;
1820

1921
namespace ProjectVG.Application
2022
{
2123
public static class ApplicationServiceCollectionExtensions
2224
{
2325
public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration configuration)
2426
{
25-
// Auth Services
27+
AddAuthServices(services);
28+
AddDomainServices(services);
29+
AddChatServices(services);
30+
AddDistributedServices(services, configuration);
31+
32+
return services;
33+
}
34+
35+
private static void AddAuthServices(IServiceCollection services)
36+
{
2637
services.AddScoped<IAuthService, AuthService>();
2738
services.AddScoped<IOAuth2AuthService, OAuth2AuthService>();
2839
services.AddScoped<IOAuth2Service, OAuth2Service>();
2940
services.AddScoped<IOAuth2CodeValidator, OAuth2CodeValidator>();
3041
services.AddScoped<IOAuth2UserService, OAuth2UserService>();
3142
services.AddScoped<IOAuth2AccountManager, OAuth2AccountManager>();
3243
services.AddScoped<IOAuth2ProviderFactory, OAuth2ProviderFactory>();
44+
}
3345

34-
// User Services
46+
private static void AddDomainServices(IServiceCollection services)
47+
{
3548
services.AddScoped<IUserService, UserService>();
36-
37-
// Character Services
3849
services.AddScoped<ICharacterService, CharacterService>();
39-
40-
// Credit Management Services
4150
services.AddScoped<ICreditManagementService, CreditManagementService>();
51+
services.AddScoped<IConversationService, ConversationService>();
52+
}
4253

43-
// Chat Services - Core
54+
private static void AddChatServices(IServiceCollection services)
55+
{
4456
services.AddScoped<IChatService, ChatService>();
4557
services.AddScoped<IChatMetricsService, ChatMetricsService>();
46-
47-
services.AddScoped<ICharacterService, CharacterService>();
48-
services.AddScoped<IUserService, UserService>();
49-
50-
// Chat Services - Validators
5158
services.AddScoped<ChatRequestValidator>();
52-
53-
// Chat Services - Preprocessors
59+
5460
services.AddScoped<MemoryContextPreprocessor>();
5561
services.AddScoped<UserInputAnalysisProcessor>();
56-
57-
// Chat Services - Processors
62+
5863
services.AddScoped<UserInputActionProcessor>();
5964
services.AddScoped<ChatLLMProcessor>();
6065
services.AddScoped<ChatTTSProcessor>();
6166
services.AddScoped<ChatResultProcessor>();
6267

63-
// Chat Services - Handlers
6468
services.AddScoped<ChatSuccessHandler>();
6569
services.AddScoped<ChatFailureHandler>();
66-
67-
// Chat Services - Cost Tracking Decorators
70+
6871
services.AddCostTrackingDecorator<UserInputAnalysisProcessor>("UserInputAnalysis");
6972
services.AddCostTrackingDecorator<ChatLLMProcessor>("ChatLLM");
7073
services.AddCostTrackingDecorator<ChatTTSProcessor>("ChatTTS");
71-
72-
// Conversation Services
73-
services.AddScoped<IConversationService, ConversationService>();
74-
75-
// Distributed System Services
76-
AddDistributedServices(services, configuration);
77-
78-
return services;
7974
}
8075

81-
/// <summary>
82-
/// 분산 시스템 관련 서비스 등록
83-
/// </summary>
8476
private static void AddDistributedServices(IServiceCollection services, IConfiguration configuration)
8577
{
86-
var distributedEnabled = configuration.GetValue<bool>("DistributedSystem:Enabled", false);
78+
services.AddSingleton<IMessageBroker, DistributedMessageBroker>();
8779

88-
if (distributedEnabled)
80+
services.AddSingleton<ISessionManager>(serviceProvider =>
8981
{
90-
// 분산 환경 서비스
91-
services.AddSingleton<IMessageBroker, DistributedMessageBroker>();
92-
services.AddSingleton<IWebSocketManager, DistributedWebSocketManager>();
93-
}
94-
else
95-
{
96-
// 단일 서버 환경 서비스
97-
services.AddSingleton<IMessageBroker, LocalMessageBroker>();
98-
services.AddSingleton<IWebSocketManager, WebSocketManager>();
99-
}
82+
var sessionStorage = serviceProvider.GetService<ISessionStorage>();
83+
var logger = serviceProvider.GetRequiredService<ILogger<RedisSessionManager>>();
84+
return new RedisSessionManager(sessionStorage, logger);
85+
});
86+
87+
AddWebSocketConnectionServices(services);
88+
}
10089

101-
// WebSocket 연결 관리
102-
services.AddSingleton<IConnectionRegistry, ConnectionRegistry>();
90+
private static void AddWebSocketConnectionServices(IServiceCollection services)
91+
{
92+
services.AddSingleton<IWebSocketConnectionManager, WebSocketConnectionManager>();
10393
}
10494
}
10595
}

ProjectVG.Application/Services/Chat/ChatService.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using ProjectVG.Application.Services.Chat.Processors;
88
using ProjectVG.Application.Services.Chat.Validators;
99
using ProjectVG.Application.Services.Conversation;
10-
using ProjectVG.Application.Services.WebSocket;
1110

1211
namespace ProjectVG.Application.Services.Chat
1312
{
@@ -19,7 +18,6 @@ public class ChatService : IChatService
1918

2019
private readonly IConversationService _conversationService;
2120
private readonly ICharacterService _characterService;
22-
private readonly IWebSocketManager _webSocketManager;
2321

2422
private readonly ChatRequestValidator _validator;
2523
private readonly MemoryContextPreprocessor _memoryPreprocessor;
@@ -38,7 +36,6 @@ public ChatService(
3836
ILogger<ChatService> logger,
3937
IConversationService conversationService,
4038
ICharacterService characterService,
41-
IWebSocketManager webSocketManager,
4239
ChatRequestValidator validator,
4340
MemoryContextPreprocessor memoryPreprocessor,
4441
ICostTrackingDecorator<UserInputAnalysisProcessor> inputProcessor,
@@ -55,7 +52,6 @@ ChatFailureHandler chatFailureHandler
5552

5653
_conversationService = conversationService;
5754
_characterService = characterService;
58-
_webSocketManager = webSocketManager;
5955
_validator = validator;
6056
_memoryPreprocessor = memoryPreprocessor;
6157
_inputProcessor = inputProcessor;

ProjectVG.Application/Services/Chat/Handlers/ChatFailureHandler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,27 @@
11
using ProjectVG.Application.Models.Chat;
22
using ProjectVG.Application.Models.WebSocket;
3-
using ProjectVG.Application.Services.WebSocket;
3+
using ProjectVG.Application.Services.MessageBroker;
44

55
namespace ProjectVG.Application.Services.Chat.Handlers
66
{
77
public class ChatFailureHandler
88
{
99
private readonly ILogger<ChatFailureHandler> _logger;
10-
private readonly IWebSocketManager _webSocketService;
10+
private readonly IMessageBroker _messageBroker;
1111

1212
public ChatFailureHandler(
1313
ILogger<ChatFailureHandler> logger,
14-
IWebSocketManager webSocketService)
14+
IMessageBroker messageBroker)
1515
{
1616
_logger = logger;
17-
_webSocketService = webSocketService;
17+
_messageBroker = messageBroker;
1818
}
1919

2020
public async Task HandleAsync(ChatProcessContext context)
2121
{
2222
try {
2323
var errorResponse = new WebSocketMessage("fail", "");
24-
await _webSocketService.SendAsync(context.UserId.ToString(), errorResponse);
24+
await _messageBroker.SendToUserAsync(context.UserId.ToString(), errorResponse);
2525
}
2626
catch (Exception ex) {
2727
_logger.LogError(ex, "오류 메시지 전송 실패: 세션 {UserId}", context.RequestId);

ProjectVG.Application/Services/Chat/Handlers/ChatSuccessHandler.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using ProjectVG.Application.Models.Chat;
22
using ProjectVG.Application.Models.WebSocket;
3-
using ProjectVG.Application.Services.WebSocket;
43
using ProjectVG.Application.Services.Credit;
54
using ProjectVG.Application.Services.MessageBroker;
65

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using ProjectVG.Application.Models.Chat;
22
using ProjectVG.Application.Services.Conversation;
3-
using ProjectVG.Application.Services.WebSocket;
3+
using ProjectVG.Application.Services.MessageBroker;
44
using ProjectVG.Infrastructure.Integrations.MemoryClient;
55
using ProjectVG.Domain.Entities.ConversationHistorys;
66
using ProjectVG.Infrastructure.Integrations.MemoryClient.Models;
@@ -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 IWebSocketManager _webSocketService;
15+
private readonly IMessageBroker _messageBroker;
1616

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

2929
public async Task PersistResultsAsync(ChatProcessContext context)

0 commit comments

Comments
 (0)