smarteye-backend / README.md
AkJeond's picture
chore(backend): add HF Spaces README config
9c8d959
---
title: SmartEyeSsen Backend
emoji: "๐Ÿ”"
colorFrom: blue
colorTo: green
sdk: docker
app_port: 7860
pinned: false
---
# SmartEyeSsen Backend
> FastAPI ยท MySQL ยท DocLayout-YOLO ๊ธฐ๋ฐ˜ AI ๋ฌธ์„œ ๋ถ„์„ ๋ฐฑ์—”๋“œ
## ๐Ÿ“š ๋ชฉ์ฐจ
- [ํ”„๋กœ์ ํŠธ ๊ฐœ์š”](#-ํ”„๋กœ์ ํŠธ-๊ฐœ์š”)
- [๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ](#-๋””๋ ‰ํ„ฐ๋ฆฌ-๊ตฌ์กฐ)
- [์‹คํ–‰ ๋ชจ๋“œ](#-์‹คํ–‰-๋ชจ๋“œ)
- [ํ™˜๊ฒฝ ๋ณ€์ˆ˜](#-ํ™˜๊ฒฝ-๋ณ€์ˆ˜)
- [๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค & Docker ๊ตฌ์„ฑ](#-๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค--docker-๊ตฌ์„ฑ)
- [FastAPI ๋ชจ๋“ˆ ๊ตฌ์„ฑ](#-fastapi-๋ชจ๋“ˆ-๊ตฌ์„ฑ)
- [ํ…Œ์ŠคํŠธ & ์Šคํฌ๋ฆฝํŠธ](#-ํ…Œ์ŠคํŠธ--์Šคํฌ๋ฆฝํŠธ)
- [์ž์ฃผ ๋ฌป๋Š” ๋ฌธ์ œ](#-์ž์ฃผ-๋ฌป๋Š”-๋ฌธ์ œ)
- [์ฐธ๊ณ  ์ž๋ฃŒ](#-์ฐธ๊ณ -์ž๋ฃŒ)
---
## ๐ŸŽฏ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”
- PDF/์ด๋ฏธ์ง€ ์—…๋กœ๋“œ โ†’ **DocLayout-YOLO + Tesseract**๋กœ ๋ ˆ์ด์•„์›ƒ๊ณผ ํ…์ŠคํŠธ๋ฅผ ์ถ”์ถœํ•˜๊ณ , **OpenAI Vision**์œผ๋กœ ๋„ํ‘œยทํ‘œ ์„ค๋ช…์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
- ๊ฒฐ๊ณผ๋ฌผ์€ **SmartEye ์ •๋ ฌ ๊ทœ์น™**์„ ๊ฑฐ์ณ ํ”„๋กœ์ ํŠธ/ํŽ˜์ด์ง€/์š”์†Œ ๋‹จ์œ„๋กœ ์ €์žฅ๋˜๋ฉฐ, **DOCX** ๋‹ค์šด๋กœ๋“œ๊นŒ์ง€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.
- ์šด์˜ ํ™˜๊ฒฝ์€ DigitalOcean Droplet์—์„œ `docker-compose.prod.yml`์„ ํ†ตํ•ด **MySQL + Backend + Frontend + Certbot** ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค.
---
## ๐Ÿ—‚ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ
```
Backend/
โ”œโ”€โ”€ app/
โ”‚ โ”œโ”€โ”€ main.py # FastAPI ์—”ํŠธ๋ฆฌํฌ์ธํŠธ
โ”‚ โ”œโ”€โ”€ database.py # ์„ธ์…˜/์—”์ง„, MySQL ์—ฐ๊ฒฐ
โ”‚ โ”œโ”€โ”€ models.py # SQLAlchemy ORM
โ”‚ โ”œโ”€โ”€ schemas.py # Pydantic v2 ์Šคํ‚ค๋งˆ
โ”‚ โ”œโ”€โ”€ crud.py # DB ์ ‘๊ทผ ํ—ฌํผ
โ”‚ โ”œโ”€โ”€ routers/ # ํ”„๋กœ์ ํŠธ/ํŽ˜์ด์ง€/๋ถ„์„/๋‹ค์šด๋กœ๋“œ ๋ผ์šฐํ„ฐ
โ”‚ โ””โ”€โ”€ services/ # OCRยท๋ ˆ์ด์•„์›ƒยท์ •๋ ฌยทAI ์„ค๋ช… ๋ชจ๋“ˆ
โ”œโ”€โ”€ scripts/
โ”‚ โ”œโ”€โ”€ init_db_complete.sql # 12๊ฐœ ํ…Œ์ด๋ธ” + ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ
โ”‚ โ””โ”€โ”€ reset_db.sh (์˜ต์…˜) # ๊ฐœ๋ฐœ์šฉ ์ดˆ๊ธฐํ™” ์Šคํฌ๋ฆฝํŠธ
โ”œโ”€โ”€ uploads/, static/ # ์—…๋กœ๋“œ/์ •์  ๊ฒฐ๊ณผ (์ปจํ…Œ์ด๋„ˆ ๋ณผ๋ฅจ ์—ฐ๊ฒฐ)
โ”œโ”€โ”€ Dockerfile # ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€
โ”œโ”€โ”€ docker-compose.yml # ๋ฐฑ์—”๋“œ ๋‹จ๋… MySQL ์ปจํ…Œ์ด๋„ˆ
โ”œโ”€โ”€ requirements.txt # Python ์˜์กด์„ฑ
โ””โ”€โ”€ README.md # ๋ณธ ๋ฌธ์„œ
```
---
## โš™ ์‹คํ–‰ ๋ชจ๋“œ
### 1. ๋กœ์ปฌ ๊ฐœ๋ฐœ (venv + Uvicorn)
```bash
cd Backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
```
ํ•„์š” ์‹œ `OPENAI_API_KEY`๋ฅผ `.env`์— ์„ค์ •ํ•˜๋ฉด AI ์„ค๋ช… ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค.
### 2. ๋ฐฑ์—”๋“œ ์ „์šฉ Docker Compose (MySQL ํฌํ•จ)
`Backend/docker-compose.yml`์€ MySQL 8.0 ์ปจํ…Œ์ด๋„ˆ๋งŒ ๋„์›Œ FastAPI๋ฅผ ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
```bash
cd Backend
docker compose up -d # smart_mysql ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘ (๊ธฐ๋ณธ ํฌํŠธ 3308โ†’3306)
uvicorn app.main:app --reload
```
์ข…๋ฃŒ ๋˜๋Š” ์ดˆ๊ธฐํ™”:
```bash
docker compose down # ์ปจํ…Œ์ด๋„ˆ๋งŒ ์ข…๋ฃŒ
docker compose down -v # smart_mysql_data ๋ณผ๋ฅจ๊นŒ์ง€ ์‚ญ์ œ (โš  ์ „์ฒด ๋ฐ์ดํ„ฐ ์‚ญ์ œ)
```
### 3. ํ”„๋กœ๋•์…˜ Docker Compose (์ „์ฒด ์Šคํƒ)
๋ฃจํŠธ `docker-compose.prod.yml`์˜ `backend` ์„œ๋น„์Šค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.
```yaml
backend:
build:
context: ./Backend
dockerfile: Dockerfile
env_file:
- Backend/.env
environment:
DB_HOST: mysql
DB_PORT: 3306
ENVIRONMENT: production
volumes:
- ./Backend/uploads:/app/uploads
- ./Backend/static:/app/static
depends_on:
mysql:
condition: service_healthy
```
๋ฐฐํฌ ์‹œ ์„œ๋ฒ„์—์„œ:
```bash
git checkout main && git pull --ff-only origin main
docker compose -f docker-compose.prod.yml build backend
docker compose -f docker-compose.prod.yml up -d --force-recreate backend
```
---
## ๐Ÿ” ํ™˜๊ฒฝ ๋ณ€์ˆ˜
`.env.example`์„ ๊ธฐ๋ฐ˜์œผ๋กœ `.env`๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
| ๋ณ€์ˆ˜ | ์„ค๋ช… | ๋น„๊ณ  |
|------|------|------|
| `DB_HOST`, `DB_PORT` | MySQL ์ ‘์† ์ •๋ณด | Docker ์‚ฌ์šฉ ์‹œ `mysql`/`3306`์œผ๋กœ ์ž๋™ override |
| `DB_USER`, `DB_PASSWORD`, `DB_NAME` | DB ๊ณ„์ • | ์ดˆ๊ธฐ ์Šคํฌ๋ฆฝํŠธ ๊ธฐ๋ณธ๊ฐ’: root / change_this_password / smarteyessen_db |
| `DATABASE_URL` | SQLAlchemy ์ ‘์† URL | ๋ณ€๊ฒฝ ๋ถˆํ•„์š” (ํ…œํ”Œ๋ฆฟ ์ž๋™ ์กฐํ•ฉ) |
| `API_HOST`, `API_PORT` | FastAPI ์„œ๋ฒ„ ํ˜ธ์ŠคํŠธ/ํฌํŠธ | ๊ธฐ๋ณธ `0.0.0.0:8000` |
| `ENVIRONMENT` | `development` / `production` | Compose์—์„œ `production`์œผ๋กœ ๊ฐ•์ œ |
| `OPENAI_API_KEY` | ์„ ํƒ ํ•ญ๋ชฉ | ์—†์œผ๋ฉด AI ์„ค๋ช… ๋น„ํ™œ์„ฑํ™” |
| `UPLOAD_DIR`, `MAX_FILE_SIZE`, `ALLOWED_EXTENSIONS` | ์—…๋กœ๋“œ ์„ค์ • | ๊ธฐ๋ณธ 100 MB, jpg/jpeg/png/pdf |
| `SECRET_KEY`, `ALGORITHM` | JWT/๋ณด์•ˆ ์˜ต์…˜ | ํ•„์š” ์‹œ ์—…๋ฐ์ดํŠธ |
| `USE_ADAPTIVE_SORTER`, `PDF_PROCESSOR_DPI` ๋“ฑ | ํŒŒ์ดํ”„๋ผ์ธ ๋™์ž‘ ์ œ์–ด | `.env.example` ์ฐธ๊ณ  |
---
## ๐Ÿณ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค & Docker ๊ตฌ์„ฑ
### Backend/docker-compose.yml (๋กœ์ปฌ MySQL)
| ํ•ญ๋ชฉ | ๊ฐ’ |
|------|----|
| ์ด๋ฏธ์ง€ | `mysql:8.0` |
| ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ | `smart_mysql` |
| ํฌํŠธ | ํ˜ธ์ŠคํŠธ `${MYSQL_PORT:-3308} โ†’ 3306` |
| ๋ณผ๋ฅจ | `smart_mysql_data:/var/lib/mysql` (Named Volume) |
| ์ดˆ๊ธฐํ™” | `./scripts/init_db_complete.sql` โ†’ `/docker-entrypoint-initdb.d/01_init.sql` |
| ๋ฌธ์ž์…‹ | `utf8mb4 / utf8mb4_unicode_ci` |
| ํ—ฌ์Šค์ฒดํฌ | `mysqladmin ping` (10์ดˆ ๊ฐ„๊ฒฉ, 5ํšŒ ์žฌ์‹œ๋„) |
### Backend/Dockerfile (ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€)
1. **Builder ๋‹จ๊ณ„ (python:3.9-slim)**
- Tesseract(ko/en), OpenCV ์˜์กด ํŒจํ‚ค์ง€ ์„ค์น˜
- `pip install -r requirements.txt` + `doclayout-yolo`
2. **Runtime ๋‹จ๊ณ„ (python:3.9-slim)**
- ๋Ÿฐํƒ€์ž„ ํŒจํ‚ค์ง€ ์„ค์น˜ ํ›„ Builder์—์„œ site-packages ๋ณต์‚ฌ
- `ko_KR.UTF-8` ๋กœ์ผ€์ผ ์ƒ์„ฑ
- `/app/uploads`, `/app/static`, `/app/test_pipeline_outputs` ์ƒ์„ฑ ๋ฐ ๊ถŒํ•œ ๋ถ€์—ฌ
- Healthcheck: `requests.get('http://localhost:8000/health')`
- CMD: Gunicorn + UvicornWorker (1 worker, timeout 300์ดˆ)
### DB ์ดˆ๊ธฐ ์Šคํ‚ค๋งˆ
- `scripts/init_db_complete.sql`์ด 12๊ฐœ ํ…Œ์ด๋ธ”(users, projects, pages, โ€ฆ combined_results)๊ณผ ์‹œ๋“œ ๋ฐ์ดํ„ฐ(document_types 2๊ฑด, formatting_rules 25๊ฑด)๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
- `combined_results.combined_text` ํƒ€์ž…์€ `LONGTEXT`๋กœ 4GB๊นŒ์ง€ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
---
## ๐Ÿง  FastAPI ๋ชจ๋“ˆ ๊ตฌ์„ฑ
| ์˜์—ญ | ์„ค๋ช… |
|------|------|
| `routers/projects.py` | ํ”„๋กœ์ ํŠธ CRUD, ๋ถ„์„ ํŠธ๋ฆฌ๊ฑฐ |
| `routers/pages.py` | ํŽ˜์ด์ง€ ์—…๋กœ๋“œ, ํ…์ŠคํŠธ ๋ฒ„์ „ API |
| `routers/analyze.py` | DocLayout-YOLO ์‹คํ–‰, Tesseract OCR, AI ์„ค๋ช… |
| `routers/download.py` | ํ†ตํ•ฉ ํ…์ŠคํŠธ/WORD ์ƒ์„ฑ |
| `services/layout_service.py` | ๋ชจ๋ธ ๋กœ๋”ฉ, ๋ ˆ์ด์•„์›ƒ ํ›„์ฒ˜๋ฆฌ |
| `services/ocr_service.py` | PDF ๋ถ„๋ฆฌ, ์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ, Tesseract ํ˜ธ์ถœ |
| `services/sorter_service.py` | ๋ฌธ์ œ์ง€/์ผ๋ฐ˜ ๋ฌธ์„œ๋ณ„ ์ •๋ ฌ ๋กœ์ง |
| `services/ai_description_service.py` | OpenAI Vision ํ˜ธ์ถœ ๋ฐ ์บ์‹ฑ |
๋ชจ๋“  ๋ผ์šฐํ„ฐ๋Š” `app/main.py`์—์„œ FastAPI ์ธ์Šคํ„ด์Šค์— ๋“ฑ๋ก๋˜๋ฉฐ, `database.SessionLocal` ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
---
## ๐Ÿงช ํ…Œ์ŠคํŠธ & ์Šคํฌ๋ฆฝํŠธ
- **Pytest**: ๋ฃจํŠธ์—์„œ `pytest -c Project/pytest.ini` ์‹คํ–‰ (ํšŒ๊ท€/ํ†ตํ•ฉ ์‹œ `-m regression` ์‚ฌ์šฉ)
- **start_backend.sh**: ์˜์กด์„ฑ ์ฒดํฌ ํ›„ Uvicorn ์‹คํ–‰ (๋ฃจํŠธ ์Šคํฌ๋ฆฝํŠธ)
- **scripts/reset_db.sh**: ๊ฐœ๋ฐœ DB ์ดˆ๊ธฐํ™” (๋ฐ์ดํ„ฐ ์ „์ฒด ์‚ญ์ œ)
- **api_server.py**: ๋ ˆ๊ฑฐ์‹œ ๋‹จ์ผ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰(ํ•„์š” ์‹œ๋งŒ ์‚ฌ์šฉ)
---
## ๐Ÿšจ ์ž์ฃผ ๋ฌป๋Š” ๋ฌธ์ œ
| ์ฆ์ƒ | ํ•ด๊ฒฐ ๋ฐฉ๋ฒ• |
|------|-----------|
| MySQL ์ปจํ…Œ์ด๋„ˆ ํ—ฌ์Šค์ฒดํฌ ์‹คํŒจ | `docker compose logs mysql` ํ™•์ธ, ํฌํŠธ ์ถฉ๋Œ ์‹œ `MYSQL_PORT` ๋ณ€๊ฒฝ, `docker compose down -v`๋กœ ์žฌ์ƒ์„ฑ |
| `DataError: ... combined_text` | `scripts/init_db_complete.sql` ์ตœ์‹  ๋ฒ„์ „ ์ ์šฉ ํ›„ `reset_db.sh` ์‹คํ–‰ |
| Tesseract ์–ธ์–ด ๋ฏธํƒ‘์žฌ | Dockerfile ์ด๋ฏธ์ง€๋Š” `tesseract-ocr-kor/eng`๋ฅผ ํฌํ•จํ•จ. ๋กœ์ปฌ ์ˆ˜๋™ ์„ค์น˜ ์‹œ `sudo apt install tesseract-ocr-kor` |
| OpenAI ์˜ค๋ฅ˜ | `.env`์˜ `OPENAI_API_KEY` ํ™•์ธ, ์š”์ฒญ ์ˆ˜ ์ œํ•œ ์‹œ `OPENAI_MAX_CONCURRENCY` ๊ฐ’ ์กฐ์ • |
| ์—…๋กœ๋“œ ํŒŒ์ผ ๋ฏธ์ €์žฅ | ์ปจํ…Œ์ด๋„ˆ ๋ณผ๋ฅจ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋งˆ์šดํŠธ๋˜์—ˆ๋Š”์ง€ (`./Backend/uploads:/app/uploads`) ํ™•์ธ |
---
## ๐Ÿ“Ž ์ฐธ๊ณ  ์ž๋ฃŒ
- `../README.md` โ€“ ์ „์ฒด ์‹œ์Šคํ…œ ๊ฐœ์š” ๋ฐ ๋ฐฐํฌ ์ „๋žต
- `Backend/docs/Backend API ๋ฌธ์„œ/` โ€“ ์„ธ๋ถ€ API ์ŠคํŽ™