# π¨ νλ‘ νΈμλ νμΌ κ΅¬μ‘° λ¬Έμ
## π λλ ν 리 ꡬ쑰
```
audio_validation_app/
βββ app.py # κΈ°μ‘΄ νμΌ (λ°±μ
μ©)
βββ app_new.py # μλ‘μ΄ λ©μΈ μ± (μ»΄ν¬λνΈ λΆλ¦¬ λ²μ )
β
βββ frontend/ # π νλ‘ νΈμλ λλ ν 리
β βββ __init__.py
β β
β βββ app_ui.py # UI 쑰립 λ©μΈ νμΌ
β β
β βββ components/ # UI μ»΄ν¬λνΈ
β β βββ __init__.py
β β βββ header.py # π¨βπ» κ°λ°μ A
β β βββ audio_input.py # π¨βπ» κ°λ°μ A
β β βββ history_display.py # π¨βπ» κ°λ°μ B
β β βββ failure_modal.py # π¨βπ» κ°λ°μ B
β β βββ success_screen.py # π¨βπ» κ°λ°μ A
β β
β βββ styles/ # CSS μ€νμΌ
β βββ custom_css.py
β
βββ components/ # λ°±μλ λ‘μ§ (κΈ°μ‘΄)
β βββ audio_validator.py
β βββ ui_renderer.py
β
βββ utils/ # μ νΈλ¦¬ν° (κΈ°μ‘΄)
β βββ stt_handler.py
β βββ state_manager.py
β
βββ config/ # μ€μ (κΈ°μ‘΄)
βββ settings.py
```
---
## π₯ ν λΆλ΄
### π¨βπ» κ°λ°μ A: μ
λ ₯ κ΄λ ¨ μ»΄ν¬λνΈ
- `frontend/components/header.py` - ν€λ λ° λμ΄λ μ€μ
- `frontend/components/audio_input.py` - μ€λμ€ μ
λ ₯ λ° κ²μ¦ λ²νΌ
- `frontend/components/success_screen.py` - μ±κ³΅ νλ©΄
### π¨βπ» κ°λ°μ B: νΌλλ°± κ΄λ ¨ μ»΄ν¬λνΈ
- `frontend/components/history_display.py` - μ€ν¨ κΈ°λ‘ νμ
- `frontend/components/failure_modal.py` - μ€ν¨ νμ
λͺ¨λ¬
### π€ κ³΅ν΅ μμ
- `frontend/styles/custom_css.py` - CSS μ€νμΌ (νμ μ κ°μ μΆκ°)
- `frontend/app_ui.py` - UI 쑰립 (μ΅μ’
ν΅ν© μ μμ )
---
## π νμΌλ³ μμΈ μ€λͺ
### 1. `frontend/app_ui.py` (UI 쑰립 λ©μΈ)
**μν **: λͺ¨λ μ»΄ν¬λνΈλ₯Ό importνκ³ μ°κ²°
**μ£Όμ ν΄λμ€**: `AppUI`
```python
# μ¬μ© μμ
ui_builder = AppUI(validator, ui_renderer)
components = ui_builder.build() # UI λΉλ
ui_builder.setup_events(components, handlers) # μ΄λ²€νΈ μ°κ²°
```
**μ£Όμ λ©μλ**:
- `build()`: μ 체 UI ꡬμ±
- `setup_events()`: μ΄λ²€νΈ λ°μΈλ©
---
### 2. `frontend/components/header.py` (π¨βπ» κ°λ°μ A)
**μν **: μ± νμ΄ν, λμ΄λ μ¬λΌμ΄λ, λͺ©ν λ¬Έμ₯ νμ
**μ£Όμ ν΄λμ€**: `HeaderComponent`
**λ λλ§ μ»΄ν¬λνΈ**:
- νμ΄ν: "ποΈ μμ± κ²μ¦ μμ€ν
"
- λμ΄λ μ¬λΌμ΄λ (1-5)
- νμ¬ λͺ©ν λ¬Έμ₯ ν
μ€νΈλ°μ€
**μ΄λ²€νΈ**:
- λμ΄λ λ³κ²½ μ β λͺ©ν λ¬Έμ₯ μ
λ°μ΄νΈ
**μμ κ°λ₯ μμ**:
- νμ΄ν μ€νμΌ λ³κ²½
- μ¬λΌμ΄λ λ²μ/λ μ΄λΈ μμ
- μ€μ Accordion λ΄λΆμ μΆκ° μ΅μ
μΆκ°
---
### 3. `frontend/components/audio_input.py` (π¨βπ» κ°λ°μ A)
**μν **: μμ± λ
Ήμ/μ
λ‘λ λ° κ²μ¦ λ²νΌ
**μ£Όμ ν΄λμ€**: `AudioInputComponent`
**λ λλ§ μ»΄ν¬λνΈ**:
- λͺ©ν λ¬Έμ₯ νμ (Markdown)
- μ€λμ€ μ
λ ₯ μμ ― (λ
Ήμ + μ
λ‘λ)
- κ²μ¦ λ²νΌ
**μ΄λ²€νΈ**:
- κ²μ¦ λ²νΌ ν΄λ¦ β `validate_audio_handler` νΈμΆ
**μμ κ°λ₯ μμ**:
- λ²νΌ ν
μ€νΈ/μ€νμΌ
- μ€λμ€ μ
λ ₯ μ€μ (μνλ μ΄νΈ λ±)
- λͺ©ν λ¬Έμ₯ νμ μ€νμΌ
---
### 4. `frontend/components/history_display.py` (π¨βπ» κ°λ°μ B)
**μν **: μ€ν¨ κΈ°λ‘μ HTMLλ‘ νμ
**μ£Όμ ν΄λμ€**: `HistoryDisplayComponent`
**λ λλ§ μ»΄ν¬λνΈ**:
- μ€ν¨ κΈ°λ‘ HTML μμ
**λ°μ΄ν° νλ¦**:
- `ui_renderer.render_failure_history()` β HTML μμ±
- κ²μ¦ μ€ν¨ μ μλ μ
λ°μ΄νΈ
**μμ κ°λ₯ μμ**:
- κΈ°λ‘ νμ λ μ΄μμ
- μΉμ
νμ΄ν λ³κ²½
- μΆκ° ν΅κ³ μ 보 νμ
---
### 5. `frontend/components/failure_modal.py` (π¨βπ» κ°λ°μ B)
**μν **: μ€ν¨ μ νμ
λͺ¨λ¬λ‘ κ²°κ³Ό νμ
**μ£Όμ ν΄λμ€**: `FailureModalComponent`
**λ λλ§ μ»΄ν¬λνΈ**:
- Modal 컨ν
μ΄λ
- HTML μ½ν
μΈ μμ
- λ«κΈ° λ²νΌ
**μ μ λ©μλ**:
- `create_modal_content(recognized_text, score, hint)`: λͺ¨λ¬ HTML μμ±
**μ΄λ²€νΈ**:
- λ«κΈ° λ²νΌ ν΄λ¦ β λͺ¨λ¬ μ¨κΉ
**μμ κ°λ₯ μμ**:
- λͺ¨λ¬ HTML ν
νλ¦Ώ (μμ, λ μ΄μμ)
- λ²νΌ μ€νμΌ
- μ λλ©μ΄μ
ν¨κ³Ό μΆκ°
**λͺ¨λ¬ HTML ꡬ쑰**:
```html
β μ€ν¨!
μΈμλ ν
μ€νΈ: ...
μ μ: ...μ
π‘ ννΈ: ...
```
---
### 6. `frontend/components/success_screen.py` (π¨βπ» κ°λ°μ B)
**μν **: μ±κ³΅ μ μ 체 νλ©΄ μ ν
**μ£Όμ ν΄λμ€**: `SuccessScreenComponent`
**λ λλ§ μ»΄ν¬λνΈ**:
- μ±κ³΅ νλ©΄ 컨ν
μ΄λ (`elem_id="success-screen"`)
- μΆν λ©μμ§ (Markdown)
- μ²μλΆν° λ€μ λ²νΌ
**μ΄λ²€νΈ**:
- μ²μλΆν° λ€μ λ²νΌ β `window.location.reload()` (JavaScript)
**μμ κ°λ₯ μμ**:
- μΆν λ©μμ§ ν
μ€νΈ/μ€νμΌ
- λ²νΌ λμμΈ
- μ λλ©μ΄μ
ν¨κ³Ό μΆκ°
**CSS μ°κ²°**:
`frontend/styles/custom_css.py`μ `SUCCESS_SCREEN_CSS` μ°Έμ‘°
---
### 7. `frontend/styles/custom_css.py` (곡ν΅)
**μν **: μ 체 μ±μ CSS μ€νμΌ μ μ
**νμ¬ CSS**:
- `#success-screen`: μ±κ³΅ νλ©΄ μ 체 μ€λ²λ μ΄ μ€νμΌ
- κ³ μ μμΉ (position: fixed)
- 보λΌμ κ·ΈλΌλ°μ΄μ
λ°°κ²½
- μ€μ μ λ ¬
**μμ κ°λ₯**:
- μλ‘μ΄ CSS μΆκ°
- μμ ν
λ§ λ³κ²½
- λ°μν λμμΈ μΆκ°
**μ¬μ© λ°©λ²**:
```python
from frontend.styles.custom_css import get_all_css
gr.Blocks(css=get_all_css())
```
---
## π λ°μ΄ν° νλ¦
### κ²μ¦ νλ‘μΈμ€
1. **μ¬μ©μ μ‘μ
**: μ€λμ€ μ
λ‘λ β κ²μ¦ λ²νΌ ν΄λ¦
2. **νΈλ€λ¬ νΈμΆ**: `validate_audio_handler(audio, difficulty, history, storage)`
3. **λλ―Έ λ‘μ§ μ€ν**:
- 5λ²κΉμ§ μ€ν¨ β λͺ¨λ¬ νμ + κΈ°λ‘ μΆκ°
- 6λ²μ§Έ μ±κ³΅ β μ±κ³΅ νλ©΄ μ ν
4. **UI μ
λ°μ΄νΈ**:
- μ€ν¨: `failure_modal` νμ, `history_html` μ
λ°μ΄νΈ
- μ±κ³΅: `main_screen` μ¨κΉ, `success_screen` νμ
### μν κ΄λ¦¬
- `gr.State`: μΈμ
μν (μλ‘κ³ μΉ¨ μ μ΄κΈ°ν)
- `difficulty`: νμ¬ λμ΄λ
- `failure_history`: μ€ν¨ κΈ°λ‘ λ¦¬μ€νΈ
- `gr.BrowserState`: localStorage μꡬ μ μ₯
- `storage_key="audio_validation_history"`
- ꡬ쑰: `{"date": "YYYY-MM-DD", "failures": [...], "successes": [...]}`
- λ μ§ λ³κ²½ μ μλ μ΄κΈ°ν
---
## π οΈ κ°λ° κ°μ΄λ
### μ»΄ν¬λνΈ μμ μ
1. ν΄λΉ μ»΄ν¬λνΈ νμΌλ§ μμ
2. ν΄λμ€μ `render()` λ©μλμμ UI ꡬμ±
3. νμ μ `setup_events()` λ©μλμμ μ΄λ²€νΈ μ°κ²°
### μ μ»΄ν¬λνΈ μΆκ° μ
1. `frontend/components/` μ μ νμΌ μμ±
2. ν΄λμ€ μμ± (`render()`, `setup_events()` λ©μλ ꡬν)
3. `frontend/app_ui.py`μμ import λ° μ°κ²°
### CSS μ€νμΌ μΆκ° μ
1. `frontend/styles/custom_css.py`μ CSS λ¬Έμμ΄ μΆκ°
2. `get_all_css()` ν¨μμμ λ°νκ°μ ν¬ν¨
---
## π§ͺ ν
μ€νΈ λ°©λ²
### κ°λ³ μ»΄ν¬λνΈ ν
μ€νΈ
κ° μ»΄ν¬λνΈλ λ
립μ μΌλ‘ ν
μ€νΈ κ°λ₯:
```python
# header.py ν
μ€νΈ
from frontend.components.header import HeaderComponent
from components.audio_validator import AudioValidator
validator = AudioValidator(config)
header = HeaderComponent(validator)
# Gradio μ±μμ header.render() νΈμΆ
```
### ν΅ν© ν
μ€νΈ
```bash
python app_new.py
```
---
## π μ»€λ° μ»¨λ²€μ
(κΆμ₯)
```
[κ°λ°μA] header: λμ΄λ μ¬λΌμ΄λ λ²μ μμ
[κ°λ°μB] modal: μ€ν¨ λͺ¨λ¬ μ€νμΌ κ°μ
[곡ν΅] css: λ°μν λμμΈ μΆκ°
```
---
## π§ λ°±μλ μ°κ²° (μΆν μμ
)
νμ¬ λλ―Έ λ°μ΄ν°λ‘ λμνλ©°, μ€μ λ°±μλ μ°κ²° μ:
1. `app_new.py`μ `validate_audio_handler()` μμ
2. `components/audio_validator.py` λ‘μ§ νμ±ν
3. `utils/stt_handler.py` μ€μ STT API μ°κ²°
**λ³κ²½μ΄ νμν λΆλΆ**:
```python
# app_new.py:72-90 (λλ―Έ λ°μ΄ν° λΆλΆ)
# μ€μ κ²μ¦ λ‘μ§μΌλ‘ κ΅μ²΄
result = self.validator.validate(audio_path, expected_text)
recognized_text = result['text']
score = result['score']
```
---
## π μ°Έκ³ λ¬Έμ
- **Gradio 곡μ λ¬Έμ**: `docs/GRADIO_BASICS.md`
- **Gradio Modal μ»΄ν¬λνΈ**: https://huggingface.co/spaces/aliabid94/gradio_modal
- **κΈ°μ‘΄ μ± κ΅¬μ‘°**: `app.py` (λ°±μ
)
---
## π μμνκΈ°
### 1. νκ²½ μ€μ
```bash
# κ°μνκ²½ νμ±ν
source .venv/bin/activate # Linux/Mac
.venv\Scripts\activate # Windows
# μμ‘΄μ± μ€μΉ (gradio_modal μΆκ°)
pip install gradio_modal
```
### 2. μ± μ€ν
```bash
# μλ‘μ΄ μ»΄ν¬λνΈ λΆλ¦¬ λ²μ
python app_new.py
# κΈ°μ‘΄ λ²μ (λ°±μ
)
python app.py
```
### 3. λΈλΌμ°μ μ μ
```
http://localhost:7860
```
---
## β FAQ
### Q1: κ°λ°μ Aμ Bκ° λμμ μμ
νλ©΄ μΆ©λμ΄ λμ§ μλμ?
**A**: κ°μ λ€λ₯Έ νμΌμ μμ νλ―λ‘ Git μΆ©λμ΄ κ±°μ μμ΅λλ€. `app_ui.py`λ§ μ΅μ’
ν΅ν© μ μ‘°μ¬ν λ³ν©νλ©΄ λ©λλ€.
### Q2: μ»΄ν¬λνΈ κ° ν΅μ μ μ΄λ»κ² νλμ?
**A**: `app_ui.py`μ `setup_events()`μμ μ΄λ²€νΈλ₯Ό μ°κ²°ν©λλ€. κ° μ»΄ν¬λνΈλ λ
립μ μ΄λ©°, μνλ `gr.State`λ₯Ό ν΅ν΄ 곡μ λ©λλ€.
### Q3: CSSλ₯Ό μ»΄ν¬λνΈλ³λ‘ λΆλ¦¬ν μ μλμ?
**A**: λ€, `frontend/styles/` μ μ¬λ¬ νμΌμ λ§λ€κ³ `get_all_css()`μμ ν©μΉ μ μμ΅λλ€.
### Q4: κΈ°μ‘΄ app.pyλ μμ ν΄μΌ νλμ?
**A**: μλμ, λ°±μ
μ©μΌλ‘ λ¨κ²¨λμΈμ. `app_new.py`κ° μμ νλλ©΄ κ΅μ²΄νλ©΄ λ©λλ€.
### Q5: "Cannot call change outside of a gradio.Blocks context" μλ¬κ° λ°μν©λλ€!
**A**: Gradio μ΄λ²€νΈ(`.click()`, `.change()` λ±)λ λ°λμ `gr.Blocks()` 컨ν
μ€νΈ λ΄λΆμμ νΈμΆν΄μΌ ν©λλ€.
**λ¬Έμ μν©**:
```python
# β μλͺ»λ λ°©λ²
with gr.Blocks() as demo:
components = build() # UIλ§ λΉλ
return components
# gr.Blocks 컨ν
μ€νΈ λ°μμ μ΄λ²€νΈ λ°μΈλ© (μλ¬ λ°μ!)
setup_events(components)
```
**ν΄κ²° λ°©λ²**:
```python
# β
μ¬λ°λ₯Έ λ°©λ²
with gr.Blocks() as demo:
components = build() # UI λΉλ
setup_events(components) # μ΄λ²€νΈ λ°μΈλ©λ μ¬κΈ°μ!
return components
```
**νμ¬ κ΅¬μ‘°μμμ ꡬν**:
- `frontend/app_ui.py`μ `build(handlers)` λ©μλ μμμ UI λ λλ§κ³Ό μ΄λ²€νΈ λ°μΈλ©μ λͺ¨λ μν
- κ° μ»΄ν¬λνΈμ `setup_events()` λ©μλλ `gr.Blocks()` 컨ν
μ€νΈ λ΄λΆμμ νΈμΆλ¨
---
## π μ°λ½μ²
λ¬Έμ κ° λ°μνλ©΄:
1. ν΄λΉ μ»΄ν¬λνΈ νμΌμ docstring νμΈ
2. `GRADIO_BASICS.md` μ°Έκ³
3. ν μ±λμ μ§λ¬Έ
---
## π λ³κ²½ μ΄λ ₯
### v1.1 (2024-11-25)
- FAQ Q5 μΆκ°: Gradio μ΄λ²€νΈ λ°μΈλ© 컨ν
μ€νΈ μ΄μ ν΄κ²° λ°©λ²
- `app_ui.py` ꡬ쑰 κ°μ : `build()` λ©μλμμ μ΄λ²€νΈ λ°μΈλ© ν΅ν©
### v1.0 (2024-11-24)
- μ΄κΈ° λ¬Έμ μμ±
- νλ‘ νΈμλ μ»΄ν¬λνΈ λΆλ¦¬ ꡬ쑰 μ€κ³
---
**μ΅μ’
μμ μΌ**: 2024-11-25
**λ²μ **: 1.1
**μμ±μ**: Claude Code Assistant