Phase 26: P1 architecture refactor — DI container, service layer, async callbacks

- config.py: APIConfig + AppConfig dataclasses, env vars centralized
- api_client.py: APIClientProtocol (Protocol) + HTTPAPIClient class, remove module-level globals
- services.py: ChatService, DocumentService, TTSService (TTS moved from app.py)
- container.py: manual DI container with lazy singleton properties
- app.py: all callbacks converted to async, asyncio.run() fully removed, container wired in
- .env.example: add TTS_EDGE_VOICE entry
- ROADMAP.md: P0/P1 checklist updated to reflect completed work

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sal
2026-06-01 17:36:35 +09:00
parent be4b7c40cb
commit d81a2f5888
7 changed files with 307 additions and 177 deletions
+13 -11
View File
@@ -5,12 +5,13 @@
| 항목 | 현재 상태 | 심각도 |
|------|---------|--------|
| 아키텍처 모듈화 | UI·비즈니스 로직 혼재 (2파일) | 🔴 높음 |
| Windows 호환성 | TTS `say` 명령어 — macOS 전용 | 🔴 즉시 |
| Gradio Chatbot 타입 | `type="messages"` 누락 | 🔴 즉시 |
| JSON yield 타입 불일치 | `JSONDecodeError` 시 타입 혼용 | 🟡 중간 |
| run_id 인덱싱 버그 | `history` / `run_ids` 동기화 취약 | 🟡 중간 |
| async/sync 혼용 | 동기 콜백에서 `run_until_complete` | 🟡 중간 |
| 코드 중복 | `run_until_complete` 패턴 5회 반복 | 🟡 중간 |
| Windows 호환성 | ~~TTS `say` 명령어 — macOS 전용~~ → 크로스플랫폼 구현 완료 | ✅ 완료 |
| Gradio Chatbot 타입 | ~~`type="messages"` 누락~~ → Gradio 6.x 기본 포맷 사용 | ✅ 완료 |
| JSON yield 타입 불일치 | ~~`JSONDecodeError` 시 타입 혼용~~`str()` 변환 적용 | ✅ 완료 |
| run_id 인덱싱 버그 | ~~`history` / `run_ids` 동기화 취약~~ → 방어 로직 추가 | ✅ 완료 |
| RAG 출처 표시 | ~~thinking 박스에 혼재~~ → 답변 하단 `📄 출처` 박스로 분리 | ✅ 완료 |
| async/sync 혼용 | 동기 콜백에서 `asyncio.run()` 사용 (5곳) | 🟡 중간 |
| 코드 중복 | `asyncio.run()` 패턴 5회 반복 | 🟡 중간 |
| 결합도 | `api_client` 직접 임포트·전역 상태 | 🔴 높음 |
| 테스트 가능성 | ~20% (모킹 불가능) | 🔴 낮음 |
| 로깅 | `print()` 만 사용 | 🟢 낮음 |
@@ -281,13 +282,14 @@ youlbot-webui/
- [x] `gr.Chatbot` — Gradio 6.x 기본 dict 포맷 사용 (`type` 파라미터 불필요, 제거)
- [x] `api_client.py` JSON yield 타입 수정
- [x] `run_id` 인덱싱 방어 로직 추가
- [x] RAG 출처 전용 박스 분리 — `source_box` gr.HTML + `_sources_html()` + `__sources` 토큰 처리
### P1
- [ ] `config.py` 작성
- [ ] `api_client.py` — `APIClientProtocol` + `HTTPAPIClient` 분리
- [ ] `services.py` 작성 (ChatService, DocumentService, TTSService)
- [ ] `container.py` 작성
- [ ] `app.py` — 모든 콜백 async 전환 및 container 사용
- [x] `config.py` 작성 (APIConfig, AppConfig)
- [x] `api_client.py` — `APIClientProtocol` + `HTTPAPIClient` 분리
- [x] `services.py` 작성 (ChatService, DocumentService, TTSService)
- [x] `container.py` 작성 (lazy singleton 프로퍼티)
- [x] `app.py` — 모든 콜백 async 전환 및 container 사용 (`asyncio.run()` 완전 제거)
### P2
- [ ] `logging` 모듈 도입