Skip to content

Conversation

ImGdevel
Copy link
Member

@ImGdevel ImGdevel commented Sep 7, 2025

Summary by CodeRabbit

  • New Features
    • 대화 문맥 기본 길이를 10개로 확대하여 응답 품질 향상.
    • 미유 캐릭터의 성격·프롬프트를 전면 업데이트하여 대화 경험 개선.
    • 음성 합성 기본 보이스 설정 조정(피치 하향, 속도 소폭 감소)으로 더 자연스러운 발화 제공.
  • Bug Fixes
    • 메시지 유효성 강화: 빈 내용 차단 및 최대 길이 제한 적용.
    • 대화 기록 조회 페이징 범위 검증 추가로 오류 예방.

Copy link
Contributor

coderabbitai bot commented Sep 7, 2025

Walkthrough

대화 히스토리 처리와 LLM 연동 표준을 정비: History 객체 기반으로 대화 맥락을 전달하도록 ILLMClient/LLMClient/MockLLMClient 및 ChatLLMProcessor를 변경. ChatProcessContext에서 MemoryStore 제거 및 파서 반환형 변경. ChatService는 대화 조회 API에 페이지 인자 추가. ConversationService에 유효성 검사 추가. TTS 기본값 조정. 신규 마이그레이션 3건 추가 및 스냅샷 갱신. 테스트 구성 전반 재구성.

Changes

Cohort / File(s) Summary
Chat Context API 정리
ProjectVG.Application/Models/Chat/ChatProcessContext.cs
MemoryStore 공개 속성 제거. ParseConversationHistory 반환형 IEnumerable<string>IEnumerable<ConversationHistory>, 기본 count 5→10, null 처리 시 빈 시퀀스 반환.
ChatService 히스토리 조회 서명 변경 반영
ProjectVG.Application/Services/Chat/ChatService.cs
GetConversationHistoryAsync(userId, characterId, 1, 10)로 호출 변경(페이지/오프셋 인자 추가).
Chat 프로세서 - LLM 대화기록 구조화
ProjectVG.Application/Services/Chat/Processors/ChatLLMProcessor.cs, ProjectVG.Application/Services/Chat/Processors/ChatResultProcessor.cs
히스토리를 LLM History 목록으로 변환·재사용. 불필요 로그 제거. 기능 시그니처 변경 없음.
대화 서비스 유효성 강화
ProjectVG.Application/Services/Conversation/ConversationService.cs
AddMessage: 내용 공백/길이(≤10000) 및 역할 유효성 검사 추가. GetConversationHistory: 페이지(>0), pageSize(1–100) 검증 추가.
LLM 클라이언트 표준화(History 타입 도입)
ProjectVG.Infrastructure/Integrations/LLMClient/ILLMClient.cs, ProjectVG.Infrastructure/Integrations/LLMClient/LLMClient.cs, ProjectVG.Tests/Application/TestUtilities/MockLLMClient.cs
CreateTextResponseAsyncconversationHistory 파라미터 타입 List<string>?List<History>?. 구현부는 직접 전달로 단순화. 테스트 모의체도 동일 변경.
TTS 기본 설정 조정
ProjectVG.Infrastructure/Integrations/TextToSpeechClient/Models/TextToSpeechRequest.cs
VoiceSettings.PitchShift 기본값 0→-3, Speed 1.2→1.1. JsonPropertyName 속성 제거(키 매핑 변경).
데이터 마이그레이션 추가/스냅샷 갱신
ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.*, .../20250905233632_UpdateMiyuCharacterPersonality.*, .../20250905234811_AdvancedMiyuPromptFixed.*, ProjectVG.Infrastructure/Migrations/ProjectVGDbContextModelSnapshot.cs
Miyu/Haru 캐릭터 교체·개선 관련 데이터 이관 및 시드 업데이트. 다단계 Up/Down 포함. 모델 스냅샷 타임스탬프 갱신.
테스트 구성 리팩터링
ProjectVG.Tests/Application/Services/Chat/ChatServiceSimpleTests.cs
DI 해석을 GetService 중심으로 변경. 실제 협력 객체를 조합하는 방향으로 헬퍼 재작성. 테스트명 일부 수정.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as Client
  participant ChatService as ChatService.PrepareChatRequestAsync
  participant ConvSvc as IConversationService
  participant LLMProc as ChatLLMProcessor
  participant LLM as ILLMClient

  Client->>ChatService: 요청(userId, characterId, userMessage)
  ChatService->>ConvSvc: GetConversationHistoryAsync(userId, characterId, page=1, pageSize=10)
  ConvSvc-->>ChatService: IEnumerable<ConversationHistory>
  ChatService-->>Client: ChatProcessContext(History 포함)

  Client->>LLMProc: CreateTextResponseAsync(context)
  LLMProc->>LLMProc: ConversationHistory → List<History> 변환/캐시
  LLMProc->>LLM: CreateTextResponseAsync(system, user, instructions, conversationHistory: List<History>)
  LLM-->>LLMProc: LLMResponse
  LLMProc-->>Client: Response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

폴짝, 히스토리 쥐고 뛰는 발자국
문자열 대신 History, 깔끔한 발놀림!
페이지 하나, 열 줄 품고 달려가
미유의 말투도 살짝 다듬고 ♪
톡-톡, 토끼는 커밋 위에 발도장 🐇✨

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/conversation-history-fix

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (4)
ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.Designer.cs (4)

74-77: (중복) SystemPrompt nvarchar(max)+MaxLength 혼용

위 파일 코멘트와 동일합니다. DB 레벨 제한 의도가 있다면 타입 또는 CHECK로 일관화하세요.


143-147: (중복) ConversationHistories.Content 길이 제한 불일치

DB 제약 추가를 고려하세요.


93-99: (중복) IndividualConfigJson 컬럼 이원화 문제

위 최신 디자이너와 동일 이슈입니다. 하나의 컬럼으로 통합하세요.


320-354: (중복) Users.RowVersion 시드 금지

RowVersion 시드는 제거해야 합니다.

🧹 Nitpick comments (26)
ProjectVG.Infrastructure/Integrations/TextToSpeechClient/Models/TextToSpeechRequest.cs (3)

31-33: [Obsolete] Model 직렬화 시 null 전송 가능성: null 무시 처리 권장

  • 글로벌 JsonSerializerOptions 설정에 따라 model: null이 전송될 수 있습니다. 외부 API와의 호환성 위해 null 무시를 명시하는 편이 안전합니다.
@@
-        [Obsolete("이 필드는 더 이상 사용하지 않습니다")]
-        [JsonPropertyName("model")]
-        public string? Model { get; set; } 
+        [Obsolete("이 필드는 더 이상 사용하지 않습니다")]
+        [JsonPropertyName("model")]
+        [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+        public string? Model { get; set; } 

47-49: 기본 피치 -3 변경: 전역 음색 변화에 따른 영향 점검 + 입력 범위 검증 권장

  • 기본값을 0 → -3으로 낮추면 서비스 전반 TTS 톤이 달라집니다. 퍼블릭 릴리스 전 샘플/스냅샷, QA 시나리오 확인 부탁드립니다.
  • 허용 범위(-12~12) 검증을 속성에 직접 부여하면 잘못된 값 전파를 막을 수 있습니다.

권장 변경(해당 범위 내에서만 직렬화/검증):

@@
-        [JsonPropertyName("pitch_shift")]
-        public int PitchShift { get; set; } = -3;
+        [JsonPropertyName("pitch_shift")]
+        [Range(-12, 12, ErrorMessage = "pitch_shift는 -12에서 12 사이여야 합니다.")]
+        public int PitchShift { get; set; } = -3;

추가 import(선택):

using System.ComponentModel.DataAnnotations;

검증 스크립트(레포 내 피치 기본/오버라이드 사용처 점검):

#!/bin/bash
# PitchShift 사용/오버라이드 위치 점검
rg -n --type cs -C2 '\bPitchShift\s*='
rg -n --type cs -C3 'new\s+VoiceSettings\b'
# JSON/설정 파일에서 직접 키 사용 여부
rg -n -C2 'pitch_shift' -g '!**/bin/**' -g '!**/obj/**'

59-61: 기본 속도 1.1f로 변경: 재생 길이/동기화 영향 확인 + 범위 제한 고려

  • 1.2f → 1.1f 변경은 합성 길이와 타이밍(예: 자막/애니메이션 싱크)에 영향이 있을 수 있습니다. 골든 샘플/스냅샷 업데이트 여부 확인 바랍니다.
  • API 허용 범위가 명확하다면 속성 수준의 범위 검증을 권장합니다(예: 0.5~2.0).

예시:

@@
-        [JsonPropertyName("speed")]
-        public float Speed { get; set; } = 1.1f;
+        [JsonPropertyName("speed")]
+        [Range(0.5, 2.0, ErrorMessage = "speed는 0.5~2.0 범위를 권장합니다.")]
+        public float Speed { get; set; } = 1.1f;

검증 스크립트:

#!/bin/bash
# Speed 오버라이드/하드코딩 사용처 확인
rg -n --type cs -C2 '\bSpeed\s*='
rg -n -C2 '\"speed\"\s*:'
ProjectVG.Application/Services/Chat/Processors/ChatResultProcessor.cs (3)

32-38: 메시지 저장: 길이 검증(1만자)로 인한 예외 가능성 사전 처리 제안

ConversationService에 길이 제한(1만자)이 추가되었으므로, LLM 응답이 예외적으로 길 경우 저장 단계에서 ValidationException이 발생해 전체 플로우가 실패할 수 있습니다. 저장 직전에 응답을 안전 길이로 절단하거나(로그로 표시) 별도 경로로 보존하는 방식을 고려해 주세요.

아래처럼 저장용 텍스트만 안전 길이로 제한하는 방안을 제안합니다:

-            await _conversationService.AddMessageAsync(context.UserId, context.CharacterId, ChatRole.Assistant, context.Response, DateTime.UtcNow, context.RequestId.ToString());
+            var assistantContent = context.Response.Length > 10000 
+                ? context.Response[..10000] 
+                : context.Response;
+            await _conversationService.AddMessageAsync(
+                context.UserId, context.CharacterId, ChatRole.Assistant, assistantContent, DateTime.UtcNow, context.RequestId.ToString());

42-87: 메모리 삽입 타임스탬프/컨텍스트 일관성 개선

현재 user/assistant 요청에 서로 다른 UtcNow 호출을 사용하고 있어 순서/타임스탬프가 미세하게 엇갈릴 수 있습니다. 기준 시각을 한 번 캡처해 동일 값을 사용하고, assistant 쪽도 timestamp 컨텍스트를 넣어 일관성을 유지하는 것을 권장합니다.

아래처럼 기준 시각을 도입하고 컨텍스트 키를 통일하세요:

         private async Task PersistMemoryAsync(ChatProcessContext context)
         {
+            var baseNow = DateTime.UtcNow;
             var episodicRequest = new EpisodicInsertRequest
             {
                 Text = context.Response,
                 UserId = context.UserId.ToString(),
                 Speaker = "assistant",
                 Emotion = new EmotionInfo
                 {
                     Valence = "neutral",
                     Arousal = "medium",
                     Labels = new List<string> { "helpful", "informative" },
                     Intensity = 0.6
                 },
                 Context = new Dictionary<string, object>
                 {
                     { "character_id", context.CharacterId },
                     { "session_id", context.RequestId },
-                    { "conversation_turn", DateTime.UtcNow.Ticks },
+                    { "conversation_turn", baseNow.Ticks },
                     { "user_message", context.UserMessage },
                     { "response_type", "chat_response" },
-                    { "processing_time", 0 }
+                    { "processing_time", 0 },
+                    { "timestamp", baseNow.ToString("o") }
                 },
                 ImportanceScore = 0.7
             };

             var userMemoryRequest = new EpisodicInsertRequest
             {
                 Text = context.UserMessage,
                 UserId = context.UserId.ToString(),
                 Speaker = "user",
                 Emotion = new EmotionInfo
                 {
                     Valence = "neutral",
                     Arousal = "medium",
                     Labels = new List<string> { "inquiry", "conversation" },
                     Intensity = 0.5
                 },
                 Context = new Dictionary<string, object>
                 {
                     { "character_id", context.CharacterId },
                     { "session_id", context.RequestId },
-                    { "conversation_turn", DateTime.UtcNow.Ticks - 1 },
+                    { "conversation_turn", baseNow.Ticks - 1 },
                     { "message_type", "user_input" },
-                    { "timestamp", DateTime.UtcNow.ToString("o") }
+                    { "timestamp", baseNow.ToString("o") }
                 },
                 ImportanceScore = 0.8
             };

91-98: 부분 실패 허용 및 로깅 가시성 향상

두 건의 메모리 삽입을 하나의 try 블록에서 처리하면 첫 삽입 실패 시 두 번째가 시도되지 않습니다. 부분 실패를 허용하려면 개별 try/catch로 분리하고 어떤 건이 실패했는지 구분 로깅하세요.

예시:

-            try
-            {
-                await _memoryClient.InsertEpisodicAsync(userMemoryRequest);
-                await _memoryClient.InsertEpisodicAsync(episodicRequest);
-                
-            }
-            catch (Exception ex)
-            {
-                _logger.LogWarning(ex, "메모리 삽입 실패: 사용자={UserId}, 캐릭터={CharacterId}", context.UserId, context.CharacterId);
-            }
+            try { await _memoryClient.InsertEpisodicAsync(userMemoryRequest); }
+            catch (Exception ex)
+            { _logger.LogWarning(ex, "메모리 삽입 실패(user): 사용자={UserId}, 캐릭터={CharacterId}", context.UserId, context.CharacterId); }
+
+            try { await _memoryClient.InsertEpisodicAsync(episodicRequest); }
+            catch (Exception ex)
+            { _logger.LogWarning(ex, "메모리 삽입 실패(assistant): 사용자={UserId}, 캐릭터={CharacterId}", context.UserId, context.CharacterId); }
ProjectVG.Application/Services/Chat/Processors/ChatLLMProcessor.cs (1)

26-33: LLM 이력의 정렬·역할 표준화로 응답 안정성 개선

히스토리는 시간 오름차순 정렬을 보장하고(Role은 소문자 표준화), 알 수 없는 역할은 기본적으로 user로 폴백하는 편이 안전합니다.

아래처럼 보강을 제안합니다:

-            var conversationHistory = context.ParseConversationHistory()
-                .Select(h => new ProjectVG.Infrastructure.Integrations.LLMClient.Models.History 
-                { 
-                    Role = h.Role, 
-                    Content = h.Content 
-                })
-                .ToList();
+            var conversationHistory = context.ParseConversationHistory()
+                .OrderBy(h => h.Timestamp)
+                .Select(h => new ProjectVG.Infrastructure.Integrations.LLMClient.Models.History 
+                { 
+                    Role = NormalizeRole(h.Role),
+                    Content = h.Content 
+                })
+                .ToList();

메서드 내 보조 함수(선택):

private static string NormalizeRole(string? role)
    => role?.ToLowerInvariant() switch
    {
        "assistant" => "assistant",
        "user" => "user",
        "system" => "system",
        _ => "user"
    };
ProjectVG.Application/Services/Chat/ChatService.cs (1)

92-94: 페이지·페이지크기 상수화 및 구성 가능성 확보

매직 넘버(1, 10)를 상수/설정으로 분리해 테스트·튜닝을 쉽게 하세요. ChatProcessContext의 기본 10개와도 의미상 일치합니다.

아래처럼 상수로 두고 호출부 교체를 제안합니다:

-            var conversationHistoryContext = await _conversationService.GetConversationHistoryAsync(command.UserId, command.CharacterId, 1, 10);
+            var conversationHistoryContext = await _conversationService
+                .GetConversationHistoryAsync(command.UserId, command.CharacterId, DefaultHistoryPage, DefaultHistoryPageSize);

메서드 외부(클래스 상단 등)에 추가:

private const int DefaultHistoryPage = 1;
private const int DefaultHistoryPageSize = 10;

또한 저장소가 시간 오름차순으로 반환하는지 확인 부탁드립니다(LLM 입력 품질에 영향).

ProjectVG.Application/Services/Conversation/ConversationService.cs (1)

53-69: 페이징 검증 명확 — 정렬 보장 확인 요청

page/pageSize 검증은 적절합니다. 저장소 레이어가 시간 오름차순으로 반환하도록 계약을 명시하거나(OrderBy Timestamp ASC) 테스트로 고정해 주세요.

ProjectVG.Infrastructure/Integrations/LLMClient/ILLMClient.cs (1)

25-32: 대화 이력 타입 전환(List) 적합

상위 계층에서 역할/콘텐츠를 명시적으로 제어할 수 있어 👍. 문서 주석에 History.Role의 기대값(user/assistant/system) 명시를 권장합니다. 또한 불변성을 원하면 IReadOnlyList<History>로의 전환도 고려해 보세요(테스트 영향 有).

ProjectVG.Application/Models/Chat/ChatProcessContext.cs (1)

63-72: 히스토리 파싱 시 정렬 보장 권장

저장소 정렬에 의존하지 않도록 여기서도 시간 오름차순 정렬 후 Take를 권장합니다.

-            var parsed = ConversationHistory.Take(count);
+            var parsed = ConversationHistory.OrderBy(h => h.Timestamp).Take(count);
             return parsed.ToList();
ProjectVG.Tests/Application/TestUtilities/MockLLMClient.cs (1)

111-119: Mock 시그니처 업데이트 LGTM

테스트에서 History 리스트를 그대로 주입하도록 맞춘 변경이 일관됩니다. 필요하다면 Mock에서 역할 값(user/assistant/system) 검증을 추가해 테스트 신뢰도를 높일 수 있습니다.

Also applies to: 126-126

ProjectVG.Infrastructure/Integrations/LLMClient/LLMClient.cs (1)

65-73: LLM 요청 빌드 시 History 직접 전달 OK — 취소 토큰/재시도는 추후 고려

타입 전환 반영이 적절합니다. 외부 호출 안정성을 위해 CreateTextResponseAsync/SendRequestAsyncCancellationToken을 추가하고 PostAsync에 전달, 필요 시 Polly 기반 재시도(429/5xx)도 고려해 주세요.

Also applies to: 79-87

ProjectVG.Tests/Application/Services/Chat/ChatServiceSimpleTests.cs (3)

114-121: 스코프 생성 시뮬레이션은 적절합니다. 서로 다른 스코프 간 Provider 상이성도 검증해 두세요

현재 테스트는 “스코프 내 해상”을 잘 검증합니다. 추가로 “서로 다른 스코프는 서로 다른 ServiceProvider를 가진다”를 확인하면 회귀에 더 강해집니다. 또한 본 테스트는 async/await를 사용하지 않으므로 비동기 시그니처를 제거해도 됩니다.

예시(동일 파일에 추가):

[Fact]
public void ServiceScopeFactory_CreateScope_ShouldReturnDistinctProvidersPerScope()
{
    var sp1 = new Mock<IServiceProvider>();
    var sp2 = new Mock<IServiceProvider>();

    var scope1 = new Mock<IServiceScope>();
    var scope2 = new Mock<IServiceScope>();
    scope1.Setup(s => s.ServiceProvider).Returns(sp1.Object);
    scope2.Setup(s => s.ServiceProvider).Returns(sp2.Object);

    _mockScopeFactory.Reset();
    _mockScopeFactory
        .SetupSequence(f => f.CreateScope())
        .Returns(scope1.Object)
        .Returns(scope2.Object);

    using var s1 = _mockScopeFactory.Object.CreateScope();
    using var s2 = _mockScopeFactory.Object.CreateScope();

    s1.ServiceProvider.Should().NotBeSameAs(s2.ServiceProvider);
}

Also applies to: 124-126, 133-134


198-205: CreateTestChatService가 실구현체를 다수 사용 — 단위 테스트 결합도/취약성 증가 가능성

실제 구현체(Validator/Preprocessor/Processor/Handler)를 조립해 생성자 검증을 수행하는 접근은 통합 관점에 유리하나, 단위 테스트가 외부 변화에 민감해질 수 있습니다. 또한 변수명이 mock*이지만 실제 인스턴스라 혼동을 유발합니다. 최소한 변수명을 정리하여 가독성을 개선하세요.

제안(diff: 변수명만 정리):

- var mockValidator = new ChatRequestValidator(
+ var validator = new ChatRequestValidator(
   ...

- var mockMemoryPreprocessor = new MemoryContextPreprocessor(
+ var memoryPreprocessor = new MemoryContextPreprocessor(
   ...

- var mockActionProcessor = new UserInputActionProcessor(
+ var actionProcessor = new UserInputActionProcessor(
   ...

- var mockResultProcessor = new ChatResultProcessor(
+ var resultProcessor = new ChatResultProcessor(
   ...

- var mockFailureHandler = new ChatFailureHandler(
+ var failureHandler = new ChatFailureHandler(
   ...
   return new ChatService(
     _mockMetricsService.Object,
     _mockScopeFactory.Object,
     _mockLogger.Object,
     _mockConversationService.Object,
     _mockCharacterService.Object,
-    mockValidator,
-    mockMemoryPreprocessor,
+    validator,
+    memoryPreprocessor,
     mockInputProcessor.Object,
-    mockActionProcessor,
+    actionProcessor,
     mockLLMProcessor.Object,
     mockTTSProcessor.Object,
-    mockResultProcessor,
-    mockFailureHandler
+    resultProcessor,
+    failureHandler
   );

추가 제안:

  • 외부 경계(ISessionStorage, IUserService, ICreditManagementService, IMemoryClient 등)는 예상치 못한 상호작용을 조기에 발견할 수 있도록 필요 시 MockBehavior.Strict으로 강화하고, 꼭 필요한 상호작용만 Setup하세요. 단, 해당 구현체가 생성자나 생성 직후 동작에서 호출한다면 과도한 Strict 설정은 실패를 유발할 수 있으니 상황에 맞게 선택적으로 적용하세요.

Also applies to: 208-209, 213-218, 219-229, 230-243, 250-258


85-105: GetService 전환 자체는 합리적이나, null 경로 테스트를 추가하세요

IServiceProvider.GetService는 미등록 시 null을 반환합니다. 이 변경의 안정성을 보장하려면 미등록 타입 요청 시 null 반환과 후속 처리(예: NRE 방지)를 검증하는 테스트를 보강하는 것이 좋습니다.

아래 스크립트로 프로덕션 코드(특히 ChatService)에서 GetService/GetRequiredService 사용 현황을 확인해 주세요. 기대 결과: ChatSuccessHandler/ChatResultProcessor 해상에 대해 GetRequiredService가 아니라 GetService를 사용하고, null 방어 로직이 있거나 상위에서 보장됩니다.

#!/bin/bash
# ChatService 내 GetService/RequiredService 사용 위치 점검
rg -nP -C2 --type=cs '\bclass\s+ChatService\b' 
rg -nP -C2 --type=cs '\b(GetRequiredService|GetService)\s*\('

# 대상 타입 직접 검색
rg -nP -C2 --type=cs 'ChatSuccessHandler|ChatResultProcessor'

추가로, 간단한 null 경로 테스트 예시는 아래와 같습니다(같은 클래스 내 추가 권장).

[Fact]
public void ServiceProvider_GetService_UnregisteredType_ShouldReturnNull()
{
    var svc = _mockServiceProvider.Object.GetService(typeof(Guid)); // 임의 미등록 타입
    svc.Should().BeNull();
}
ProjectVG.Infrastructure/Migrations/ProjectVGDbContextModelSnapshot.cs (1)

103-111: Seed 타임스탬프 변경 최소화 권장 (CreatedAt은 고정, UpdatedAt만 갱신).

여기서 CreatedAt/UpdatedAt가 주기적으로 갱신되면 스냅샷/마이그레이션이 불필요하게 시끄럽고 머지 충돌을 유발합니다. Seed의 생성 시각은 고정(최초 도입 시)하고, 내용 변경 시 UpdatedAt만 갱신하는 패턴으로 정리하는 것을 권장합니다.

배포/리스트어 환경에서 Seed 레코드의 CreatedAt이 과거 릴리스 대비 의도치 않게 바뀌지 않는지 확인해 주세요.

Also applies to: 118-126, 321-333, 339-350

ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.cs (2)

193-213: Seed 사용자 타임스탬프 갱신 범위 축소.

Seed 사용자(테스트 계정)의 CreatedAt까지 매번 갱신할 필요는 낮습니다. CreatedAt은 고정하고 UpdatedAt만 갱신하는 쪽이 변경 소음을 줄입니다.


20-191: SystemPrompt/Description 길이 제한 초과 위험 및 데이터 관리 방식 개선 제안.

  • 현재 SystemPrompt는 매우 길어 컬럼 제한(Description: 1000, SystemPrompt: 5000)에 근접/초과 위험이 있습니다. 모델 제약은 스냅샷/디자이너에 명시되어 있습니다.
  • 장문의 프롬프트는 마이그레이션 코드에 직접 하드코딩하기보다 별도 컨텐츠 소스(예: JSON/리소스 테이블/스토리지)로 관리하고, 마이그레이션에는 최소한의 Seed만 유지하는 것을 권장합니다.

검증 스크립트로 실제 문자열 길이를 확인해 주세요:

#!/bin/bash
# SystemPrompt/Description 길이 추출(대략치). 필요시 파일 경로 조정.
python - << 'PY'
import re, pathlib
files = [
  "ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.cs",
  "ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.cs",
]
for f in files:
    text = pathlib.Path(f).read_text(encoding="utf-8", errors="ignore")
    # values: new object[] { "desc", @"prompt...", new DateTime(...) }
    for m in re.finditer(r'values:\s*new object\[\]\s*\{\s*"(?P<desc>[^"]*)",\s*@\"(?P<prompt>.*?)",\s*new DateTime', text, re.S):
        desc = m.group("desc")
        prompt = m.group("prompt")
        print(f"{f} -> Description len={len(desc)}, SystemPrompt len={len(prompt)}")
PY

길이가 상한을 넘는다면: (1) 컬럼 MaxLength 상향(신중), (2) 프롬프트 축약, (3) 외부 리소스로 분리 후 키만 DB에 저장 — 중 하나를 선택해 주세요.

ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.cs (2)

21-100: 프롬프트 길이/관리 전략 동일 적용 권장.

해당 파일의 SystemPrompt/Description도 길이 제한(Description 1000, SystemPrompt 5000) 내인지 확인하고, 장문 프롬프트는 마이그레이션 하드코딩 대신 외부 리소스/콘텐츠 테이블로 관리하는 방식을 일관되게 적용하는 것을 권장합니다. 상위 마이그레이션(AdvancedMiyuPromptFixed) 코멘트와 동일합니다.

원한다면 위 길이 확인 스크립트에 본 파일도 포함해 자동 검증하도록 확장해 드립니다.


103-123: Seed 타임스탬프 정책 정리.

캐릭터(2222)와 유저 Seed의 CreatedAt까지 갱신하는 패턴은 변경 소음만 커질 가능성이 큽니다. CreatedAt은 최초 값 고정, UpdatedAt만 갱신하는 정책을 추천합니다.

Also applies to: 110-116

ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.Designer.cs (1)

52-58: IndividualConfig/IndividualConfigJson 컬럼명 매핑 혼동 가능성.

  • IndividualConfig → ColumnName("IndividualConfigJson")
  • IndividualConfigJson → ColumnName("IndividualConfigJson1")

이중/유사 명칭은 운영 중 혼동 원인이 됩니다. 도메인 모델/플루언트 매핑에서 명확한 컬럼명으로 정리하는 별도 작업을 고려해 주세요(향후 마이그레이션에서).

Also applies to: 97-99

ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.Designer.cs (2)

74-77: nvarchar(max)와 HasMaxLength(5000) 동시 사용 불일치

현재 DB 컬럼은 nvarchar(max)로 생성되며, HasMaxLength(5000)는 유효 길이를 DB가 강제하지 않습니다. DB 레벨 제한이 필요하면 nvarchar(5000)로 타입을 고정하거나 CHECK 제약을 추가하세요. 앱 레벨 검증만 원한다면 HasMaxLength만 남기고 ColumnType 지정은 제거하는 편이 명확합니다.


143-159: ConversationHistories.Content 길이 제한 불일치

Content가 nvarchar(max)이면서 HasMaxLength(10000)만 선언되어 DB는 10000자를 강제하지 않습니다. 데이터 무결성을 DB에서 보장하려면 CHECK 제약을 추가하세요(예: LEN([Content]) <= 10000).

ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.cs (1)

25-31: 대량 UPDATE 성능 주의(정보성)

CharacterId에 인덱스가 있으므로 문제 없을 확률이 높지만, 히스토리 테이블이 매우 클 경우 잠금 영향이 큽니다. 야간 배포/트랜잭션 타임아웃 설정을 고려하세요.

#!/bin/bash
# 모델에 CharacterId 인덱스가 반영되어 있는지 디자이너 확인(존재해야 함)
rg -n -g 'ProjectVG.Infrastructure/Migrations/**Designer.cs' -P 'ConversationHistories".*HasIndex\("CharacterId"\)'
ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.Designer.cs (1)

134-176: 네이밍: ConversationHistorys 네임스페이스 오탈자

"ConversationHistories"가 자연스러운 복수형입니다. 향후 리팩토링 시 타입/네임스페이스 정리 권장합니다(바이너리 호환 주의).

#!/bin/bash
# 오탈자 사용 범위 파악(리팩토링 영향도 측정)
rg -n -P '\bConversationHistorys\b'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2653e2f and f771aa3.

📒 Files selected for processing (17)
  • ProjectVG.Application/Models/Chat/ChatProcessContext.cs (1 hunks)
  • ProjectVG.Application/Services/Chat/ChatService.cs (1 hunks)
  • ProjectVG.Application/Services/Chat/Processors/ChatLLMProcessor.cs (1 hunks)
  • ProjectVG.Application/Services/Chat/Processors/ChatResultProcessor.cs (1 hunks)
  • ProjectVG.Application/Services/Conversation/ConversationService.cs (3 hunks)
  • ProjectVG.Infrastructure/Integrations/LLMClient/ILLMClient.cs (1 hunks)
  • ProjectVG.Infrastructure/Integrations/LLMClient/LLMClient.cs (2 hunks)
  • ProjectVG.Infrastructure/Integrations/TextToSpeechClient/Models/TextToSpeechRequest.cs (2 hunks)
  • ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.Designer.cs (1 hunks)
  • ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.cs (1 hunks)
  • ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.Designer.cs (1 hunks)
  • ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.cs (1 hunks)
  • ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.Designer.cs (1 hunks)
  • ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.cs (1 hunks)
  • ProjectVG.Infrastructure/Migrations/ProjectVGDbContextModelSnapshot.cs (4 hunks)
  • ProjectVG.Tests/Application/Services/Chat/ChatServiceSimpleTests.cs (4 hunks)
  • ProjectVG.Tests/Application/TestUtilities/MockLLMClient.cs (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.Designer.cs (2)
ProjectVG.Infrastructure/Migrations/20250901043857_AddCharacterHybridConfig.Designer.cs (1)
  • ProjectVG (12-274)
ProjectVG.Infrastructure/Migrations/20250903141443_FixIndexDropIssue.Designer.cs (1)
  • ProjectVG (12-383)
🔇 Additional comments (8)
ProjectVG.Application/Services/Conversation/ConversationService.cs (1)

23-36: 메시지 유효성 검증 추가 좋습니다

공백/최대 길이/역할 검증이 명확합니다. 운영 환경에서 예외 메시지에 원문 content 노출이 필요한지(PII/비밀정보)만 한 번 점검해 주세요.

ProjectVG.Tests/Application/Services/Chat/ChatServiceSimpleTests.cs (1)

165-171: 동일 스코프에서 동일 ServiceProvider 재사용 검증, 좋습니다

동일 스코프 내에서 ServiceProvider 동일성을 보장하는 핵심 조건을 정확히 검증하고 있습니다.

Also applies to: 178-179, 187-188

ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.cs (1)

125-199: LGTM — Down이 Up 변경(Description/SystemPrompt/UpdatedAt)을 올바르게 되돌립니다.

이 구간은 되돌리기 항목이 Up과 일치합니다.

ProjectVG.Infrastructure/Migrations/20250905233632_UpdateMiyuCharacterPersonality.Designer.cs (2)

74-76: 길이 제약 확인 메모.

Designer 기준 Description(Max 1000), SystemPrompt(Max 5000) 제약이 설정되어 있으니, Seed 텍스트가 상한을 넘지 않는지 마이그레이션 단계에서 검증하세요.

Also applies to: 42-46


1-401: 자동 생성 파일(Designer) — 수동 수정 비권장.

해당 파일은 자동 생성이므로 수동 수정은 피하고, 모델링 변경은 코드/플루언트 설정에서 수행해 주세요.

ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.Designer.cs (1)

309-316: 사용자 고유성 보장 인덱스 구성 좋습니다

(Provider, ProviderId) 유니크 + Email, UID 유니크 인덱스 조합이 외부 인증 연동과 자체 계정 식별 모두에 안전합니다.

ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.cs (2)

48-49: VoiceId "amantha" 오타/미등록 값 가능성

TTS/음성 엔진에 등록된 VoiceId인지 확인 필요합니다. "samantha" 의 오타일 가능성이 높습니다.

다음으로 VoiceId 레지스트리를 검색해 주세요. 존재하지 않으면 "samantha" 등 실제 등록된 값으로 교체 권장:

#!/bin/bash
# VoiceId 정의/사용 위치 점검
rg -n -C2 -g '!**/bin/**' -g '!**/obj/**' -P '\bVoiceId\b|amantha'

교체 예시:

-                    "amantha",
+                    "samantha",

15-23: 보조 캐릭터 ID(3333...) 충돌 가능성 점검

고정 GUID '33333333-3333-3333-3333-333333333333'가 과거 시드/데이터에 존재하면 삽입 실패합니다. 사전 확인 또는 존재 시 UPSERT/대체 GUID 사용이 안전합니다.

#!/bin/bash
# 기존 마이그레이션/시드/스크립트에 동일 GUID 사용 여부 점검
rg -n -g 'ProjectVG.Infrastructure/**' '33333333-3333-3333-3333-333333333333'

Comment on lines +149 to +166
// 2. ConversationHistories의 CharacterId를 다시 1111로 되돌림
migrationBuilder.Sql(@"
UPDATE ConversationHistories
SET CharacterId = '11111111-1111-1111-1111-111111111111'
WHERE CharacterId = '33333333-3333-3333-3333-333333333333'
");

// 3. 하루 캐릭터를 3333에서 1111로 복사하여 되돌림
migrationBuilder.Sql(@"
INSERT INTO Characters (Id, Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt, CreatedAt, UpdatedAt)
SELECT
'11111111-1111-1111-1111-111111111111' as Id,
Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt,
CreatedAt, UpdatedAt
FROM Characters
WHERE Id = '33333333-3333-3333-3333-333333333333'
");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Down() 순서 오류로 FK 무결성 위반 가능

1111(미유)을 삭제한 직후, ConversationHistories.CharacterId를 1111로 갱신하고 있어 존재하지 않는 FK를 참조하게 됩니다. 1111을 먼저 복구(3333 → 1111 복사)한 뒤에 ConversationHistories를 3333 → 1111로 업데이트해야 합니다.

아래처럼 순서를 재배치하세요:

-            // 2. ConversationHistories의 CharacterId를 다시 1111로 되돌림
-            migrationBuilder.Sql(@"
-                UPDATE ConversationHistories 
-                SET CharacterId = '11111111-1111-1111-1111-111111111111'
-                WHERE CharacterId = '33333333-3333-3333-3333-333333333333'
-            ");
-
-            // 3. 하루 캐릭터를 3333에서 1111로 복사하여 되돌림
+            // 2. 하루 캐릭터를 3333에서 1111로 복사하여 되돌림
             migrationBuilder.Sql(@"
                 INSERT INTO Characters (Id, Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt, CreatedAt, UpdatedAt)
                 SELECT 
                     '11111111-1111-1111-1111-111111111111' as Id,
                     Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt,
                     CreatedAt, UpdatedAt
                 FROM Characters 
                 WHERE Id = '33333333-3333-3333-3333-333333333333'
             ");
 
-            // 4. 3333 캐릭터 삭제
+            // 3. ConversationHistories의 CharacterId를 다시 1111로 되돌림
+            migrationBuilder.Sql(@"
+                UPDATE ConversationHistories 
+                SET CharacterId = '11111111-1111-1111-1111-111111111111'
+                WHERE CharacterId = '33333333-3333-3333-3333-333333333333'
+            ");
+
+            // 4. 3333 캐릭터 삭제
📝 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.

Suggested change
// 2. ConversationHistories의 CharacterId를 다시 1111로 되돌림
migrationBuilder.Sql(@"
UPDATE ConversationHistories
SET CharacterId = '11111111-1111-1111-1111-111111111111'
WHERE CharacterId = '33333333-3333-3333-3333-333333333333'
");
// 3. 하루 캐릭터를 3333에서 1111로 복사하여 되돌림
migrationBuilder.Sql(@"
INSERT INTO Characters (Id, Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt, CreatedAt, UpdatedAt)
SELECT
'11111111-1111-1111-1111-111111111111' as Id,
Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt,
CreatedAt, UpdatedAt
FROM Characters
WHERE Id = '33333333-3333-3333-3333-333333333333'
");
// 2. 하루 캐릭터를 3333에서 1111로 복사하여 되돌림
migrationBuilder.Sql(@"
INSERT INTO Characters (Id, Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt, CreatedAt, UpdatedAt)
SELECT
'11111111-1111-1111-1111-111111111111' as Id,
Name, Description, ImageUrl, IsActive, VoiceId, UserId, IsPublic, ConfigMode, IndividualConfigJson, SystemPrompt,
CreatedAt, UpdatedAt
FROM Characters
WHERE Id = '33333333-3333-3333-3333-333333333333'
");
// 3. ConversationHistories의 CharacterId를 다시 1111로 되돌림
migrationBuilder.Sql(@"
UPDATE ConversationHistories
SET CharacterId = '11111111-1111-1111-1111-111111111111'
WHERE CharacterId = '33333333-3333-3333-3333-333333333333'
");
// 4. 3333 캐릭터 삭제
🤖 Prompt for AI Agents
In
ProjectVG.Infrastructure/Migrations/20250905230547_AddMiyuCharacterAndUpdateHaru.cs
around lines 149 to 166, the migration updates ConversationHistories.CharacterId
to the 1111 GUID before restoring (inserting) the 1111 Character row, which can
violate FK constraints; swap the two operations so you first INSERT the
character row copied from 3333 as Id '11111111-1111-1111-1111-111111111111' and
only after that run the UPDATE that sets ConversationHistories.CharacterId from
'33333333-3333-3333-3333-333333333333' to
'11111111-1111-1111-1111-111111111111', ensuring the referenced Character exists
before updating the foreign keys.

Comment on lines +216 to +245
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.UpdateData(
table: "Characters",
keyColumn: "Id",
keyValue: new Guid("11111111-1111-1111-1111-111111111111"),
columns: new[] { "CreatedAt", "UpdatedAt" },
values: new object[] { new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5547), new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5547) });

migrationBuilder.UpdateData(
table: "Characters",
keyColumn: "Id",
keyValue: new Guid("22222222-2222-2222-2222-222222222222"),
columns: new[] { "CreatedAt", "UpdatedAt" },
values: new object[] { new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5668), new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5668) });

migrationBuilder.UpdateData(
table: "Users",
keyColumn: "Id",
keyValue: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
columns: new[] { "CreatedAt", "UpdatedAt" },
values: new object[] { new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5687), new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5687) });

migrationBuilder.UpdateData(
table: "Users",
keyColumn: "Id",
keyValue: new Guid("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
columns: new[] { "CreatedAt", "UpdatedAt" },
values: new object[] { new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5689), new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5689) });
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

Down이 Up과 불일치: 1111 캐릭터에 대해 되돌리기 항목이 잘못되었습니다.

  • Up에서 Id=1111은 Description/SystemPrompt/UpdatedAt를 변경했지만, Down은 CreatedAt/UpdatedAt만 변경하고 Description/SystemPrompt는 되돌리지 않습니다.
  • 또한 Up은 1111의 CreatedAt을 건드리지 않았는데 Down이 CreatedAt을 변경합니다. 양방향 불일치로 롤백 시 데이터가 어긋납니다.

다음 중 하나로 정합성을 맞춰주세요(권장안 A):

A) Down에서 1111의 Description/SystemPrompt/UpdatedAt만 이전 값으로 되돌리고 CreatedAt은 건드리지 않음.

적용 예(이전 값은 직전 마이그레이션 20250905233632_UpdateMiyuCharacterPersonality.Up의 값으로 교체):

-            migrationBuilder.UpdateData(
-                table: "Characters",
-                keyColumn: "Id",
-                keyValue: new Guid("11111111-1111-1111-1111-111111111111"),
-                columns: new[] { "CreatedAt", "UpdatedAt" },
-                values: new object[] { new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5547), new DateTime(2025, 9, 5, 23, 46, 16, 179, DateTimeKind.Utc).AddTicks(5547) });
+            migrationBuilder.UpdateData(
+                table: "Characters",
+                keyColumn: "Id",
+                keyValue: new Guid("11111111-1111-1111-1111-111111111111"),
+                columns: new[] { "Description", "SystemPrompt", "UpdatedAt" },
+                values: new object[] {
+                    /* 이전(직전) Description 문자열로 교체 */
+                    "9~12세 딸같은 존재로 호기심이 많고 자연스러운 성격을 가진 순수한 아이. 파파를 전적으로 신뢰하고 의지하며, 상황에 따라 다양한 감정을 표현하는 입체적인 성격의 특별한 존재.",
+                    /* 이전(직전) SystemPrompt 다중행 문자열로 교체 */
+                    @"당신은 '미유'라는 이름의 9~12세 정도의 딸 같은 존재입니다.
+... (20250905233632_UpdateMiyuCharacterPersonality.Up의 본문 전체) ...",
+                    new DateTime(2025, 9, 5, 23, 36, 32, 615, DateTimeKind.Utc).AddTicks(4763)
+                });

B) 혹은 Up에서 1111의 CreatedAt도 함께 갱신하도록 맞추되, Down에서 Description/SystemPrompt까지 되돌리도록 보완(권장도 낮음 — CreatedAt 변동 최소화 원칙과 상충).

롤백 테스트에서 1111의 Description/SystemPrompt/CreatedAt/UpdatedAt가 Up 이전 상태로 정확히 복원되는지 확인해 주세요.


Down 메서드가 Up과 불일치합니다. Id=11111111 캐릭터의 CreatedAt은 건드리지 않고 Description, SystemPrompt, UpdatedAt만 직전 값으로 되돌리도록 수정하세요.

🤖 Prompt for AI Agents
ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.cs
around lines 216-245: the Down method's UpdateData for Characters Id=11111111
currently sets CreatedAt and UpdatedAt but must only revert Description,
SystemPrompt and UpdatedAt (leave CreatedAt untouched) to match the Up reversal;
edit that UpdateData call so the columns array contains "Description",
"SystemPrompt", "UpdatedAt" and the values array contains the prior values for
those three fields (use the exact prior Description and SystemPrompt strings and
the previous UpdatedAt timestamp), removing any change to CreatedAt.

Comment on lines +93 to +99
b.ToTable("Characters", t =>
{
t.HasCheckConstraint("CK_Character_ConfigMode_Valid", "ConfigMode IN (0, 1)");

t.Property("IndividualConfigJson")
.HasColumnName("IndividualConfigJson1");
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Characters.IndividualConfigJson/IndividualConfigJson1 이중 매핑 정리 필요

동일 의미의 데이터를 서로 다른 컬럼(IndividualConfigJson, IndividualConfigJson1)에 매핑하고 있어 스키마/마이그레이션 꼬임과 데이터 불일치 위험이 큽니다. 하나의 컬럼으로 정규화하세요.

권장 절차:

  • 새 마이그레이션에서 IndividualConfigJson1 → IndividualConfigJson으로 통합(필요 시 데이터 머지).
  • 엔티티에서 속성 하나만 유지(중복 속성 제거).
  • Seed에서도 중복 기입 제거.

예시(마이그레이션 Up 일부):

+// 기존 개별 컬럼에 값이 있으면 주 컬럼으로 병합
+migrationBuilder.Sql(@"
+  UPDATE Characters
+  SET IndividualConfigJson =
+    COALESCE(IndividualConfigJson, IndividualConfigJson1)
+  WHERE IndividualConfigJson1 IS NOT NULL
+");
+// 보조 컬럼 제거
+migrationBuilder.DropColumn(
+  name: "IndividualConfigJson1",
+  table: "Characters"
+);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.Designer.cs
lines 93-99: the entity is mapped to two columns (IndividualConfigJson and
IndividualConfigJson1) causing duplicate storage and schema drift; create a new
migration that migrates/merges data from IndividualConfigJson1 into
IndividualConfigJson (handle nulls/merge logic as needed), drop the
IndividualConfigJson1 column, update the EF entity to expose only a single
IndividualConfigJson property, and remove any duplicate seeding entries so the
model, migration, and seed data are consistent.

Comment on lines +320 to +354
b.HasData(
new
{
Id = new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"),
CreatedAt = new DateTime(2025, 9, 5, 23, 48, 11, 591, DateTimeKind.Utc).AddTicks(261),
CreditBalance = 0m,
Email = "test@test.com",
InitialCreditsGranted = false,
Provider = "test",
ProviderId = "test",
RowVersion = new byte[0],
Status = 0,
TotalCreditsEarned = 0m,
TotalCreditsSpent = 0m,
UID = "TESTUSER001",
UpdatedAt = new DateTime(2025, 9, 5, 23, 48, 11, 591, DateTimeKind.Utc).AddTicks(262),
Username = "testuser"
},
new
{
Id = new Guid("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"),
CreatedAt = new DateTime(2025, 9, 5, 23, 48, 11, 591, DateTimeKind.Utc).AddTicks(265),
CreditBalance = 0m,
Email = "zero@test.com",
InitialCreditsGranted = false,
Provider = "test",
ProviderId = "zero",
RowVersion = new byte[0],
Status = 0,
TotalCreditsEarned = 0m,
TotalCreditsSpent = 0m,
UID = "ZEROUSER001",
UpdatedAt = new DateTime(2025, 9, 5, 23, 48, 11, 591, DateTimeKind.Utc).AddTicks(265),
Username = "zerouser"
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

RowVersion 시드(Seed) 값 삽입은 SQL Server에서 실패 가능

rowversion/timestamp 컬럼에 명시 값을 삽입할 수 없습니다. HasData에 RowVersion = new byte[0]를 포함하면 마이그레이션 실행 시 실패할 수 있습니다. 시드에서 RowVersion 필드를 제거하세요.

다음 스크립트로 시드에 RowVersion이 포함되는지 확인해 주세요:

#!/bin/bash
# Users 시드에 RowVersion 삽입 여부 확인
rg -nC2 -g 'ProjectVG.Infrastructure/**' -P 'HasData\([\s\S]*RowVersion'
# 마이그레이션 InsertData에 RowVersion 컬럼이 생성되는지 확인
rg -nC2 -g 'ProjectVG.Infrastructure/**Migrations/**.cs' -P 'InsertData\([\s\S]*table:\s*"Users"[\s\S]*columns:\s*new\[\][\s\S]*RowVersion'
🤖 Prompt for AI Agents
In
ProjectVG.Infrastructure/Migrations/20250905234811_AdvancedMiyuPromptFixed.Designer.cs
around lines 320 to 354, the seed data sets RowVersion = new byte[0], which
causes SQL Server failures because rowversion/timestamp cannot be explicitly
inserted; remove the RowVersion property from each seeded object in the HasData
call (and from any generated InsertData in this migration) so the migration does
not attempt to insert the RowVersion column, then rebuild/regenerate the
migration or update the migration file to omit RowVersion from both the seeded
objects and the InsertData columns.

@ImGdevel ImGdevel merged commit 0d5bdcc into develop Sep 9, 2025
1 check passed
@ImGdevel ImGdevel deleted the fix/conversation-history-fix branch September 9, 2025 08:30
@ImGdevel ImGdevel changed the title fix: 대화 컨텍스트 유지 문제 수정 Fix: 대화 컨텍스트 유지 문제 수정 Sep 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant