Skip to content

Conversation

@ImGdevel
Copy link
Member

@ImGdevel ImGdevel commented Aug 21, 2025

  • New Features
    • 캐릭터 로딩/해제 및 액션 제어를 위한 캐릭터 관리와 모델 로더 추가
    • 해상도 기반 Live2D 자동 스케일링(ResolutionManager, Live2DModelScaler) 및 카메라 스케일러 추가
    • 대화 오케스트레이션(ChatSystemManager)과 메시지 큐 도입
    • 채팅 응답에 행동(Action)·비용(Cost) 정보 표시 지원
  • Refactor
    • 기존 대화 매니저를 ChatSystemManager로 교체하며 입력 뷰 연동 업데이트
  • Documentation
    • Live2D 해상도 스케일링 가이드 추가
    • 프로젝트 구조 가이드 추가
  • Chores
    • 특정 모델 경로 .gitignore 업데이트

Summary by CodeRabbit

  • 신규 기능

    • Live2D 해상도 자동 스케일링(전역 매니저/모델 스케일러)과 카메라 해상도 스케일러 추가
    • 캐릭터 로딩·동작 제어 매니저 추가
    • 채팅 시스템 매니저 및 메시지 큐 도입으로 대화 처리 및 음성 재생 흐름 개선
    • 채팅 응답에 액션 표시와 비용 정보(사용/잔여 토큰) 표시 지원
  • 버그 수정

    • 마이크 시작 실패 시 오류 처리 및 녹음 중지 이벤트 누락 방지
    • 메인 씬 로딩 이름 오탈자 수정
    • 오디오 믹서 그룹 라벨 정정
  • 문서

    • Live2D 해상도 스케일링 가이드, 프로젝트 구조 가이드 추가
  • 기타

    • 리소스/샘플 정리 및 .gitignore 업데이트

@coderabbitai
Copy link

coderabbitai bot commented Aug 21, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

새 ChatSystemManager 및 ChatMessageQueue 추가, CharacterActionController/CharacterManager/CharacterModelLoader 및 Live2D 해상도 스케일링(ResolutionManager/Live2DModelScaler/CameraResolutionScaler/ResolutionModelScaleConfig) 도입; 기존 Live2D 컨트롤러·매니저·테스트 다수 삭제; 오디오 및 채팅 DTO 변경, .gitignore·메타·GUID 조정.

Changes

Cohort / File(s) Summary
Audio Core
Assets/Core/Audio/AudioManager.cs, Assets/Core/Audio/AudioRecorder.cs, Assets/Core/Audio/NewAudioMixer.mixer, Assets/Voice.mixer
AudioManager에 OnInitialized 이벤트 및 GetVoiceController() 추가; AudioRecorder null 처리 강화 및 StopRecording() nullable 반환; 믹서 그룹명 SFX/BGM/UI/Voice → SFXVolume/BGMVolume/UIVolume/VoiceVolume; 기존 Assets/Voice.mixer 삭제
SystemManager 연결
Assets/Core/Managers/SystemManager.cs
_chatManager 타입을 ChatManager?ChatSystemManager?로 변경, 씬 로드 오타 수정
Chat 오케스트레이션
Assets/Domain/Chat/Service/ChatSystemManager.cs, Assets/Domain/Chat/Service/ChatMessageQueue.cs, Assets/Domain/Chat/View/TextInputView.cs, Assets/Domain/Chat/View/VoiceInputView.cs
신규 ChatSystemManagerChatMessageQueue 추가; 입력 뷰들이 ChatSystemManager를 참조하도록 타입/시그니처 변경
Chat 모델/DTO
Assets/Domain/Chat/Model/CharacterActionData.cs, Assets/Domain/Chat/Model/ChatMessage.cs, Assets/Domain/Chat/Model/CostInfo.cs, Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs, Assets/Domain/Chat/Model/Actor.cs, Assets/Domain/Chat/Model/VoiceData.cs
ChatMessageActionData/CostInfo 추가 및 Metadata 제거; CharacterActionData 추가(문자열→액션 파싱); CostInfo 추가; ChatResponseaction/used_cost/remaining_cost 추가; VoiceData 기본 생성자 제거
캐릭터 액션·매니저·로더
Assets/Domain/Character/Script/CharacterActionController.cs, .../CharacterManager.cs, .../CharacterModelLoader.cs
CharacterActionController(애니메이션 액션 enum/제어) 추가, CharacterManager(로드/언로드/액션 파사드) 추가, CharacterModelLoader(비동기 모델 생성·립싱크·블링크 설정) 추가
해상도 스케일링(모델/카메라/매니저/설정)
Assets/Domain/Character/Script/Component/Live2DModelScaler.cs, .../ResolutionManager.cs, .../CameraResolutionScaler.cs, Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs, Assets/Resources/ResolutionModelScaleConfig.asset.meta
규칙·모드 기반 ResolutionModelScaleConfig(SO) 추가, Live2DModelScaler·CameraResolutionScaler·ResolutionManager 추가 및 리소스 메타 등록
구 컨트롤러/매니저/테스트 제거
Assets/Domain/Character/Script/Component/CubismHitHandler.cs, .../CubismLookTarget.cs, .../Controller/*, .../Manager/*, .../Test/*, 다수 .meta
기존의 Hit/Look/Action/Emotion/Parameter 컨트롤러, Live2D 매니저·파사드, 테스트·샘플 스크립트 및 관련 .meta 파일 대거 삭제
문서 추가
Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md, Assets/Docs/ProjectVG_Structure_Guide.md (+ .meta)
Live2D 해상도 스케일 가이드 및 프로젝트 구조 가이드 신규 추가
프로젝트 설정/메타 및 .gitignore
.gitignore, Assets/Domain/Character/Model/...*.meta, Assets/Resources/Character/Model.meta, Assets/Samples...*.meta
특정 모델 폴더(ignore) 패턴 추가, 다수 GUID/메타 업데이트 및 일부 폴더 메타 삭제

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant TextInput as TextInputView
  participant ChatMgr as ChatSystemManager
  participant API as ChatApiService
  participant WS as WebSocketManager
  participant UI as ChatBubblePanel
  participant Audio as AudioManager
  participant Char as CharacterManager

  User->>TextInput: Send message
  TextInput->>ChatMgr: SendUserMessage(message)
  ChatMgr->>Char: PlayAction(Listen)
  ChatMgr->>API: Send request
  WS-->>ChatMgr: OnChatMessageReceived(ChatMessage)
  ChatMgr->>UI: Show character bubble
  alt voice present
    ChatMgr->>Audio: Play(VoiceData)
  end
  ChatMgr->>Char: PlayAction(Talk)
  ChatMgr-->>ChatMgr: Wait (voice length or default)
  ChatMgr->>Char: PlayAction(Idle)
  ChatMgr-->>UI: OnConversationEnd
Loading
sequenceDiagram
  autonumber
  participant Sys as SystemManager
  participant Audio as AudioManager
  participant CharMgr as CharacterManager
  participant Loader as CharacterModelLoader
  participant Registry as Live2DModelRegistry

  Sys->>Audio: Initialize()
  Audio-->>Sys: OnInitialized
  Sys->>CharMgr: Initialize()
  CharMgr->>Loader: Initialize(registry, voiceSource)
  CharMgr->>Loader: LoadAndInitializeModelAsync("zero", parent)
  Loader->>Registry: Get config
  Loader-->>CharMgr: GameObject (initialized, inactive)
  CharMgr->>CharMgr: Activate & cache action controller
Loading
sequenceDiagram
  autonumber
  participant ResMgr as ResolutionManager
  participant Scaler as Live2DModelScaler
  participant Cam as CameraResolutionScaler
  Note over ResMgr,Scaler: 해상도 변경 발생 시
  ResMgr->>ResMgr: CalculateScale()
  ResMgr->>Scaler: ApplyScaleWithPreCalculatedScale(scale)
  Cam->>Cam: ApplyCameraScale()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

귀 쫑긋한 토끼가 말하네, 새 시스템이 왔다고 🐇
대화는 흐르고, 목소리는 울려 퍼지고, 모델은 화면에 꼭 맞추네.
액션은 톡, 스케일은 척, 버그는 도망가라!
깡충—다음 버전도 기대해도 좋아.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9340da5 and 5945645.

⛔ Files ignored due to path filters (2)
  • Assets/App/Scenes/MainScene.unity is excluded by !**/*.unity
  • ProjectSettings/EditorBuildSettings.asset is excluded by !**/*.asset
📒 Files selected for processing (7)
  • Assets/Core/Audio/AudioRecorder.cs (4 hunks)
  • Assets/Core/Managers/SystemManager.cs (2 hunks)
  • Assets/Domain/Character/Script/CharacterManager.cs (1 hunks)
  • Assets/Domain/Character/Script/CharacterModelLoader.cs (1 hunks)
  • Assets/Domain/Character/Script/Config/Live2DModelConfig.cs (4 hunks)
  • Assets/Domain/Chat/Service/ChatMessageQueue.cs (1 hunks)
  • Assets/Domain/Chat/Service/ChatSystemManager.cs (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/character

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 27

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
Assets/Core/Audio/AudioRecorder.cs (3)

316-321: 런타임에서 DestroyImmediate 사용은 안전하지 않습니다 — Destroy로 교체하세요.

DestroyImmediate는 에디터 전용 사용이 권장되며, 플레이 모드/런타임에서 예기치 않은 부작용을 유발할 수 있습니다. 여기서는 프레임 말에 안전하게 파괴되는 Destroy가 적절합니다.

권장(diff):

-            if (_recordingClip != null)
-            {
-                DestroyImmediate(_recordingClip);
-            }
+            if (_recordingClip != null)
+            {
+                Destroy(_recordingClip);
+            }

96-107: Microphone.Start 실패(null 반환) 경로를 처리하세요.

일부 환경에서 Microphone.Start가 예외 없이 null을 반환할 수 있습니다. 현재는 null이어도 OnRecordingStarted가 호출되어 UX가 혼동될 수 있습니다.

권장(diff):

-                _isRecording = true;
-                _recordingStartTime = Time.time;
-                
-                _recordingClip = Microphone.Start(_currentDevice != null ? _currentDevice : string.Empty, false, _maxRecordingLength, _sampleRate);
-                
-                Debug.Log($"[AudioRecorder] 음성 녹음 시작됨 (최대 {_maxRecordingLength}초, {_sampleRate}Hz)");
-                OnRecordingStarted?.Invoke();
-                
-                return true;
+                _isRecording = true;
+                _recordingStartTime = Time.time;
+
+                _recordingClip = Microphone.Start(_currentDevice ?? string.Empty, false, _maxRecordingLength, _sampleRate);
+                if (_recordingClip == null)
+                {
+                    _isRecording = false;
+                    Debug.LogError("[AudioRecorder] 마이크 시작 실패: 반환된 AudioClip이 null입니다.");
+                    OnError?.Invoke("마이크 시작 실패");
+                    return false;
+                }
+
+                Debug.Log($"[AudioRecorder] 음성 녹음 시작됨 (최대 {_maxRecordingLength}초, {_sampleRate}Hz)");
+                OnRecordingStarted?.Invoke();
+                return true;

149-158: 예외 경로에서도 항상 OnRecordingStopped를 보장하세요.

현재 catch 경로에서는 OnRecordingStopped가 호출되지 않아 구독자가 정리 로직을 받지 못할 수 있습니다. finally에서 한 번만 보장 호출하는 형태로 통합하세요.

권장(diff):

-                OnRecordingStopped?.Invoke();
-                return null;
-            }
-            catch (Exception ex)
-            {
-                Debug.LogError($"[AudioRecorder] 녹음 중지 실패: {ex.Message}");
-                OnError?.Invoke($"녹음 중지 실패: {ex.Message}");
-                _isRecording = false;
-                return null;
-            }
+                OnRecordingStopped?.Invoke();
+                return null;
+            }
+            catch (Exception ex)
+            {
+                Debug.LogError($"[AudioRecorder] 녹음 중지 실패: {ex.Message}");
+                OnError?.Invoke($"녹음 중지 실패: {ex.Message}");
+                _isRecording = false;
+                return null;
+            }
+            finally
+            {
+                // 중복 호출 방지를 위해 성공 분기에서 이미 호출했다면 옵저버 측에서 idempotent 처리 가정
+                OnRecordingStopped?.Invoke();
+            }

비고: 중복 호출이 우려되면 bool stoppedRaised 플래그로 한 번만 호출되도록 정교화할 수 있습니다.

Assets/Domain/Character/Script/Config/Live2DModelConfig.cs (1)

80-87: 용어 오타(lockAt → lookAt) 및 역호환 보장 리팩터

lockAtDamping은 시선 추적(LookAt) 컨텍스트상 lookAtDamping이 맞습니다. 필드명을 수정하되, 에셋 값 보존을 위해 FormerlySerializedAs로 역호환을 보장하고, 속성은 신규/구명 병행 노출 후 구명에 [Obsolete]를 권장합니다.

+ using UnityEngine.Serialization; // 파일 상단 using 추가
@@
- [SerializeField] private float lockAtDamping = 0.0f;
+ [SerializeField, FormerlySerializedAs("lockAtDamping")]
+ private float lookAtDamping = 0.0f;
@@
- public float LockAtDamping => lockAtDamping;
+ [Obsolete("Use LookAtDamping instead.")]
+ public float LockAtDamping => lookAtDamping;
+ public float LookAtDamping => lookAtDamping;

Also applies to: 155-159

Assets/Core/Managers/SystemManager.cs (1)

214-216: 씬 이름 오타로 인한 전환 실패 가능성: "MainSence" → "MainScene"

현재 문자열 "MainSence"는 일반적으로 오타로 보입니다. 존재하지 않는 씬 이름이면 전환 시 런타임 오류가 납니다. 아래와 같이 교체를 권장합니다.

- await UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("MainSence");
+ await UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("MainScene");
♻️ Duplicate comments (4)
Assets/Domain/Character/Script/CharacterManager.cs.meta (1)

1-2: 위와 동일: .cs.meta에 MonoImporter 블록이 없습니다 — 명시적 추가 권장.

CharacterManager는 실행 순서(Execution Order) 요구사항이 생길 수 있으므로, 기본값이라도 명시해 두면 변경/리뷰가 수월합니다. 첫 코멘트의 제안 패턴을 동일 적용해 주세요.

Assets/Domain/Character/Script/CharacterModelLoader.cs.meta (1)

1-2: 위와 동일: MonoImporter 섹션 누락.

ModelLoader는 Import 설정 변경 가능성이 높은 스크립트입니다. 추후 변경 히스토리를 위해서라도 기본 블록을 포함시켜 두는 것을 권장합니다.

Assets/Domain/Character/Script/Component/ResolutionManager.cs.meta (1)

1-2: 위와 동일: MonoImporter 섹션 누락.

Resolution 관련 컴포넌트는 에디터 간 충돌이 자주 납니다. Importer 블록을 고정해 두면 불필요한 diff를 줄일 수 있습니다.

Assets/Domain/Chat/Model/CostInfo.cs.meta (1)

1-2: 위와 동일: MonoImporter 섹션 누락.

Chat/Cost 트래킹 로직은 협업이 잦은 영역이라 메타 안정화가 중요합니다. 기본 Importer 블록을 명시해 주세요.

🧹 Nitpick comments (59)
Assets/Domain/Chat/Model/CharacterActionData.cs.meta (1)

1-2: .cs.meta에서 MonoImporter 섹션이 누락되어 있습니다 — 기본값을 명시해 두는 편이 안전합니다.

Unity가 자동으로 기본 Import 설정을 채워주긴 하지만, 팀/브랜치 간 병합 시 의도치 않은 재생성(diff churn)과 에디터별 편차를 줄이기 위해 MonoImporter 블록을 메타에 포함하는 것을 권장합니다.

아래처럼 기본 블록을 추가해 주세요(같은 패턴을 이번 PR의 모든 .cs.meta에 적용 권장).

 fileFormatVersion: 2
 guid: 333379fbd87c0304db4ae39689691795
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {fileID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
Assets/Domain/Character/Script/Component/ResolutionManager.cs.meta (1)

1-2: 파일 말미 개행(Newline at EOF) 추가를 권장합니다.

일부 도구/플러그인은 EOF 개행 유무로 불필요한 diff를 생성합니다. 한 줄 개행을 추가해 주세요.

Assets/Domain/Chat/Model/CostInfo.cs.meta (1)

1-2: EOF 개행이 없습니다 — 추가 권장.

사소하지만 CI/리뷰 노이즈를 줄이는 데 도움이 됩니다.

Assets/Core/Audio/AudioRecorder.cs (2)

282-289: 실제 샘플 수는 _recordingClip.frequency를 기반으로 계산하는 것이 더 견고합니다.

디바이스가 요청한 _sampleRate를 그대로 지원하지 않을 수 있습니다. 실측 클립의 frequency를 사용하면 왜곡 가능성을 줄일 수 있습니다.

권장(diff):

-            int actualSamples = Mathf.RoundToInt(actualDuration * _sampleRate);
+            int actualSamples = Mathf.RoundToInt(actualDuration * _recordingClip.frequency);

101-101: 널 병합 연산자로 간결화.

_currentDevice != null ? _currentDevice : string.Empty 대신 ?? 사용으로 가독성을 개선하세요.

권장(diff):

-                _recordingClip = Microphone.Start(_currentDevice != null ? _currentDevice : string.Empty, false, _maxRecordingLength, _sampleRate);
+                _recordingClip = Microphone.Start(_currentDevice ?? string.Empty, false, _maxRecordingLength, _sampleRate);
-                Microphone.End(_currentDevice != null ? _currentDevice : string.Empty);
+                Microphone.End(_currentDevice ?? string.Empty);

Also applies to: 135-135

Assets/Core/Audio/AudioManager.cs (3)

70-87: 구독 타이밍 레이스 대비: ‘이미 초기화된’ 경우도 처리하는 헬퍼 제공 제안

Initialize 직후에만 이벤트가 발행되므로, 나중에 구독한 컴포넌트는 놓칠 수 있습니다. 다음과 같이 즉시 실행/지연 구독을 통합하는 유틸을 제공하면 사용성이 좋아집니다.

추가(파일 내 임의 위치, public 메서드로):

public void WhenInitialized(Action action)
{
    if (action == null) return;
    if (_isInitialized) { action(); return; }
    OnInitialized += action;
}

사용 예:

AudioManager.Instance.WhenInitialized(() => {
    // 초기화 이후 안전한 접근
});

254-257: 내부 컨트롤러 직접 노출 최소화 및 API 단순화

직접 게터 대신 읽기 전용 프로퍼티로 단순화하면 호출부 가독성과 캡슐화가 향상됩니다.

아래처럼 메서드를 프로퍼티로 교체하는 것을 권장합니다(호출 사이트 일괄 치환 필요: GetVoiceController() → VoiceController).

-        public VoiceController? GetVoiceController()
-        {
-            return _voiceController;
-        }
+        public VoiceController? VoiceController => _voiceController;

대안: 컨크리트 타입 노출을 줄이려면 IAudioController(또는 IReadOnlyVoiceController 같은 얇은 인터페이스)로 반환 타입을 한정해 결합도를 낮출 수 있습니다.


70-87: 검증 결과: 이름 일관성 문제 없음 & OnInitialized 이벤트 구독 여부 확인 필요

  • _audio_manager, _audio_source, _is_playing 등 의심 식별자는 전체 코드베이스 스캔 결과 존재하지 않음 확인 (rg 스캔)
  • OnInitialized 이벤트는 Assets/Core/Audio/AudioManager.cs 내에서 선언 및 Invoke만 확인, 외부 구독자는 미발견됨

필요하다면 ChatSystemManager, VoiceController 등 AudioManager 초기화 완료 알림을 받아야 하는 컴포넌트에서 OnInitialized 이벤트 구독을 추가하거나, 사용되지 않는다면 이벤트 제거를 고려해주세요.

Assets/Domain/Chat/View/TextInputView.cs (2)

40-43: SetChatManager 인자 유효성 검사 추가 제안

null 전달 시 이후 호출에서 NRE가 발생할 수 있습니다. 간단한 가드를 추가해 방어적으로 처리하는 것을 권장합니다.

-        public void SetChatManager(ChatSystemManager chatManager)
-        {
-            _chatManager = chatManager;
-        }
+        public void SetChatManager(ChatSystemManager chatManager)
+        {
+            if (chatManager == null)
+            {
+                Debug.LogError("[TextInputView] ChatSystemManager가 null입니다.");
+                return;
+            }
+            _chatManager = chatManager;
+        }

76-86: 인스턴스 획득 방식 단순화 및 경고 메시지 업데이트

싱글턴을 제공하므로 Find 호출 대신 Instance 사용이 명확합니다. 또한 로그 메시지의 클래스명이 갱신되지 않았습니다.

-                _chatManager = FindAnyObjectByType<ChatSystemManager>();
+                _chatManager = ChatSystemManager.Instance;
                 if (_chatManager == null)
                 {
-                    Debug.LogWarning("[TextInputView] ChatManager를 찾을 수 없습니다. 수동으로 SetChatManager를 호출해주세요.");
+                    Debug.LogWarning("[TextInputView] ChatSystemManager를 찾을 수 없습니다. 수동으로 SetChatManager를 호출해주세요.");
                 }
Assets/Docs/ProjectVG_Structure_Guide.md (4)

31-38: 'Script' vs 'Scripts' 혼용 — 디렉터리명 일관화 제안

Domain 계층에서는 Script/를, UI 계층에서는 Scripts/를 사용하고 있어 혼용됩니다. 검색/탐색성과 신규 온보딩 가독성을 위해 복수형(Scripts/)으로 통일하는 것을 권장합니다.

아래와 같이 트리 예시를 조정하는 패치 제안:

-│   │   ├── Script/                
+│   │   ├── Scripts/               
...
-│   │   ├── Script/
+│   │   ├── Scripts/

Also applies to: 47-47


39-41: 트리 도식의 브랜치 기호 오타

Popup 하위에서 자식 노드 두 개가 모두 └──로 표기되어 있습니다. 첫 자식은 ├──, 마지막 자식만 └──가 되어야 시각적으로 올바릅니다.

-│   │   └── System/                # 팝업 매니저, 팝업 큐
-│   │   └── Instances/             # 실제 팝업 프리팹들
+│   │   ├── System/                # 팝업 매니저, 팝업 큐
+│   │   └── Instances/             # 실제 팝업 프리팹들

51-56: Resources 사용 경계 조건을 문서에 명시해주세요

Resources/는 런타임 상주 특성으로 메모리 압력을 높이므로, 대용량 에셋은 Addressables 우선, Resources에는 경량 설정/플레이스홀더만 권장 같은 한 줄 가이드를 추가하면 실무 적용 시 안전합니다. 또한 AddressablesDummy/의 사용 시점(개발/테스트 한정)을 명시하면 더 명확합니다.


90-97: 관련 가이드에 하이퍼링크 추가 제안

Live2D 활용 예시 아래에 이번 PR에 포함된 Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md를 교차 링크하면 독자가 흐름(Structure → Live2D 해상도 스케일링)으로 이어서 읽기 좋습니다.

Assets/Core/Audio/NewAudioMixer.mixer (1)

65-65: Exposed Parameter 미설정 — 볼륨 제어 전략 확인 필요

m_ExposedParameters: []입니다. 코드에서 AudioMixer.SetFloat("BGMVolume", ...) 등으로 볼륨을 제어하려면 각 그룹의 Volume 파라미터를 Expose해야 합니다(그룹 선택 → Volume 우클릭 → Expose). 스냅샷 기반이 아니라 직접 제어라면 파라미터 노출을 추가해 주세요.

Assets/Domain/Chat/Model/CostInfo.cs (2)

11-14: 비즈니스 의미 명확화(단위/명명) 검토

UsedCost, RemainingCost가 토큰 수를 의미한다면 UsedTokens, RemainingTokens 같은 명칭이 오해를 줄입니다. 금액(통화)이면 float는 반올림 오차가 있어 부적합하므로 decimal을 검토해야 합니다. 현재 DTO(ChatResponse)가 float?이므로 일단 정합성은 맞습니다.


26-29: 로그/표시 문자열의 문화권 독립 및 소수점 포맷 고정

UI/로그 혼용 시 문화권에 따라 소수점/구분자가 달라질 수 있습니다. 불필요한 소수점 꼬리 방지를 위해 고정 포맷과 InvariantCulture를 권장합니다.

- return $"Used: {UsedCost} Token, Remaining: {RemainingCost} Token";
+ using System.Globalization; // 파일 상단에 추가
+ return string.Format(
+   CultureInfo.InvariantCulture,
+   "Used: {0:0.##} Tokens, Remaining: {1:0.##} Tokens",
+   UsedCost, RemainingCost);
Assets/Domain/Character/Script/Config/Live2DModelConfig.cs (2)

151-153: ScriptableObject 외부 변형 방지(읽기 전용 인터페이스 노출)

에디터/런타임에서 List<> 참조가 그대로 노출되면 외부 코드가 에셋 내용을 변경할 수 있습니다. 프로퍼티 타입을 IReadOnlyList<>로 노출하고 내부 목록만 List<>로 유지하는 것을 권장합니다.

- public List<EmotionMapping> EmotionMappings => emotionMappings;
- public List<ActionMapping> ActionMappings => actionMappings;
+ public IReadOnlyList<EmotionMapping> EmotionMappings => emotionMappings;
+ public IReadOnlyList<ActionMapping> ActionMappings => actionMappings;

93-99: 기본값 및 범위 설정 합리적 — 연동 컴포넌트와의 계약만 명시되면 충분

립싱크/오토 블링크 토글과 타이밍 파라미터 구성은 실무에서 쓰기 좋습니다. 추후 CharacterModelLoader에서 이 값들이 어떤 컴포넌트(CubismAudioMouthInput, CubismAutoEyeBlinkInput 등)에 어떻게 매핑되는지(단위/스케일)만 주석으로 합의해 두면 유지보수성이 더 좋아집니다.

Also applies to: 104-137, 160-169

Assets/Domain/Character/Script/CharacterActionController.cs (3)

48-49: 불필요한 로그 제거

Debug.Log(actionType);는 상위 로깅(70라인)과 중복되어 콘솔 노이즈만 증가합니다. 제거를 권장합니다.

-               Debug.Log(actionType);

52-67: Animator 파라미터 문자열 의존 제거(해시 사용) 및 트리거 정리

문자열 기반 SetTrigger/SetBool은 GC/성능 및 오타 리스크가 있습니다. 파라미터 해시를 캐싱하고, 정지 시 관련 트리거를 ResetTrigger로 정리하면 안전합니다.

+       private static readonly int HashIdle = Animator.StringToHash("Idle");
+       private static readonly int HashListen = Animator.StringToHash("Listen");
+       private static readonly int HashTalk = Animator.StringToHash("Talk");
@@
-                       _animator.SetTrigger("Idle");
-                       _animator.SetBool("Talk", false);
+                       _animator.SetTrigger(HashIdle);
+                       _animator.SetBool(HashTalk, false);
@@
-                       _animator.SetTrigger("Listen");
-                       _animator.SetBool("Talk", false);
+                       _animator.SetTrigger(HashListen);
+                       _animator.SetBool(HashTalk, false);
@@
-                       _animator.SetBool("Talk", true);
+                       _animator.SetBool(HashTalk, true);
@@
-           _animator.SetTrigger("Idle");
-           _animator.SetBool("Talk", false);
+           _animator.ResetTrigger(HashListen);
+           _animator.ResetTrigger(HashIdle);
+           _animator.SetTrigger(HashIdle);
+           _animator.SetBool(HashTalk, false);

Also applies to: 86-87


36-43: Animator 파라미터 존재 여부 사전 검증 권장

런타임에 미존재 파라미터를 호출하면 경고가 발생합니다. Initialize 시 한 번 Animator.parameters를 검사해 누락된 파라미터를 로그로 명시하면 원인 추적이 쉬워집니다.

예시:

 public void Initialize(Animator animator)
 {
     _animator = animator;
     _isPlaying = false;
     _currentAction = CharacterActionType.Idle;
+    // 파라미터 사전 검증(옵션)
+    var names = new HashSet<string>();
+    foreach (var p in _animator.parameters) names.Add(p.name);
+    if (!names.Contains("Idle") || !names.Contains("Listen") || !names.Contains("Talk"))
+        Debug.LogWarning("[CharacterActionController] Animator 파라미터(Idle/Listen/Talk) 누락 감지");
 }
Assets/Core/Managers/SystemManager.cs (2)

23-23: 미사용 직렬화 필드 정리 또는 실제 초기화에 통합 필요

_chatManager 필드가 선언만 되어 있고 전역적으로 사용되지 않습니다. Inspector 노출만 하고 사용하지 않으면 유지보수 시 혼란을 줄 수 있습니다. 아래 중 하나를 권장합니다.

  • 단순 제거
  • 또는 매니저 생성/초기화 흐름에 통합해서 실제로 참조 사용

Option A — 제거:

-[SerializeField] private ChatSystemManager? _chatManager;

Option B — 생성 로직에 통합(아래 CreateManagersIfNotExist 코멘트 참고) 후 사용 지점 추가.


228-238: ChatSystemManager 인스턴스 확보 누락

다른 매니저들은 CreateManagersIfNotExist에서 인스턴스를 확보하지만 ChatSystemManager는 누락되어 있습니다. 싱글톤 패턴과 일관성을 위해 함께 확보해 두는 것이 좋습니다.

 private void CreateManagersIfNotExist()
 {
     if (_createManagersIfNotExist)
     {
         _webSocketManager = WebSocketManager.Instance;
         _sessionManager = SessionManager.Instance;
         _httpApiClient = HttpApiClient.Instance;
         _audioManager = AudioManager.Instance;
         _loadingManager = LoadingManager.Instance;
+        _chatManager = ChatSystemManager.Instance;
     }
 }

추가로 상태 로깅(LogManagerStatus)에도 Chat 상태 출력이 있으면 디버깅에 유용합니다.

Assets/Domain/Chat/View/VoiceInputView.cs (3)

240-247: 초기화 레이스 컨디션 방지: 초기화 완료 전 버튼 비활성화

FindAnyObjectByType<ChatSystemManager>()로 참조는 얻었지만, 내부적으로 ChatSystemManagerInitializeWhenReady 코루틴을 통해 지연 초기화됩니다. 초기화 완료 전 음성 전송을 시도하면 사용자 버블만 생성되고 실제 전송이 누락될 수 있습니다.

권장: ChatSystemManager.IsInitialized 확인 전까지 음성 버튼 비활성화 또는 대기.

예시:

 private void SetupChatManager()
 {
     if (_chatManager == null)
     {
         _chatManager = FindAnyObjectByType<ChatSystemManager>();
         if (_chatManager == null)
         {
             Debug.LogWarning("[VoiceInputView] ChatManager를 찾을 수 없습니다. 수동으로 SetChatManager를 호출해주세요.");
+            UpdateVoiceButtonState(false);
+            return;
         }
     }
+
+    // 초기화 대기 코루틴 시작
+    StartCoroutine(WaitUntilChatReady());
 }
+
+private System.Collections.IEnumerator WaitUntilChatReady()
+{
+    while (_chatManager != null && !_chatManager.IsInitialized)
+        yield return null;
+    UpdateVoiceButtonState(true);
+}

80-101: 전송 전 가드 추가: ChatSystemManager 준비 여부 확인

SendVoiceMessage에서 _chatManager?.SendUserMessage를 호출하기 전에 매니저 존재 및 초기화 여부를 검사해 사용자에게 명확한 피드백을 주는 것이 좋습니다.

-    if (!string.IsNullOrWhiteSpace(transcribedText))
+    if (!string.IsNullOrWhiteSpace(transcribedText))
     {
-        _chatManager?.SendUserMessage(transcribedText);
-        OnVoiceMessageSent?.Invoke(transcribedText);
+        if (_chatManager == null)
+        {
+            OnError?.Invoke("ChatSystemManager를 찾을 수 없습니다.");
+            return;
+        }
+        if (!_chatManager.IsInitialized)
+        {
+            OnError?.Invoke("채팅 시스템이 아직 준비되지 않았습니다. 잠시 후 다시 시도해 주세요.");
+            return;
+        }
+        _chatManager.SendUserMessage(transcribedText);
+        OnVoiceMessageSent?.Invoke(transcribedText);
     }

126-127: 불필요한 상태값 제거로 간소화

_recordingStartTime은 더 이상 사용되지 않습니다(최대 시간 체크 로직 제거 주석과 일치). 상태 관리 단순화를 위해 할당 및 필드를 제거하는 것이 좋습니다.

적용 예:

- private float _recordingStartTime;
...
- _recordingStartTime = Time.time;
Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs (2)

23-25: Action을 문자열로 노출하기보다 강타입(enum) 또는 입력 검증을 고려해주세요

서버가 "idle/listen/talk" 외의 문자열을 보낼 경우, 도메인 레이어까지 그대로 전파됩니다. 최소한 허용 값 화이트리스트 검증 또는 StringEnumConverter 기반 역직렬화를 권장합니다. 현재 PR에서는 CharacterActionData에서 파싱을 하고 있지만, DTO 단계에서 1차 방어를 해두면 디버깅과 유효성 보장이 쉬워집니다.

적용 예시(선호):

-        [JsonProperty("action")]
-        public string? Action { get; set; }
+        [JsonProperty("action")]
+        public string? Action { get; set; } // 유지
+        // TODO: DTO 수준에서 허용 문자열("idle","listen","talk") 외 입력 시 null 처리 또는 로그

35-40: 금액/크레딧 의미의 Cost를 float 대신 double/decimal로 저장하는 것을 권장

float는 표현 정밀도가 낮아 누적 오차가 발생하기 쉽습니다. 비용/잔액 성격이라면 double(성능 우선) 또는 decimal(금융/정밀 우선)로 변경을 검토해주세요. 또한 서버에서 0 또는 음수(정책상 불가) 케이스가 들어올 때의 해석(절삭/절대값/무시)도 명확히 해두면 좋습니다.

Assets/Domain/Chat/Model/CharacterActionData.cs (4)

48-54: 문자열 소문자 변환은 ToLowerInvariant() 사용 권장

문화권에 따른 소문자 규칙 차이를 피하기 위해 ToLower() 대신 ToLowerInvariant() 사용이 안전합니다.

-            return actionString.ToLower() switch
+            return actionString.ToLowerInvariant() switch
             {
                 "idle" => CharacterActionType.Idle,
                 "listen" => CharacterActionType.Listen,
                 "talk" => CharacterActionType.Talk,
                 _ => CharacterActionType.Idle
             };

2-4: 레이어 결합도 완화: Chat 모델이 Character.Service 네임스페이스에 의존

Chat 도메인 모델이 CharacterActionType(현재 CharacterActionController.cs에 정의)에 직접 의존합니다. enum을 공용 도메인(예: ProjectVG.Domain.Shared 또는 ProjectVG.Domain.Character.Abstractions)으로 분리하면 계층 간 결합도를 낮출 수 있습니다.


1-1: 불필요한 using 정리

UnityEngine 네임스페이스를 사용하지 않습니다. 불필요한 의존성 제거를 권장합니다.

-using UnityEngine;

43-47: 기본값/비정상 값의 정책 합리화 제안

null/empty는 Talk, 미인식 문자열은 Idle로 매핑됩니다. 둘 모두 동일 기본값(예: Idle 또는 Talk)으로 통일하거나, 미인식 입력을 로그로 남기고 Idle로 폴백하는 등 정책을 명문화하면 유지보수성이 좋아집니다.

Assets/Domain/Chat/Model/ChatMessage.cs (3)

27-31: 오디오 Base64 파싱 실패 시 방어 코드 권장

외부 입력이므로 FromBase64에서 예외가 발생할 수 있습니다. 실패 시 로그만 남기고 텍스트 메시지 처리는 계속되도록 try/catch를 권장합니다.

-            if (!string.IsNullOrEmpty(response.AudioData))
-            {
-                chatMessage.VoiceData = VoiceData.FromBase64(response.AudioData, response.AudioFormat);
-            }
+            if (!string.IsNullOrEmpty(response.AudioData))
+            {
+                try
+                {
+                    chatMessage.VoiceData = VoiceData.FromBase64(response.AudioData, response.AudioFormat);
+                }
+                catch (Exception ex)
+                {
+                    Debug.LogWarning($"[ChatMessage] 오디오 데이터 파싱 실패: {ex.Message}");
+                }
+            }

44-46: HasActionData 논리 재점검 필요

CharacterActionData.HasAction() 수정(항상 true → 명시 여부 반영)에 맞춰, 현재 구현은 그대로 사용해도 됩니다. 다만 ActionData를 생성 시점 조건부로 바꾸면(위 제안) 이 메서드는 ActionData != null만으로 충분해집니다.

-        public bool HasActionData() => ActionData != null && ActionData.HasAction();
+        public bool HasActionData() => ActionData != null;

32-35: CostInfo 조건 생성 로직은 합리적이나, 0 값도 의미 있는 경우가 있는지 확인

현재는 0/0이면 생성하지 않습니다. “잔여 크레딧만 의미 있음(사용 0)” 또는 “정확한 사용량을 UI에 항상 표시” 같은 요구가 있다면, 생성 조건을 ‘null 여부’로 바꾸는 옵션도 검토해주세요.

Assets/Domain/Character/Script/CharacterModelLoader.cs (3)

30-34: Initialize 인자 유효성 확인 및 NRT 명시 권장

modelRegistry가 null일 수 있는 경로가 있다면 조기에 방어하세요. 또한 본 파일은 NRT가 꺼져 있어 메서드 시그니처만으로 null 허용 여부가 드러나지 않습니다. #nullable enable 도입 또는 파라미터에 ? 표시를 권장합니다.

-        public void Initialize(Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource)
+        public void Initialize(Live2DModelRegistry modelRegistry, AudioSource voiceAudioSource)
         {
-            _modelRegistry = modelRegistry;
+            if (modelRegistry == null)
+            {
+                Debug.LogError("[CharacterModelLoader] Live2DModelRegistry가 null입니다.");
+                return;
+            }
+            _modelRegistry = modelRegistry;
             _voiceAudioSource = voiceAudioSource;
         }

72-88: null 반환 가능성이 있는 메서드의 반환 형식 명시

설정 누락 시 return null이 여러 곳에 존재합니다. API 명확성을 위해 UniTask<GameObject?>로 바꾸거나, 예외/Result 패턴으로 의도를 드러내는 것을 권장합니다. 현재 사용처(CharacterManager 등)에서 null 처리가 가능한지 확인 필요합니다.


126-135: 매직 스트링 상수화 제안: "ParamMouthOpenY"

파라미터 ID는 오타·변경 리스크가 큽니다. 상수로 추출해 재사용하면 유지보수에 유리합니다.

Assets/Domain/Character/Script/Component/ResolutionManager.cs (3)

81-88: FindObjectsOfType에서 비활성(Disabled) 오브젝트 포함 여부 확인

현재는 활성 오브젝트만 수집됩니다. 비활성 모델도 초기 스케일 적용 대상이라면 includeInactive 지원 버전(Unity 2020.2+)에서는 FindObjectsOfType<Live2DModelScaler>(true)를, 그 이전 버전이면 적절한 대안(Resources.FindObjectsOfTypeAll + 필터)을 사용하세요.

-            var scalers = FindObjectsOfType<Live2DModelScaler>();
+            var scalers = FindObjectsOfType<Live2DModelScaler>(true); // Unity 2020.2+인 경우

142-153: 신규 등록 스케일러에 즉시 스케일 적용 고려

등록 직후 다음 해상도 변경까지 기다리지 않고 현재 스케일을 바로 적용하면 일관된 UX를 만들 수 있습니다.

             if (scaler != null && !modelScalers.Contains(scaler))
             {
                 modelScalers.Add(scaler);
+                if (scaleConfig != null)
+                {
+                    var scale = scaleConfig.CalculateScale();
+                    scaler.ApplyScaleWithPreCalculatedScale(scale);
+                }

189-193: 해상도 변경 감지 시 Vector2 대신 정수 비교 또는 Vector2Int 사용 권장

현재 Vector2로 저장하고 비교하지만, 해상도는 정수 개념입니다. Vector2Int 또는 width/height 정수 비교가 더 명확합니다.

-            var currentResolution = new Vector2(Screen.width, Screen.height);
-            return currentResolution != lastResolution;
+            var currentResolution = new Vector2(Screen.width, Screen.height);
+            return currentResolution != lastResolution; // 기능은 동일하나 Vector2Int 사용을 고려
Assets/Domain/Chat/Service/ChatMessageQueue.cs (3)

20-23: Count/IsEmpty는 현재 락 없이 읽혀 비일관 상태를 노출할 수 있음

Enqueue/Dequeue/Clear는 락으로 보호되지만, Count/IsEmpty는 락 없이 읽혀 경쟁 상황에서 잘못된 값을 반환할 수 있습니다. GetStatus()가 이미 스냅샷을 안전하게 제공하므로, 두 프로퍼티도 락으로 보호하거나 외부에는 GetStatus()만 노출하는 것을 권장합니다.

다음과 같이 최소 수정 가능합니다:

-        public int Count => _messageQueue.Count;
-        public bool IsProcessing => _isProcessing;
-        public bool IsEmpty => _messageQueue.Count == 0;
+        public int Count { get { lock (_queueLock) { return _messageQueue.Count; } } }
+        public bool IsProcessing { get { lock (_queueLock) { return _isProcessing; } } }
+        public bool IsEmpty { get { lock (_queueLock) { return _messageQueue.Count == 0; } } }

42-49: 큐 가득 참 시에도 콜백으로 신호를 주십시오

현재는 경고 로그만 출력하고 false를 반환합니다. 상위(예: UI/매니저)가 대응할 수 있도록 OnError를 함께 호출하는 것이 좋습니다.

                 if (_messageQueue.Count >= _maxQueueSize) {
                     Debug.LogWarning($"[ChatMessageQueue] 메시지 큐가 가득 찼습니다. (최대: {_maxQueueSize})");
-                    return false;
+                    OnError?.Invoke($"메시지 큐가 가득 찼습니다. (최대: {_maxQueueSize})");
+                    return false;
                 }

80-112: UniTaskVoid 남용 및 취소 불가 처리 — 취소 토큰을 받아 propagate 하거나 UniTask로 전환 권장

UniTaskVoid는 예외 전파/취소가 어렵습니다. 큐 처리 중 객체 파괴/씬 전환 시 안전하게 중단하려면 CancellationToken을 받아 내부 await에 전달하거나 반환 형식을 UniTask로 바꾸고 호출부에서 .Forget()을 명시적으로 사용하는 구조가 좋습니다.

필요 시 아래와 같이 선택적으로 개선할 수 있습니다(호출부 동반 변경 필요):

-        public async UniTaskVoid ProcessQueueAsync(Func<ChatMessage, UniTask> processAction)
+        public async UniTask ProcessQueueAsync(Func<ChatMessage, UniTask> processAction, CancellationToken cancellationToken = default)
         {
             lock (_queueLock)
             {
                 if (_isProcessing)
                     return;
                 _isProcessing = true;
             }

             try {
-                while (true) {
+                while (!cancellationToken.IsCancellationRequested) {
                     ChatMessage? message = Dequeue();
                     
                     if (message == null) {
                         break;
                     }

                     try {
-                        await processAction(message);
+                        await processAction(message).AttachExternalCancellation(cancellationToken);
                         OnMessageProcessed?.Invoke(message);
Assets/Domain/Character/Script/Component/Live2DModelScaler.cs (3)

75-80: 사전 계산 스케일 적용 경로에서 scaleConfig null 시 조기 반환은 과도함

ApplyScaleWithPreCalculatedScale(float scale)는 이미 외부에서 계산된 스케일을 적용하는 목적이므로, scaleConfig == null이어도 적용 가능해야 합니다. 현재는 경고 후 return하여 적용 자체가 막힙니다. 로그만 남기고 적용은 계속하도록 변경하세요.

         public void ApplyScaleWithPreCalculatedScale(float scale)
         {
             if (scaleConfig == null)
             {
                 Debug.LogWarning("[Live2DModelScaler] 스케일 설정이 없습니다.");
-                return;
+                // 정보 로그만 남기고 적용은 계속 진행
             }

134-148: 수동 스케일 범위 검증 누락

음수/비정상 스케일이 적용될 수 있습니다. 최소 0 이상으로 클램프하거나 scaleConfig의 Min/Max를 고려하세요.

         public void SetManualScale(float scale)
         {
             if (originalScale == Vector3.zero)
             {
                 originalScale = transform.localScale;
+                _originalScaleCaptured = true;
             }
-            
-            currentScale = scale;
+            currentScale = Mathf.Max(0f, scale);
             transform.localScale = originalScale * scale;

42-48: ResolutionManager와 중복 적용 가능성 — 역할 분담 정리 필요

Update에서 해상도 변화를 감지해 자체 적용하고, 동시에 ResolutionManager도 주기적으로 전체 적용을 수행합니다. 중복 적용/중복 로그가 발생할 수 있으니, 매니저 사용 시 이 컴포넌트의 applyOnResolutionChange를 끄거나, 매니저 존재 시 자체 적용을 생략하는 가드가 필요합니다.

Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md (1)

29-31: 코드 펜스 언어 지정 누락(MD040) — lint 경고 해결

이 블록은 언어 지정이 없어 markdownlint에 걸립니다. text 언어로 명시하세요.

-   ```
+   ```text
    Assets > Create > ProjectVG > Character > Resolution Model Scale Config
    ```
Assets/Domain/Chat/Service/ChatSystemManager.cs (3)

189-195: 디버그 잔존 로그 정리

Debug.Log(_chracterManager);/Debug.Log(chatMessage.ActionData);는 노이즈만 유발합니다. 필요 시 조건부 로그로 전환하거나 제거하세요.

-                Debug.Log(_chracterManager);
                 // 캐릭터 액션 실행
-                if (_chracterManager != null)
+                if (_characterManager != null)
                 {
-                    Debug.Log(chatMessage.ActionData);
-                    _chracterManager.PlayAction(chatMessage.ActionData);
+                    _characterManager.PlayAction(chatMessage.ActionData);
                 }

76-82: UI 패널 미탐 시 완전 미초기화 — 기능 최소화 초기화로 degrade 권장

5초 타임아웃 후 yield break로 종료하면 WebSocket 구독/큐 초기화도 이뤄지지 않습니다. UI 없이도 동작 가능한 범위(네트워크 수신/큐/오디오 등)는 초기화하도록 변경하는 것을 권장합니다.

             if (_chatBubblePanel == null) {
                 Debug.LogError("[ChatSystemManager] ChatBubblePanel 초기화 타임아웃");
-                yield break;
+                // UI 없이도 가능한 초기화는 시도
+                Initialize();
+                yield break;
             }

120-145: UX: 사용자 말풍선은 전송 즉시 표시하는 편이 자연스러움

현재는 API 응답 전송 이후에 사용자 버블을 생성합니다. 네트워크 지연이 있을 때 UX가 어색합니다. 버블을 먼저 그리고, 실패 시 에러 표시만 별도로 처리하는 흐름을 권장합니다.

         public async void SendUserMessage(string message)
         {
             if (!ValidateUserInput(message)) { return; }

             try {
+                if (_chatBubblePanel != null) {
+                    _chatBubblePanel.CreateBubble(Actor.User, message);
+                }
                 // 유저 메시지 전송 시 캐릭터를 Listen 상태로 변경
-                if (_chracterManager != null)
+                if (_characterManager != null)
                 {
                     var listenAction = new CharacterActionData(CharacterActionType.Listen);
-                    _chracterManager.PlayAction(listenAction);
+                    _characterManager.PlayAction(listenAction);
                 }

                 if (_chatApiService != null) {
                     var response = await _chatApiService.SendChatAsync(
                         message: message,
                         characterId: _characterId,
                         userId: _userId
                     );
                     if (response == null) {
                         Debug.LogWarning("[ChatSystemManager] 채팅 응답이 null입니다.");
                     }
                 }
-                if (_chatBubblePanel != null) {
-                    _chatBubblePanel.CreateBubble(Actor.User, message);
-                }
Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs (2)

181-196: 불필요한 계산 제거 및 주석 보완

baseAspect는 사용되지 않습니다. 제거해 코드 명료성을 높이세요.

-            var currentAspect = resolution.x / resolution.y;
-            var baseAspect = baseResolution.x / baseResolution.y;
+            var currentAspect = resolution.x / resolution.y;

또한 이 방식은 세로/가로에 따라 높이/너비 중 하나만 정규화합니다. ResolutionModelScaleConfig의 스케일과 곱할 때 중복 보정이 될 수 있으므로, 설계 의도(이중 보정인지, 보조 계수인지)를 주석으로 명확히 남기는 것을 권장합니다.


152-176: 스케일 합성 로직의 의도 확인(중복 보정 위험)

카메라 스케일 = “해상도 비율 × Config 스케일”로 계산합니다. ResolutionModelScaleConfig.CalculateScale() 자체가 이미 해상도 기반 스케일이라면 이중 보정 가능성이 있습니다. 의도대로라면 둘 중 하나를 선택/가중하는 파라미터가 필요합니다(예: useConfigOnly, blendFactor).

구현 의도를 문서/설정으로 확인해 주시고, 필요 시 파라미터 추가를 제안드립니다.

Assets/Domain/Character/Script/CharacterManager.cs (3)

48-54: 스케일러 자동 부착: _modelTransform이 null인 경우 커버리지 부족

_modelTransform이 미설정이면 스케일러가 전혀 부착되지 않습니다. 프리팹 루트(실제 인스턴스)에 스케일러가 없으면 로딩 완료 시점에 부착하는 보완 로직을 권장합니다.

원하시면 로딩 완료 시 _currentCharacterLive2DModelScaler 존재 여부를 점검하고, 없을 경우 자동 부착하는 패치를 제안드릴게요.


145-164: AudioManager 초기화 타이밍 이슈 — Voice AudioSource 재획득/재바인딩 필요

GetVoiceAudioSource()는 초기화 시점에만 1회 조회합니다. 오디오 매니저가 늦게 초기화되는 경우 입모양(LipSync)에 오디오 입력이 영구히 누락될 수 있습니다. AudioManager의 초기화 완료 이벤트(요약에 따르면 OnInitialized 존재)를 구독하여 재획득/재바인딩하는 로직을 추가하는 것을 권장합니다.

AudioManager에 OnInitialized 이벤트가 맞는지 확인 부탁드립니다. 존재한다면 다음과 같은 형태로 보완 가능합니다:

// Initialize 내
if (AudioManager.Instance != null)
{
    AudioManager.Instance.OnInitialized += () =>
    {
        var source = GetVoiceAudioSource();
        _modelLoader.Initialize(_modelRegistry, source); // 필요 시 현재 모델의 mouth input에도 재바인딩
    };
}

20-21: 이름 일관성: Service → Controller

필드명 _currentActionService는 타입 CharacterActionController와 불일치합니다. _currentActionController로 변경하면 가독성이 향상됩니다.

-    private CharacterActionController _currentActionService;
+    private CharacterActionController _currentActionController;

참고: 해당 식별자의 모든 사용처도 함께 변경 필요.

Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs (2)

33-44: 빈번 호출 시 미미하지만 오버헤드 존재 — 캐시/지연평가 고려(선택)

CalculateScale()가 프레임마다 호출되는 구조라면 Screen.width/height 조회 및 내부 로직을 캐시하거나 변경 이벤트(해상도 변경/세이프 영역 변경) 기반으로 갱신하는 것이 유리합니다.

원하시면 ResolutionManager에서 이벤트 구독 방식으로 스케일을 브로드캐스트하는 구조 제안 드릴게요.


204-220: 모바일 판정 휴리스틱 보강 제안(선택)

세로모드+높이 1000px 기준만으로는 태블릿/세로 모니터(피벗) 환경을 오탐/미탐할 수 있습니다. DPI, 안전영역, 짧은 변 길이(예: min(width,height) < 임계값) 등을 추가 고려하면 정확도를 높일 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0c28c9d and 9340da5.

⛔ Files ignored due to path filters (33)
  • Assets/App/Scenes/MainSence.unity is excluded by !**/*.unity
  • Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Model.fadeMotionList.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natoriConfig.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_Sample.prefab is excluded by !**/*.prefab
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Angry.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Blushing.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Normal.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Sad.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Smile.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/Surprised.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_01.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_02.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_03.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_04.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/exp/exp_05.exp3.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_00.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_01.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_02.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_03.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_04.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_05.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_06.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/motions/mtn_07.fade.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.4096/texture_00.png is excluded by !**/*.png
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06.prefab is excluded by !**/*.prefab
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/natori_pro_t06MaskTexture.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.expressionList.asset is excluded by !**/*.asset
  • Assets/Domain/Character/Model/Sample/natori_pro_en/runtime/runtime.fadeMotionList.asset is excluded by !**/*.asset
  • Assets/Resources/Character/Model/CharacterZero.asset is excluded by !**/*.asset
  • Assets/Resources/Live2DModelRegistry.asset is excluded by !**/*.asset
  • Assets/Resources/ResolutionModelScaleConfig.asset is excluded by !**/*.asset
📒 Files selected for processing (81)
  • .gitignore (1 hunks)
  • Assets/Core/Audio/AudioManager.cs (3 hunks)
  • Assets/Core/Audio/AudioRecorder.cs (1 hunks)
  • Assets/Core/Audio/NewAudioMixer.mixer (4 hunks)
  • Assets/Core/Managers/SystemManager.cs (1 hunks)
  • Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md (1 hunks)
  • Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md.meta (1 hunks)
  • Assets/Docs/ProjectVG_Structure_Guide.md (1 hunks)
  • Assets/Docs/ProjectVG_Structure_Guide.md.meta (1 hunks)
  • Assets/Domain/Character/Model/Chikuwa.meta (1 hunks)
  • Assets/Domain/Character/Model/Chikuwa/Character-Zero.asset.meta (1 hunks)
  • Assets/Domain/Character/Model/Chikuwa/model.meta (1 hunks)
  • Assets/Domain/Character/Model/Model.fadeMotionList.asset.meta (1 hunks)
  • Assets/Domain/Character/Model/Sample.meta (1 hunks)
  • Assets/Domain/Character/Script/CharacterActionController.cs (1 hunks)
  • Assets/Domain/Character/Script/CharacterActionController.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/CharacterManager.cs (1 hunks)
  • Assets/Domain/Character/Script/CharacterManager.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/CharacterModelLoader.cs (1 hunks)
  • Assets/Domain/Character/Script/CharacterModelLoader.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs (1 hunks)
  • Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/Component/CubismHitHandler.cs (0 hunks)
  • Assets/Domain/Character/Script/Component/CubismHitHandler.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Component/CubismLookTarget.cs (0 hunks)
  • Assets/Domain/Character/Script/Component/CubismLookTarget.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Component/Live2DModelScaler.cs (1 hunks)
  • Assets/Domain/Character/Script/Component/Live2DModelScaler.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/Component/ResolutionManager.cs (1 hunks)
  • Assets/Domain/Character/Script/Component/ResolutionManager.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/Config/Live2DModelConfig.cs (3 hunks)
  • Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs (1 hunks)
  • Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs.meta (1 hunks)
  • Assets/Domain/Character/Script/Controller/ActionController.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/ActionController.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/DTOs.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/DTOs.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/EmotionController.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/EmotionController.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/IActionController.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/IActionController.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/IEmotionController.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/IEmotionController.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Controller/Live2DParameterController.cs (0 hunks)
  • Assets/Domain/Character/Script/Controller/Live2DParameterController.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs (0 hunks)
  • Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs (0 hunks)
  • Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Manager/Live2DModelManager.cs (0 hunks)
  • Assets/Domain/Character/Script/Manager/Live2DModelManager.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Test/Live2DModelTest.cs (0 hunks)
  • Assets/Domain/Character/Script/Test/Live2DModelTest.cs.meta (0 hunks)
  • Assets/Domain/Character/Script/Test/TestVoice.cs (0 hunks)
  • Assets/Domain/Character/Script/Test/TestVoice.cs.meta (0 hunks)
  • Assets/Domain/Chat/Model/Actor.cs (1 hunks)
  • Assets/Domain/Chat/Model/CharacterActionData.cs (1 hunks)
  • Assets/Domain/Chat/Model/CharacterActionData.cs.meta (1 hunks)
  • Assets/Domain/Chat/Model/ChatMessage.cs (1 hunks)
  • Assets/Domain/Chat/Model/CostInfo.cs (1 hunks)
  • Assets/Domain/Chat/Model/CostInfo.cs.meta (1 hunks)
  • Assets/Domain/Chat/Model/VoiceData.cs (0 hunks)
  • Assets/Domain/Chat/Service/ChatManager.cs (0 hunks)
  • Assets/Domain/Chat/Service/ChatMessageQueue.cs (1 hunks)
  • Assets/Domain/Chat/Service/ChatMessageQueue.cs.meta (1 hunks)
  • Assets/Domain/Chat/Service/ChatSystemManager.cs (1 hunks)
  • Assets/Domain/Chat/View/TextInputView.cs (3 hunks)
  • Assets/Domain/Chat/View/VoiceInputView.cs (3 hunks)
  • Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs (2 hunks)
  • Assets/Resources/Character/Model.meta (0 hunks)
  • Assets/Resources/ResolutionModelScaleConfig.asset.meta (1 hunks)
  • Assets/Samples.meta (0 hunks)
  • Assets/Samples/Core.meta (0 hunks)
  • Assets/Samples/Core/Managers.meta (0 hunks)
  • Assets/Samples/Core/Managers/SampleSystemManager.cs (0 hunks)
  • Assets/Samples/Core/Managers/SampleSystemManager.cs.meta (0 hunks)
  • Assets/Voice.mixer (0 hunks)
💤 Files with no reviewable changes (39)
  • Assets/Domain/Character/Script/Controller/IActionController.cs
  • Assets/Samples.meta
  • Assets/Samples/Core/Managers/SampleSystemManager.cs
  • Assets/Resources/Character/Model.meta
  • Assets/Domain/Character/Script/Controller/Live2DParameterController.cs.meta
  • Assets/Samples/Core.meta
  • Assets/Domain/Character/Script/Component/CubismHitHandler.cs
  • Assets/Domain/Character/Script/Test/Live2DModelTest.cs.meta
  • Assets/Domain/Character/Script/Component/CubismLookTarget.cs.meta
  • Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs
  • Assets/Domain/Character/Script/Manager/Live2DModelManager.cs.meta
  • Assets/Domain/Character/Script/Controller/Live2DModelApplier.cs.meta
  • Assets/Domain/Character/Script/Controller/Live2DParameterController.cs
  • Assets/Domain/Character/Script/Controller/ActionController.cs.meta
  • Assets/Domain/Character/Script/Controller/DTOs.cs
  • Assets/Domain/Chat/Model/VoiceData.cs
  • Assets/Domain/Character/Script/Controller/EmotionController.cs
  • Assets/Voice.mixer
  • Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs
  • Assets/Domain/Character/Script/Component/CubismHitHandler.cs.meta
  • Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs.meta
  • Assets/Samples/Core/Managers.meta
  • Assets/Domain/Character/Script/Manager/ILive2DCharacterManager.cs
  • Assets/Domain/Character/Script/Controller/IActionController.cs.meta
  • Assets/Samples/Core/Managers/SampleSystemManager.cs.meta
  • Assets/Domain/Character/Script/Manager/Live2DCharacterManager.cs.meta
  • Assets/Domain/Character/Script/Test/Live2DModelTest.cs
  • Assets/Domain/Character/Script/Controller/EmotionController.cs.meta
  • Assets/Domain/Character/Script/Test/TestVoice.cs
  • Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs
  • Assets/Domain/Chat/Service/ChatManager.cs
  • Assets/Domain/Character/Script/Controller/ActionController.cs
  • Assets/Domain/Character/Script/Component/CubismLookTarget.cs
  • Assets/Domain/Character/Script/Controller/IEmotionController.cs.meta
  • Assets/Domain/Character/Script/Manager/Live2DModelManager.cs
  • Assets/Domain/Character/Script/Controller/ILive2DModelApplier.cs.meta
  • Assets/Domain/Character/Script/Controller/DTOs.cs.meta
  • Assets/Domain/Character/Script/Test/TestVoice.cs.meta
  • Assets/Domain/Character/Script/Controller/IEmotionController.cs
🧰 Additional context used
🧬 Code graph analysis (17)
Assets/Core/Managers/SystemManager.cs (1)
Assets/Domain/Chat/Service/ChatSystemManager.cs (1)
  • ChatSystemManager (20-252)
Assets/Core/Audio/AudioManager.cs (1)
Assets/Core/Audio/VoiceController.cs (1)
  • VoiceController (10-216)
Assets/Domain/Character/Script/CharacterManager.cs (4)
Assets/Domain/Character/Script/CharacterModelLoader.cs (2)
  • CharacterModelLoader (15-184)
  • Initialize (30-34)
Assets/Domain/Character/Script/CharacterActionController.cs (5)
  • CharacterActionController (15-110)
  • Initialize (25-30)
  • PlayAction (36-77)
  • StopCurrentAction (82-91)
  • IsPlaying (97-100)
Assets/Domain/Chat/Model/CharacterActionData.cs (4)
  • CharacterActionData (7-56)
  • CharacterActionData (18-21)
  • CharacterActionData (27-30)
  • HasAction (36-36)
Assets/Core/Audio/AudioManager.cs (1)
  • AudioManager (11-341)
Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs (1)
Assets/Domain/Character/Script/Component/ResolutionManager.cs (1)
  • ResolutionModelScaleConfig (169-172)
Assets/Domain/Chat/Model/CharacterActionData.cs (1)
Assets/Domain/Character/Script/CharacterActionController.cs (1)
  • CharacterActionType (106-109)
Assets/Domain/Chat/View/VoiceInputView.cs (2)
Assets/Domain/Chat/Service/ChatSystemManager.cs (1)
  • ChatSystemManager (20-252)
Assets/Domain/Chat/View/TextInputView.cs (1)
  • SetChatManager (40-43)
Assets/Domain/Chat/Model/ChatMessage.cs (2)
Assets/Domain/Chat/Model/CharacterActionData.cs (4)
  • CharacterActionData (7-56)
  • CharacterActionData (18-21)
  • CharacterActionData (27-30)
  • HasAction (36-36)
Assets/Domain/Chat/Model/CostInfo.cs (3)
  • CostInfo (15-15)
  • CostInfo (17-21)
  • HasCostInfo (23-23)
Assets/Domain/Character/Script/CharacterActionController.cs (2)
Assets/Domain/Chat/Model/CharacterActionData.cs (1)
  • CharacterActionType (43-55)
Assets/Domain/Character/Script/CharacterManager.cs (3)
  • Initialize (32-55)
  • PlayAction (102-110)
  • StopCurrentAction (115-118)
Assets/Domain/Character/Script/CharacterModelLoader.cs (3)
Assets/Domain/Character/Script/CharacterManager.cs (2)
  • AudioSource (145-164)
  • Initialize (32-55)
Assets/Domain/Character/Script/CharacterActionController.cs (2)
  • Initialize (25-30)
  • CharacterActionController (15-110)
Assets/Domain/Character/Script/Config/Live2DModelRegistry.cs (1)
  • TryGetConfig (18-30)
Assets/Domain/Chat/Service/ChatSystemManager.cs (9)
Assets/Domain/Chat/View/ChatBubblePanel.cs (2)
  • ChatBubblePanel (11-252)
  • CreateBubble (55-107)
Assets/Domain/Character/Script/CharacterManager.cs (3)
  • CharacterManager (13-167)
  • Initialize (32-55)
  • PlayAction (102-110)
Assets/Infrastructure/Network/WebSocket/WebSocketManager.cs (1)
  • WebSocketManager (14-472)
Assets/Core/Audio/AudioManager.cs (1)
  • AudioManager (11-341)
Assets/Domain/Chat/Service/ChatMessageQueue.cs (4)
  • ChatMessageQueue (13-129)
  • ChatMessageQueue (27-30)
  • ChatMessage (58-63)
  • Enqueue (37-52)
Assets/Infrastructure/Network/Services/ApiServiceManager.cs (1)
  • ApiServiceManager (9-53)
Assets/Domain/Chat/Model/CharacterActionData.cs (4)
  • CharacterActionData (7-56)
  • CharacterActionData (18-21)
  • CharacterActionData (27-30)
  • CharacterActionType (43-55)
Assets/Domain/Chat/Model/ChatMessage.cs (1)
  • ChatMessage (18-38)
Assets/Domain/Chat/Model/VoiceData.cs (3)
  • VoiceData (13-18)
  • VoiceData (20-40)
  • IsPlayable (191-191)
Assets/Domain/Character/Script/Component/Live2DModelScaler.cs (2)
Assets/Domain/Character/Script/Component/ResolutionManager.cs (4)
  • ResolutionModelScaleConfig (169-172)
  • ResolutionManager (12-232)
  • RegisterModelScaler (142-153)
  • UnregisterModelScaler (158-164)
Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs (1)
  • CalculateScale (32-44)
Assets/Core/Audio/AudioRecorder.cs (2)
Assets/Domain/Chat/Model/ChatMessage.cs (1)
  • AudioClip (48-48)
Assets/Core/Audio/VoiceController.cs (1)
  • AudioClip (193-196)
Assets/Domain/Character/Script/Component/CameraResolutionScaler.cs (2)
Assets/Domain/Character/Script/Component/ResolutionManager.cs (1)
  • ResolutionModelScaleConfig (169-172)
Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs (1)
  • CalculateScale (32-44)
Assets/Domain/Chat/Model/CostInfo.cs (2)
Assets/Domain/Chat/Model/ChatMessage.cs (2)
  • Serializable (8-50)
  • HasCostInfo (46-46)
Assets/Infrastructure/Network/DTOs/Chat/ChatResponse.cs (1)
  • Serializable (8-43)
Assets/Domain/Character/Script/Component/ResolutionManager.cs (2)
Assets/Domain/Character/Script/Component/Live2DModelScaler.cs (3)
  • Live2DModelScaler (9-237)
  • ApplyScaleWithPreCalculatedScale (73-112)
  • GetCurrentScale (153-156)
Assets/Domain/Character/Script/Config/ResolutionModelScaleConfig.cs (2)
  • CalculateScale (32-44)
  • ResolutionInfo (190-202)
Assets/Domain/Chat/View/TextInputView.cs (2)
Assets/Domain/Chat/Service/ChatSystemManager.cs (1)
  • ChatSystemManager (20-252)
Assets/Domain/Chat/View/VoiceInputView.cs (1)
  • SetChatManager (75-78)
Assets/Domain/Chat/Service/ChatMessageQueue.cs (1)
Assets/Domain/Chat/Service/ChatSystemManager.cs (1)
  • UniTask (182-218)
🪛 markdownlint-cli2 (0.17.2)
Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md

10-10: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Comment on lines +122 to +123
/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa
/Assets/Domain/Character/Model/Chikuwa/model
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

디렉터리 무시 패턴을 명확히 하고 .gitkeep 예외를 추가하세요.

현재는 디렉터리 경로 뒤에 슬래시가 없어 파일명과 충돌 여지가 있습니다. 또한 해당 폴더를 완전히 무시하면 Git이 빈 폴더를 생성하지 않아 Unity에서 model.meta만 존재하고 실제 폴더가 없어질 수 있습니다. 빌드/에셋 탐색 시 폴더 부재로 흐름이 끊길 수 있으니, 디렉터리 패턴에 슬래시를 붙이고 .gitkeep 예외를 추가해 폴더를 보존하는 것을 권장합니다.

적용 제안(diff):

 /Assets/Plugins/FiveMinuteChat
 /Assets/Plugins/WebGLTemplates
-/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa
-/Assets/Domain/Character/Model/Chikuwa/model
+/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa/
+/Assets/Domain/Character/Model/Chikuwa/model/
+!/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa/.gitkeep
+!/Assets/Domain/Character/Model/Chikuwa/model/.gitkeep

추가 작업:

  • 위 두 디렉터리에 빈 .gitkeep 파일을 커밋해 폴더를 저장소에 유지하세요.
📝 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
/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa
/Assets/Domain/Character/Model/Chikuwa/model
/Assets/Plugins/FiveMinuteChat
/Assets/Plugins/WebGLTemplates
/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa/
/Assets/Domain/Character/Model/Chikuwa/model/
!/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa/.gitkeep
!/Assets/Domain/Character/Model/Chikuwa/model/.gitkeep
🤖 Prompt for AI Agents
In .gitignore around lines 122-123, the two entries target directories but lack
trailing slashes and thus can match files; update the patterns to include
trailing slashes (e.g. /Assets/Domain/Character/Model/Chikuwa/ziraitikuwa/) and
add negation rules to allow .gitkeep files (e.g.
!/Assets/Domain/Character/Model/Chikuwa/ziraitikuwa/.gitkeep), and do the same
for the second path; then create and commit an empty .gitkeep in each directory
so Git preserves the folders.

Comment on lines +52 to +55
1. **CharacterFacade 사용 시**
- 모델 스케일러가 자동으로 추가됨
- 별도 설정 불필요

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

CharacterFacade 자동 적용 부분 사실 확인 필요

가이드에 CharacterFacade 사용 시 자동 추가된다고 되어 있으나, 코드베이스에 해당 파사드가 보이지 않습니다. 존재하지 않거나 비활성화된 기능이면 문서를 정정해야 합니다.


🏁 Script executed:

#!/bin/bash
# CharacterFacade 존재 여부 확인
fd -a --exclude node_modules --exclude Library --exclude Packages --exclude ProjectSettings 'CharacterFacade.*\.(cs|md|asmdef|meta)$'
rg -nP '\bCharacterFacade\b' -C2 --glob '!**/Library/**' --glob '!**/node_modules/**' Assets

Length of output: 595


CharacterFacade 기능 미존재 확인: 가이드 수정 필요

코드베이스 전반을 검색한 결과 CharacterFacade 관련 구현(.cs, .asmdef 등)이 존재하지 않으며, 문서에서 “CharacterFacade 사용 시 자동 적용”으로 안내된 기능을 찾을 수 없습니다. 따라서 해당 설명은 삭제하거나 실제 적용 가능한 대체 API/클래스명으로 수정해야 합니다.

수정 대상:

  • 파일: Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md
  • 라인: 52–55

제안하는 diff:

- 1. **CharacterFacade 사용 시**
-    - 모델 스케일러가 자동으로 추가됨
-    - 별도 설정 불필요
+ 1. **(해당 기능 구현 미지원)**
+    - 현재 `CharacterFacade` 관련 자동 스케일러 적용 기능은 제공되지 않습니다.
+    - 향후 지원 시점에 맞추어 이 가이드를 업데이트해주세요.
📝 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
1. **CharacterFacade 사용 시**
- 모델 스케일러가 자동으로 추가됨
- 별도 설정 불필요
1. **(해당 기능 구현 미지원)**
- 현재 `CharacterFacade` 관련 자동 스케일러 적용 기능은 제공되지 않습니다.
- 향후 지원 시점에 맞추어 이 가이드를 업데이트해주세요.
🤖 Prompt for AI Agents
Assets/Docs/Guides/Live2D_Resolution_Scaling_Guide.md lines 52-55: the doc
claims "CharacterFacade 사용 시 모델 스케일러가 자동으로 추가됨" but no CharacterFacade
implementation exists in the repo; remove this incorrect section or replace it
with the correct API/class name that actually enables automatic scaler (search
the codebase for the actual entry point or component that adds the scaler and
use that name), update the text to either state that automatic scaler is not
available and must be added manually (and provide brief steps) or reference the
correct class/API, and run a repo-wide search to confirm the replacement is
present elsewhere before committing.

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a07cad638f33a9e448551011b2f085bf
guid: 2fc0b961763d08c47b49035b4dfb295d
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

폴더 GUID 변경 영향 점검 및 폴더 보존.

폴더 GUID 변경은 일반적으로 큰 문제는 없지만, Addressables/툴링에서 폴더 GUID를 참조하는 경우가 드물게 있어 영향 점검이 필요합니다. 또한 .gitignore로 폴더 전체를 무시하면 폴더 자체가 저장소에 존재하지 않을 수 있으니, .gitkeep 예외로 폴더를 보존하세요(별도 코멘트 참고).

빠른 점검 스크립트:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== 폴더 GUID 직접 참조 여부(YAML 내) =="
rg -n --no-messages -g '!Library/**' -g '!Logs/**' '2fc0b961763d08c47b49035b4dfb295d' || echo "직접 참조 없음(일반적)"

Length of output: 266


폴더 GUID 직접 참조 없음 확인, .gitkeep로 폴더 보존 필요

빠른 스크립트 검증 결과, GUID 2fc0b961763d08c47b49035b4dfb295d는 해당 폴더의 model.meta 파일 외에는 코드베이스 내에서 직접 참조되는 부분이 없습니다.
따라서 Addressables나 기타 툴링에서의 영향은 없음을 확인했습니다.

– 여전히 .gitignore에 폴더 단위를 무시할 경우 폴더 자체가 저장소에 남지 않으므로,
해당 빈 폴더(Assets/Domain/Character/Model/Chikuwa/)에 .gitkeep 파일을 추가하여 폴더를 유지해주세요.

🤖 Prompt for AI Agents
Assets/Domain/Character/Model/Chikuwa/model.meta (lines 2-2): the folder
contains only a model.meta with GUID 2fc0b961763d08c47b49035b4dfb295d and will
be removed from the repo if the folder is empty due to .gitignore rules; add a
.gitkeep file into Assets/Domain/Character/Model/Chikuwa/ (and commit it) to
ensure the empty folder is preserved in the repository, and verify .gitignore
does not exclude .gitkeep so the folder remains tracked.

Comment on lines +2 to +5
guid: b1083908ab87d904a95806ad80fc75eb
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 24100000
mainObjectFileID: 11400000
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

GUID 및 mainObjectFileID 변경: 참조/타입 일치 검증 필요

GUID 교체(→ b1083908ab87d904a95806ad80fc75eb)와 메인 오브젝트 fileID 변경(24100000 → 11400000)은 씬/프리팹에서 서브에셋 참조가 깨질 수 있는 고위험 변경입니다. 특히 YAML 참조는 {fileID, guid, type} 조합으로 링크되므로 기존 조합이 남아있는지 확인을 권장합니다.

변경 후 씬/프리팹에서 관련 애니메이션/페이드 리스트가 정상 재생되는지 수동 재현 테스트도 권장합니다.


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
OLD_GUID="3bf46f7daa3260847aa1b964ad2b01a7"
# 이전 GUID 사용 흔적 검색(참조 잔재 확인)
rg -nP -C2 "$OLD_GUID" -g '!Library/**' -- '*.unity' '*.prefab' '*.asset' || echo "OK: 이전 GUID 참조 없음"

# fileID-참조 흔적(문맥 기반) 확인: 이전 fileID 숫자 주변을 스캔
rg -nP -C2 '24100000' -g '!Library/**' -- '*.unity' '*.prefab' '*.asset' || echo "참고: 이전 fileID 직접 참조 미발견"

Length of output: 625


다음과 같이 검색 범위와 파일 필터링을 수정하여 다시 한 번 이전 GUID 및 fileID 참조를 확인해 주세요.


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

OLD_GUID="3bf46f7daa3260847aa1b964ad2b01a7"
OLD_FILEID="24100000"

echo "=== 이전 GUID 참조 검색 (.unity, .prefab, .asset, .asset.meta) ==="
rg -nP -C2 "$OLD_GUID" \
  -g '!Library/**' \
  -g '*.unity' \
  -g '*.prefab' \
  -g '*.asset' \
  -g '*.asset.meta' \
  || echo "OK: 이전 GUID 참조 없음"

echo "=== 이전 fileID 참조 검색 (.unity, .prefab, .asset) ==="
rg -nP -C2 "$OLD_FILEID" \
  -g '!Library/**' \
  -g '*.unity' \
  -g '*.prefab' \
  -g '*.asset' \
  || echo "참고: 이전 fileID 직접 참조 미발견"

Length of output: 278148


⚠️ 이전 GUID 참조가 씬에 남아있습니다.

다음 경로에서 guid: 3bf46f7daa3260847aa1b964ad2b01a7(이전 GUID) 참조가 발견되었습니다. 이 값을 새 GUID(b1083908ab87d904a95806ad80fc75eb)로 모두 교체하거나, Unity 에디터에서 에셋 재연결을 수행해 주세요.

  • Assets/App/Scenes/MainSence.unity (줄 528)
    OutputAudioMixerGroup: {fileID: 24300002, guid: 3bf46f7daa3260847aa1b964ad2b01a7, type: 2}
    guid 값을 새 GUID로 업데이트하거나, 에디터에서 해당 AudioMixerGroup을 다시 연결해야 합니다.

이 외에도 Prefab(.prefab)·Asset(.asset)·Meta(.asset.meta) 파일 전역에서 guid: 3bf46f7daa3260847aa1b964ad2b01a7를 검색하여 누락된 참조가 없는지 반드시 검증해 주세요. 또한, fileID 변경(24100000 → 11400000)으로 인해 연결이 깨진 서브애셋이 없는지 수동 재생 테스트를 추천드립니다.

🤖 Prompt for AI Agents
In Assets/Domain/Character/Model/Model.fadeMotionList.asset.meta around lines 2
to 5, the file's GUID was changed to b1083908ab87d904a95806ad80fc75eb but other
assets (notably Assets/App/Scenes/MainSence.unity line 528) still reference the
old guid 3bf46f7daa3260847aa1b964ad2b01a7; update all occurrences of the old
GUID to the new GUID (or reassign the asset in the Unity Editor) and verify any
fileID changes (24100000 → 11400000) haven't broken sub-asset links; search the
repo for guid: 3bf46f7daa3260847aa1b964ad2b01a7 and replace or reconnect each
reference, then open the scene/prefabs in Unity to re-link the AudioMixerGroup
or affected assets and run a manual playtest to confirm everything loads
correctly.

Comment on lines +23 to +26
void Start()
{
Initialize();
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

비동기 호출 미대기(unobserved)로 예외 누락 가능 — Start에서 명시적으로 await 권장

Initialize() 내부에서 LoadCharacter("zero")를 호출하지만 await하지 않아, 로드 중 발생하는 예외가 놓칠 수 있습니다. Unity에서는 라이프사이클 메서드를 async로 선언해도 됩니다. Start에서 명시적으로 await하고, Initialize에서는 더 이상 호출하지 않는 것이 깔끔합니다.

@@
-    void Start()
-    {
-        Initialize();
-    }
+    async void Start()
+    {
+        Initialize();
+        await LoadCharacter("zero");
+    }
@@
-            // 임시로 zero 캐릭터 로드
-            LoadCharacter("zero");
+            // 캐릭터 로드는 Start()에서 await로 처리

Also applies to: 45-47

🤖 Prompt for AI Agents
In Assets/Domain/Character/Script/CharacterManager.cs around lines 23-26 (and
similarly 45-47), Start calls Initialize() without awaiting it, causing any
exceptions from the async LoadCharacter("zero") flow to be unobserved; make
Start async and explicitly await Initialize() there, and remove the internal
call to Initialize() from Initialize itself (or ensure Initialize no longer
invokes LoadCharacter synchronously) so the async flow is awaited at the
lifecycle entry point.

Comment on lines 23 to 25
[SerializeField] private ChatBubblePanel? _chatBubblePanel;
[SerializeField] private CharacterManager? _chracterManager;

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

오탈자 필드명(_chracterManager) → 직렬화 호환 유지하며 정정 권장

가독성과 일관성을 위해 _characterManager로 변경하되, 씬/프리팹 직렬화가 깨지지 않도록 FormerlySerializedAs를 사용하세요.

 using ProjectVG.Domain.Character.Service;
+using UnityEngine.Serialization;
@@
-        [SerializeField] private CharacterManager? _chracterManager;
+        [SerializeField, FormerlySerializedAs("_chracterManager")] private CharacterManager? _characterManager;

이후 사용처도 모두 _characterManager로 교체해야 합니다:

-                if (_chracterManager != null)
+                if (_characterManager != null)
@@
-                    _chracterManager.PlayAction(listenAction);
+                    _characterManager.PlayAction(listenAction);
@@
-                if (_chracterManager != null)
+                if (_characterManager != null)
@@
-                    _chracterManager.PlayAction(chatMessage.ActionData);
+                    _characterManager.PlayAction(chatMessage.ActionData);
@@
-                if (_chracterManager != null)
+                if (_characterManager != null)
@@
-                    _chracterManager.PlayAction(idleAction);
+                    _characterManager.PlayAction(idleAction);
🤖 Prompt for AI Agents
In Assets/Domain/Chat/Service/ChatSystemManager.cs around lines 23-25, rename
the misspelled serialized field `_chracterManager` to `_characterManager` and
add the UnityEngine.Serialization.FormerlySerializedAs attribute pointing to "
_chracterManager" so scene/prefab serialization stays intact; also add or ensure
`using UnityEngine.Serialization;` is present at the top, then update every
usage in this file to the new `_characterManager` identifier.

Comment on lines +75 to 78
public void SetChatManager(ChatSystemManager chatManager)
{
_chatManager = chatManager;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

SetChatManager 시그니처 변경에 따른 호출부 정합성 확인 필요

메서드 시그니처가 SetChatManager(ChatSystemManager)로 변경되었습니다. 기존 호출부에서 구형 타입을 넘기던 곳이 있으면 컴파일 에러가 납니다. 리포지토리 전반 점검을 권장합니다.

다음 스크립트로 호출부를 점검하세요.


🏁 Script executed:

#!/bin/bash
# SetChatManager 호출부 점검
rg -nP --glob '!**/Library/**' --glob '!**/ProjectSettings/**' '\bSetChatManager\s*\(' -C2

# 구형 ChatManager 타입 직접 참조 점검
rg -nP --glob '!**/Library/**' --glob '!**/ProjectSettings/**' '\bChatManager\b' -C2

Length of output: 59777


문서 예제 내 SetChatManager 시그니처 반영 필요

SetChatManager 메서드 시그니처가 ChatManagerChatSystemManager로 변경되었으나, 코드 호출부는 존재하지 않아 컴파일 오류는 발생하지 않습니다. 다만 아래 README 문서의 예제에서는 여전히 _chatManager(ChatManager 타입)를 넘기고 있어 불일치가 발생하니, 문서 예제를 ChatSystemManager 사용 형태로 업데이트해야 합니다.

수정 대상:

  • Assets/Domain/Chat/View/README_InputViews.md
    • 예제 호출부 (_textInputView.SetChatManager(_chatManager); / _voiceInputView.SetChatManager(_chatManager);)
    • 2834, 9099, 109112, 136143 라인 등

변경 예시:

-    private ChatManager _chatManager;
+    private ChatSystemManager _chatSystemManager;

     private void Start()
     {
-        _textInputView.SetChatManager(_chatManager);
-        _voiceInputView.SetChatManager(_chatManager);
+        _textInputView.SetChatManager(_chatSystemManager);
+        _voiceInputView.SetChatManager(_chatSystemManager);
     }
📝 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
public void SetChatManager(ChatSystemManager chatManager)
{
_chatManager = chatManager;
}
private ChatSystemManager _chatSystemManager;
private void Start()
{
_textInputView.SetChatManager(_chatSystemManager);
_voiceInputView.SetChatManager(_chatSystemManager);
}
🤖 Prompt for AI Agents
In Assets/Domain/Chat/View/VoiceInputView.cs around lines 75 to 78, the
SetChatManager signature was changed from ChatManager to ChatSystemManager;
update the README examples accordingly: open
Assets/Domain/Chat/View/README_InputViews.md and replace example calls that pass
a ChatManager (e.g. _textInputView.SetChatManager(_chatManager);
_voiceInputView.SetChatManager(_chatManager);) with the ChatSystemManager type,
ensuring the variable names and type in the examples match the new signature
across the specified ranges (lines ~28–34, 90–99, 109–112, 136–143) so examples
compile and reflect current API.

@ImGdevel ImGdevel changed the title Feature/character Feature: 캐릭터 모델 기능 추가 Aug 21, 2025
@ImGdevel ImGdevel merged commit 2bec04a into develop Aug 21, 2025
@ImGdevel ImGdevel deleted the feature/character branch August 21, 2025 15:18
This was referenced Aug 26, 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.

2 participants