-
Notifications
You must be signed in to change notification settings - Fork 0
Pref : 비동기 작업 최적화 #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Task.Run 내부에 예외 처리 추가로 관찰되지 않은 태스크 예외 방지 ConfigureAwait(false) 적용으로 컨텍스트 스위칭 최적화
중복된 예외 처리 제거 및 코드 정리 LogChatRequestCommand, LogChatProcessContext 메서드 삭제 메트릭스 서비스에서 이미 로깅 제공으로 중복 방지
|
Caution Review failedThe pull request is closed. Walkthrough여러 서비스와 인프라 파일에서 내부 로깅 호출 제거 및 비동기 await 관련 ConfigureAwait 제거(동기화 컨텍스트 캡처 변경), WebSocket 미들웨어의 멀티프레임 메시지 조립·핑/퐁 핸들링·바이너리 프레임 처리 추가, 토큰 저장 실패 시 예외 처리와 채팅 요청 토큰 잔액 검증이 도입되었습니다. 공개 API 서명은 변경되지 않았습니다. Changes
Sequence Diagram(s)sequenceDiagram
%% WebSocket: 멀티프레임 수집 + ping/pong 처리 (간략)
autonumber
actor Client
participant WS as WebSocketMiddleware
participant App as ApplicationHandler
Client->>WS: send frame(chunk1)
WS->>WS: append to MemoryStream (not EndOfMessage)
Client->>WS: send frame(chunkN, EndOfMessage)
WS->>WS: assemble full message
alt message == "ping" or contains {"type":"ping"}
WS->>Client: send {"type":"pong"}
else message is text
WS->>App: Invoke handler with full text message
App-->>WS: response / ack
else message is binary
WS->>WS: log and skip processing
end
Client->>WS: Close frame
WS->>Client: CloseAsync (cleanup)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
ProjectVG.Application/Services/Chat/ChatService.cs (7)
74-75: Task.Run 백그라운드 작업: 예외 관찰 및 취소 불가fire-and-forget로 반환 Task를 버리면 초기 예외(예: 스코프 생성 전) 로깅 누락 가능, 취소도 불가. 최소한 Task를 캡처해 Faulted 시 로깅하세요. 장기적으로는 IBackgroundTaskQueue/HostedService 패턴 전환 권장.
적용 예시:
- _ = Task.Run(async () => { - await ProcessChatRequestInternalAsync(preprocessContext).ConfigureAwait(false); - }); + var task = Task.Run(() => ProcessChatRequestInternalAsync(preprocessContext)); + _ = task.ContinueWith( + t => _logger.LogError(t.Exception, "비동기 채팅 처리 실패: 요청 {RequestId}", command.Id), + TaskContinuationOptions.OnlyOnFaulted);
110-112: ASP.NET Core에서는 ConfigureAwait(false) 실익 제한적 — 스타일 일관성 필요ASP.NET Core는 SynchronizationContext가 없어 체감 이득이 거의 없습니다. 유지할지 제거할지 팀 규칙을 정하고 일관 적용하세요(가독성 vs. 규칙).
가독성 선호 시:
- await _llmProcessor.ProcessAsync(context).ConfigureAwait(false); - await _ttsProcessor.ProcessAsync(context).ConfigureAwait(false); + await _llmProcessor.ProcessAsync(context); + await _ttsProcessor.ProcessAsync(context);
116-118: 전송(Handle) → 영속화(Persist) 순서 검토저장 실패 시 이미 사용자에게 결과가 송신되어 불일치가 발생할 수 있습니다. 비즈니스 의도라면 유지, 아니라면 저장 선행 또는 저장 실패시 보정/리트라이 정책을 고려하세요.
121-121: Failure 경로의 ConfigureAwait(false) 및 예외 처리 의도 확인FailureHandler 내부에서 예외를 로깅만 하고 흡수합니다. 장애 추적/알림 관점에서 충분한지 확인 바랍니다. ConfigureAwait(false) 역시 스타일 일관성 원칙에 맞춰 정리 권장.
29-31: 미사용 필드/생성자 인자 정리: _resultProcessor, _chatFailureHandler메서드 내에서 스코프를 통해 핸들러/프로세서를 해석하므로, 이 필드/인자들이 사용되지 않습니다. 혼선을 줄이기 위해 제거를 권장합니다.
예시:
- private readonly ChatResultProcessor _resultProcessor; - private readonly ChatFailureHandler _chatFailureHandler; ... - ICostTrackingDecorator<ChatTTSProcessor> ttsProcessor, - ChatResultProcessor resultProcessor, + ICostTrackingDecorator<ChatTTSProcessor> ttsProcessor, ... - ChatFailureHandler chatFailureHandler + /* removed: ChatFailureHandler chatFailureHandler */ ) { ... - _resultProcessor = resultProcessor; - _chatFailureHandler = chatFailureHandler; + /* removed unused assignments */Also applies to: 45-46, 61-63
108-115: 스코프 혼용 확인성공/결과/실패 핸들러는 새 scope에서, LLM/TTS 프로세서는 기존 주입 인스턴스를 사용합니다. 서비스 라이프타임(Scoped/Singleton) 정책과 일치하는지 확인해 주세요. 필요 시 전부 동일 scope에서 해석하도록 정렬 권장.
67-72: 검증/전처리 단계 예외 시 메트릭 종료 누락 가능Start는 Enqueue에서, End/Log는 내부 처리 finally에서 호출됩니다. Validate/Prepare에서 예외가 나면 End/Log가 실행되지 않을 수 있습니다. 가드 로직을 추가하거나, 메트릭 수명 관리를 한 곳으로 모으는 것을 권장합니다.
Also applies to: 124-126
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
ProjectVG.Application/Services/Chat/ChatService.cs(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ProjectVG.Application/Services/Chat/ChatService.cs (3)
ProjectVG.Application/Services/Chat/Handlers/ChatSuccessHandler.cs (2)
ChatSuccessHandler(9-122)ChatSuccessHandler(15-23)ProjectVG.Application/Services/Chat/Processors/ChatResultProcessor.cs (2)
ChatResultProcessor(10-100)ChatResultProcessor(17-27)ProjectVG.Application/Services/Chat/Handlers/ChatFailureHandler.cs (2)
ChatFailureHandler(7-30)ChatFailureHandler(12-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build & Test
불필요한 ConfigureAwait(false) 호출 제거로 코드 가독성 개선 - WebSocketMiddleware.cs - AuthService.cs - ChatService.cs - ChatRequestValidator.cs - TokenService.cs - SqlServerUserRepository.cs ASP.NET Core에서는 SynchronizationContext가 없어 ConfigureAwait(false)의 이점이 제한적 코드 일관성 및 가독성 향상을 위해 제거
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
ProjectVG.Application/Services/Chat/Validators/ChatRequestValidator.cs (1)
79-97: 세션 스토리지 오류 시 ‘검증 통과(fail-open)’는 보안 리스크스토리지 장애가 곧바로 인증/세션 우회를 허용할 수 있습니다. 기본은 실패-폐쇄(fail-closed)로 전환하고, 필요 시 플래그/설정으로만 fail-open 허용을 권장합니다.
대안 A(권장, 기본 fail-closed):
- catch (Exception ex) { - _logger.LogError(ex, "세션 검증 중 예상치 못한 오류: {UserId}", userId); - // 세션 스토리지 오류 시에는 검증을 통과시키되 로그는 남김 (서비스 가용성 우선) - _logger.LogWarning("세션 스토리지 오류로 인해 세션 검증을 건너뜁니다: {UserId}", userId); - } + catch (Exception ex) { + _logger.LogError(ex, "세션 검증 중 오류: {UserId}", userId); + throw new ValidationException(ErrorCode.SESSION_EXPIRED, "세션 검증 실패. 다시 로그인해 주세요."); + }대안 B(플래그로 제어):
- IConfiguration/IOptions에
AllowFailOpenOnSessionError추가(기본 false).- catch 블록에서 플래그가 true일 때만 현재 동작 유지.
ProjectVG.Api/Middleware/WebSocketMiddleware.cs (1)
130-151: 대용량 메시지 무제한 수신으로 인한 메모리 DoS 가능성프레임 결합 시 상한이 없어 MemoryStream이 무제한 성장할 수 있습니다. 합리적 상한(예: 1–4MB)으로 차단 권장.
+ // 메시지 최대 크기(바이트) - 필요시 설정화 + private const int MaxMessageBytes = 1 * 1024 * 1024; ... while (socket.State == WebSocketState.Open && !cancellationTokenSource.Token.IsCancellationRequested) { WebSocketReceiveResult result; using var ms = new MemoryStream(); do { - result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationTokenSource.Token); + result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationTokenSource.Token); if (result.MessageType == WebSocketMessageType.Close) { _logger.LogInformation("연결 종료 요청: {UserId}", userId); break; } - ms.Write(buffer, 0, result.Count); + ms.Write(buffer, 0, result.Count); + if (ms.Length > MaxMessageBytes) { + _logger.LogWarning("메시지 크기 초과로 연결 종료: {UserId}, Size={Size}", userId, ms.Length); + await socket.CloseAsync(WebSocketCloseStatus.MessageTooBig, "Message too large", cancellationTokenSource.Token); + return; + } } while (!result.EndOfMessage);
🧹 Nitpick comments (5)
ProjectVG.Application/Services/Chat/Validators/ChatRequestValidator.cs (1)
52-67: 잔액 체크 메시지/분기 단순화 제안중복 예외 메시지 생성을 줄일 수 있습니다. 동작 동일.
- if (currentBalance <= 0) { - _logger.LogWarning("토큰 잔액 부족 (0 토큰): UserId={UserId}", command.UserId); - throw new ValidationException(ErrorCode.INSUFFICIENT_CREDIT_BALANCE, $"토큰이 부족합니다. 현재 잔액: {currentBalance} 토큰, 필요 토큰: {ESTIMATED_CHAT_COST} 토큰"); - } - - var hasSufficientTokens = currentBalance >= ESTIMATED_CHAT_COST; - if (!hasSufficientTokens) { + var hasSufficientTokens = currentBalance >= ESTIMATED_CHAT_COST; + if (!hasSufficientTokens) { _logger.LogWarning("토큰 부족: UserId={UserId}, 현재잔액={CurrentBalance}, 필요토큰={RequiredTokens}", command.UserId, currentBalance, ESTIMATED_CHAT_COST); throw new ValidationException(ErrorCode.INSUFFICIENT_CREDIT_BALANCE, $"토큰이 부족합니다. 현재 잔액: {currentBalance} 토큰, 필요 토큰: {ESTIMATED_CHAT_COST} 토큰"); }ProjectVG.Api/Middleware/WebSocketMiddleware.cs (2)
111-116: RequestAborted와 링크된 취소 토큰 사용 권장연결이 끊긴 HTTP 요청 조기 취소를 반영하려면 RequestAborted와 링크하세요.
- var cancellationTokenSource = new CancellationTokenSource(); + using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource( + // HttpContext=RequestAborted 전달 필요: 메서드 시그니처 변경 또는 필드 보관 + CancellationToken.None + ); // Set a reasonable timeout for WebSocket operations cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(30));메서드 시그니처를
RunSessionLoop(WebSocket socket, string userId, CancellationToken requestAborted)로 변경해context.RequestAborted를 전달하는 것을 권장합니다.
151-163: pong 메시지 인코딩 재사용으로 GC 압력 감소(마이크로 최적화)상수 바이트 배열을 캐시하면 할당을 줄일 수 있습니다.
- var pongMessage = System.Text.Encoding.UTF8.GetBytes("{\"type\":\"pong\"}"); + // 클래스 정적 필드로 캐시: private static readonly byte[] PongPayload = Encoding.UTF8.GetBytes("{\"type\":\"pong\"}"); + var pongMessage = PongPayload;ProjectVG.Application/Services/Auth/AuthService.cs (1)
94-99: LogoutAsync에서 사용되지 않는 userId 조회 제거의미 없는 호출입니다. 제거로 불필요 I/O 감소.
var revoked = await _tokenService.RevokeRefreshTokenAsync(refreshToken); - if (revoked) { - var userId = await _tokenService.GetUserIdFromTokenAsync(refreshToken); - } return revoked;ProjectVG.Infrastructure/Persistence/Repositories/User/SqlServerUserRepository.cs (1)
24-28: CancellationToken 전파 도입 권장 — 비동기 DB 호출에 토큰 전달(선택 매개변수, 기본값 포함)EF Core의 ToListAsync / FirstOrDefaultAsync / SaveChangesAsync 등 비동기 DB 호출은 취소 가능하므로, 레포 전반의 리포지토리 인터페이스에 선택적 CancellationToken 매개변수를 추가하고 내부 호출에 전달하세요(다음 PR에서 적용 권장). 검증 결과 ProjectVG.Infrastructure/Persistence/Repositories의 Credit, Conversation, Character, User 리포지토리에서 관련 호출이 확인되었습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
ProjectVG.Api/Middleware/WebSocketMiddleware.cs(3 hunks)ProjectVG.Application/Services/Auth/AuthService.cs(4 hunks)ProjectVG.Application/Services/Chat/ChatService.cs(1 hunks)ProjectVG.Application/Services/Chat/Validators/ChatRequestValidator.cs(2 hunks)ProjectVG.Infrastructure/Auth/TokenService.cs(5 hunks)ProjectVG.Infrastructure/Persistence/Repositories/User/SqlServerUserRepository.cs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- ProjectVG.Application/Services/Chat/ChatService.cs
🧰 Additional context used
🧬 Code graph analysis (3)
ProjectVG.Application/Services/Auth/AuthService.cs (2)
ProjectVG.Infrastructure/Auth/TokenService.cs (6)
Task(19-41)Task(43-92)Task(94-97)Task(99-114)Task(116-126)Task(128-136)ProjectVG.Application/Services/Auth/IUserAuthService.cs (1)
AuthResult(39-46)
ProjectVG.Infrastructure/Persistence/Repositories/User/SqlServerUserRepository.cs (3)
ProjectVG.Application/Services/User/UserService.cs (6)
Task(19-43)Task(45-56)Task(58-62)Task(64-68)Task(70-74)Task(76-80)ProjectVG.Common/Exceptions/NotFoundException.cs (5)
NotFoundException(3-24)NotFoundException(5-8)NotFoundException(10-13)NotFoundException(15-18)NotFoundException(20-23)ProjectVG.Domain/Common/BaseEntity.cs (1)
Update(16-19)
ProjectVG.Api/Middleware/WebSocketMiddleware.cs (2)
ProjectVG.Application/Services/Session/ConnectionRegistry.cs (1)
Unregister(28-38)ProjectVG.Application/Services/Session/IConnectionRegistry.cs (1)
Unregister(16-16)
🔇 Additional comments (5)
ProjectVG.Infrastructure/Persistence/Repositories/User/SqlServerUserRepository.cs (1)
27-27: ASP.NET Core에서 ConfigureAwait 제거는 합리적입니다동기화 컨텍스트가 없으므로 오버헤드 제거 측면에서 👍
Also applies to: 32-32, 37-37, 42-42, 47-47, 52-52, 57-57, 68-68, 75-75, 88-88, 95-95, 103-103
ProjectVG.Application/Services/Chat/Validators/ChatRequestValidator.cs (1)
38-39: ConfigureAwait 제거 변경은 무해하며 일관성 좋습니다검증 흐름에 영향 없고 가독성 향상입니다.
Also applies to: 40-45, 46-51, 53-59, 61-67, 79-81
ProjectVG.Application/Services/Auth/AuthService.cs (2)
33-49: ConfigureAwait 제거 일관성 👍컨트롤 플로우 변화 없음. 유지보수성 향상.
Also applies to: 55-61, 74-81, 94-97
59-61: 예외 매핑 확인 필요 — GenerateTokensAsync가 InvalidOperationException을 던집니다
- 요약: TokenService.GenerateTokensAsync가 저장 실패 시 InvalidOperationException을 던지는 것으로 확인됨(증거: ProjectVG.Tests/Auth/TokenServiceTests.cs, 구현: ProjectVG.Infrastructure/Auth/TokenService.cs).
- 영향: 호출 지점 — ProjectVG.Application/Services/Auth/AuthService.cs (약 line 60), ProjectVG.Application/Services/Auth/OAuth2AuthService.cs (약 line 73).
- 현 상태: 현재 검색 결과로는 앱 수준 예외 처리(UseExceptionHandler / ExceptionFilter 등)를 찾지 못함. 전역 예외 미들웨어(Program.cs / Startup.cs 또는 커스텀 미들웨어)에서 InvalidOperationException이 5xx/도메인 에러(ProblemDetails 포함)로 매핑되는지 확인 필요.
- 검증용 명령(로컬 실행): rg -nP -C3 'app.UseExceptionHandler|UseExceptionHandler(|ExceptionHandlerMiddleware|ExceptionFilter|AddProblemDetails|UseProblemDetails|Program.cs|Startup.cs' --type=cs
ProjectVG.Infrastructure/Auth/TokenService.cs (1)
27-33: 로그인/회원가입 흐름에서 GenerateTokensAsync 예외 전파 영향 재확인 필요GenerateTokensAsync가 리프레시 토큰 저장 실패 시 InvalidOperationException을 던지므로 상위 계층에서 이 예외를 포착해 적절한 API 응답(오류 코드/메시지 변환 또는 롤백)을 반환하는지 확인하세요.
- 확인 지점: ProjectVG.Application/Services/Auth/AuthService.cs (라인 60).
- 확인 지점: ProjectVG.Application/Services/Auth/OAuth2AuthService.cs (라인 73).
- 참고: ProjectVG.Tests/Auth/TokenServiceTests.cs (라인 72-74)는 이미 InvalidOperationException을 기대함.
- 참고: ProjectVG.Tests/Auth/AuthServiceTests.cs (라인 66, 112 등)는 GenerateTokensAsync를 성공으로 모킹함 — 예외 시나리오 테스트 추가 권장.
| if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived) { | ||
| await socket.CloseAsync( | ||
| WebSocketCloseStatus.NormalClosure, | ||
| "Connection closed", | ||
| CancellationToken.None).ConfigureAwait(false); | ||
| CancellationToken.None); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
CloseAsync에 무제한 CancellationToken.None 사용 지양
네트워크 이슈 시 무기한 블로킹될 수 있습니다. 짧은 타임아웃을 가진 CTS를 사용하세요.
- await socket.CloseAsync(
+ using var closeCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
+ await socket.CloseAsync(
WebSocketCloseStatus.NormalClosure,
"Connection closed",
- CancellationToken.None);
+ closeCts.Token);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived) { | |
| await socket.CloseAsync( | |
| WebSocketCloseStatus.NormalClosure, | |
| "Connection closed", | |
| CancellationToken.None).ConfigureAwait(false); | |
| CancellationToken.None); | |
| } | |
| if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived) { | |
| using var closeCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); | |
| await socket.CloseAsync( | |
| WebSocketCloseStatus.NormalClosure, | |
| "Connection closed", | |
| closeCts.Token); | |
| } |
🤖 Prompt for AI Agents
In ProjectVG.Api/Middleware/WebSocketMiddleware.cs around lines 183 to 188, the
call to socket.CloseAsync uses CancellationToken.None which can block
indefinitely on network issues; replace it with a CancellationToken from a
short-lived CancellationTokenSource (e.g., a few seconds) to bound the wait.
Create a CTS with a sensible timeout, pass cts.Token into CloseAsync, and ensure
the CTS is disposed (use a using or try/finally) so the token cancels after the
timeout to avoid hanging on network failures.
| var accessTokenExpiresAt = DateTime.UtcNow.AddMinutes(15); | ||
| var refreshTokenExpiresAt = DateTime.UtcNow.AddMinutes(1440); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
토큰 만료 시간 하드코딩 → 설정화 권장
운영 정책 변경에 유연하게 대응할 수 있도록 IOptions 패턴으로 구성값화하세요.
예: TokenOptions { AccessTokenMinutes, RefreshTokenMinutes }를 DI로 주입.
🤖 Prompt for AI Agents
In ProjectVG.Infrastructure/Auth/TokenService.cs around lines 24 to 26, the
access and refresh token lifetimes are hardcoded; change this to use
configurable options injected via IOptions<TokenOptions>. Add a TokenOptions
class with AccessTokenMinutes and RefreshTokenMinutes, inject
IOptions<TokenOptions> into the TokenService constructor, and replace
DateTime.UtcNow.AddMinutes(15) / AddMinutes(1440) with values from
options.Value.AccessTokenMinutes and options.Value.RefreshTokenMinutes; also
ensure TokenOptions is bound in DI (e.g.,
services.Configure<TokenOptions>(configuration.GetSection("TokenOptions"))) and
consider validating the values before use.
| return await _context.Users | ||
| .Where(u => u.Status == AccountStatus.Active) | ||
| .OrderBy(u => u.Username) | ||
| .ToListAsync().ConfigureAwait(false); | ||
| .ToListAsync(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
읽기 전용 쿼리에 AsNoTracking 적용으로 트래킹 오버헤드 제거 제안
레포 메서드 다수가 조회 전용입니다. EF Core 트래킹 비용 절감을 위해 AsNoTracking 추가를 권장합니다. Update/Delete는 내부에서 별도 조회로 트래킹 엔티티를 확보하므로 안전합니다.
return await _context.Users
- .Where(u => u.Status == AccountStatus.Active)
+ .AsNoTracking()
+ .Where(u => u.Status == AccountStatus.Active)
.OrderBy(u => u.Username)
.ToListAsync();
-return await _context.Users.FirstOrDefaultAsync(u => u.Id == id && u.Status != AccountStatus.Deleted);
+return await _context.Users
+ .AsNoTracking()
+ .FirstOrDefaultAsync(u => u.Id == id && u.Status != AccountStatus.Deleted);
-return await _context.Users.FirstOrDefaultAsync(u => u.Username == username && u.Status != AccountStatus.Deleted);
+return await _context.Users
+ .AsNoTracking()
+ .FirstOrDefaultAsync(u => u.Username == username && u.Status != AccountStatus.Deleted);
-return await _context.Users.FirstOrDefaultAsync(u => u.Email == email && u.Status != AccountStatus.Deleted);
+return await _context.Users
+ .AsNoTracking()
+ .FirstOrDefaultAsync(u => u.Email == email && u.Status != AccountStatus.Deleted);
-return await _context.Users.FirstOrDefaultAsync(u => u.ProviderId == providerId && u.Status != AccountStatus.Deleted);
+return await _context.Users
+ .AsNoTracking()
+ .FirstOrDefaultAsync(u => u.ProviderId == providerId && u.Status != AccountStatus.Deleted);
-return await _context.Users.FirstOrDefaultAsync(u => u.Provider == provider && u.ProviderId == providerId && u.Status != AccountStatus.Deleted);
+return await _context.Users
+ .AsNoTracking()
+ .FirstOrDefaultAsync(u => u.Provider == provider && u.ProviderId == providerId && u.Status != AccountStatus.Deleted);
-return await _context.Users.FirstOrDefaultAsync(u => u.UID == uid && u.Status != AccountStatus.Deleted);
+return await _context.Users
+ .AsNoTracking()
+ .FirstOrDefaultAsync(u => u.UID == uid && u.Status != AccountStatus.Deleted);Also applies to: 32-33, 37-38, 42-43, 47-48, 52-53, 57-58
Summary by CodeRabbit
리팩터링
개선사항
버그 수정
작업(Chores)