- /admin/ai-crm: AI 업셀링 추천 생성 현황 대시보드 (통계 카드 + 로그 테이블 + 아코디언 상세) - 마이페이지 바텀시트: 터치 드래그로 닫기 기능 추가 (80px 임계값) - Windows 콘솔 UTF-8 인코딩 강제 (app.py, clawdbot_client.py) - admin.html 헤더에 AI CRM 네비 링크 추가 - docs: ai-upselling-crm.md, windows-utf8-encoding.md 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
75 lines
2.6 KiB
Markdown
75 lines
2.6 KiB
Markdown
# Windows 콘솔 한글 인코딩 (UTF-8) 가이드
|
||
|
||
## 문제
|
||
Windows 콘솔 기본 인코딩이 `cp949`여서 Python에서 한글 출력 시 깨짐 발생.
|
||
Claude Code bash 터미널, cmd, PowerShell 모두 동일 증상.
|
||
|
||
```
|
||
# 깨진 출력 예시
|
||
{"product": "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD>", "message": "<22>迵<EFBFBD><E8BFB5><EFBFBD>, ..."}
|
||
```
|
||
|
||
## 해결: 3단계 방어
|
||
|
||
### 1단계: Python 파일 상단 — sys.stdout UTF-8 래핑
|
||
```python
|
||
import sys
|
||
import os
|
||
|
||
if sys.platform == 'win32':
|
||
import io
|
||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||
os.environ.setdefault('PYTHONIOENCODING', 'utf-8')
|
||
```
|
||
|
||
**적용 위치**: `app.py`, `clawdbot_client.py` 등 진입점 파일 맨 위 (import 전)
|
||
|
||
> 모듈로 import되는 파일은 `hasattr(sys.stdout, 'buffer')` 체크 추가:
|
||
> ```python
|
||
> if sys.platform == 'win32':
|
||
> import io
|
||
> if hasattr(sys.stdout, 'buffer'):
|
||
> sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||
> sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||
> ```
|
||
|
||
### 2단계: 환경변수 — PYTHONIOENCODING
|
||
```bash
|
||
# ~/.bashrc (Claude Code bash 세션)
|
||
export PYTHONIOENCODING=utf-8
|
||
```
|
||
|
||
또는 실행 시:
|
||
```bash
|
||
PYTHONIOENCODING=utf-8 python backend/app.py
|
||
```
|
||
|
||
### 3단계: json.dumps — ensure_ascii=False
|
||
```python
|
||
import json
|
||
data = {"product": "비타민C", "message": "추천드려요"}
|
||
print(json.dumps(data, ensure_ascii=False, indent=2))
|
||
```
|
||
`ensure_ascii=False` 없으면 `\uBE44\uD0C0\uBBFCC` 같은 유니코드 이스케이프로 출력됨.
|
||
|
||
## 프로젝트 내 적용 현황
|
||
|
||
| 파일 | 방식 |
|
||
|------|------|
|
||
| `backend/app.py` | sys.stdout 래핑 + PYTHONIOENCODING |
|
||
| `backend/services/clawdbot_client.py` | sys.stdout 래핑 (buffer 체크) |
|
||
| `backend/ai_tag_products.py` | sys.stdout 래핑 |
|
||
| `backend/view_products.py` | sys.stdout 래핑 |
|
||
| `backend/import_il1beta_foods.py` | sys.stdout 래핑 |
|
||
| `backend/import_products_from_mssql.py` | sys.stdout 래핑 |
|
||
| `backend/update_product_category.py` | sys.stdout 래핑 |
|
||
| `backend/gui/check_cash.py` | `sys.stdout.reconfigure(encoding='utf-8')` |
|
||
| `backend/gui/check_sunab.py` | `sys.stdout.reconfigure(encoding='utf-8')` |
|
||
| `~/.bashrc` | `export PYTHONIOENCODING=utf-8` |
|
||
|
||
## 주의사항
|
||
- Flask 로거(`logging.info()` 등)도 stderr로 출력하므로 **stderr도 반드시 래핑**
|
||
- `io.TextIOWrapper`는 이미 래핑된 스트림에 중복 적용하면 에러남 → `hasattr(sys.stdout, 'buffer')` 체크
|
||
- PyQt GUI에서는 stdout이 다를 수 있음 → `hasattr` 가드 필수
|