# π¬ h-lab Β· `yanalyst`
**μ νλΈ λ°μ΄ν°λ₯Ό μμ§Β·λΆμνκ³ , λ‘μ μμμ λ°κ΅΄ν΄ μ¬κ°κ³΅Β·λ°νκΉμ§ μλ 1μΈ μ½ν
μΈ νμ΄νλΌμΈ**










---
## π κ°μ
**h-lab**(μν°ν©νΈλͺ
`yanalyst`)μ μ νλΈ Shorts λ°μ΄ν°λ₯Ό μμ§Β·λΆμν΄ **λ‘μ(ꡬλ
μ λλΉ μ‘°νμ νλ°) ν보**λ₯Ό 골λΌλ΄κ³ , μλ§μ μΆμΆΒ·μ¬κ°κ³΅ν λ€ λ°νκΉμ§ μΆμ νλ **κ°μΈμ© SSR μΉ μλΉμ€**μ
λλ€. Spring Boot λ°±μλμ **Thymeleaf μλ² μ¬μ΄λ λ λλ§** UIλ₯Ό μ¬μ©νλ©°, λͺ¨λ νλ©΄μ΄ λμΌν μ±μμ μ 곡λ©λλ€.
> λΌμ΄νΈ/λ€ν¬ ν
λ§ ν κΈμ μ§μνλ μ μ λ SaaS UIλ‘, μ¬μ΄λλ° ν κ³³μμ μ 체 ν
λ§κ° μ νλ©λλ€.
## β¨ μ£Όμ κΈ°λ₯
| λ¨κ³ | μ€λͺ
|
|------|------|
| π°οΈ **μμ§ (Collection)** | λ±λ‘ μ±λ λκΈ°ν + μ‘°νμ κ²μ μμ§. μ κΈ° μλ μμ§ μ€μΌμ€λ¬. |
| π **λ°κ΅΄ (Discovery)** | `μ‘°νμ Γ· ꡬλ
μ` **λ°°μ¨**λ‘ λ‘μ ν보 μλ μ λ³. μΉ΄ν
κ³ λ¦¬Β·λΆλ§ν¬Β·νν°. |
| ποΈ **νλ μ΄μ
(Curation)** | κ΄μ¬ μν(NEWβREVIEWINGβTARGETβDONE/EXCLUDED) μΉΈλ° λ³΄λ + λλκ·Έ μ΄λ. |
| βοΈ **μ¬κ°κ³΅ (Rework)** | **μμ μ
λ‘λ β Whisper μκ°μ±ν¬ μλ§ μΆμΆ β λ§ μλ κ΅¬κ° μ κ±°Β·λ°°μ β SRT+μμ λ΄λ³΄λ΄κΈ°**(CapCut import). μ¬μμ± μλν°. |
| π€ **λ°ν (Publish)** | μ λͺ©Β·μ€λͺ
Β·ν΄μνκ·ΈΒ·μμ½ ν¨ν€μ§ κ΄λ¦¬, λ°ν μΆμ (μ
λ‘λλ μλ). |
## π μ½ν
μΈ νμ΄νλΌμΈ
```
μμ§ λ°κ΅΄/νλ μ΄μ
μ¬κ°κ³΅ λ°ν
ββββββββββ ββββββββββββββ ββββββββββββββββ ββββββββββββ
β CHANNELβ βββΆ β λ°°μ¨ μ λ ¬ β βββΆ β μλ§μΆμΆΒ·μ»· β βββΆ β μ λͺ©/νκ·Έ β
β SEARCH β β μΉΈλ° λ³΄λ β β SRTΒ·μμ μΆλ ₯ β β λ°ν μΆμ β
ββββββββββ ββββββββββββββ ββββββββββββββββ ββββββββββββ
\______________ ChannelVideo (λ¨μΌ λ§μ€ν°) ______________/
```
λͺ¨λ λ¨κ³λ `domain/channel/ChannelVideo` μν°ν° νλλ₯Ό μ€μ¬μΌλ‘ λλλ€.
## π κΈ°μ μ€ν
**Backend** Β· Spring Boot 3.4.0 / Java 21 Β· Spring Data JPA Β· Validation
**View** Β· Thymeleaf + Layout Dialect (SSR) Β· Inter Β· Lucide Icons Β· Light/Dark CSS λ³μ ν
λ§
**DB** Β· PostgreSQL (`ddl-auto: update`) Β· p6spy Β· Hypersistence Utils
**Docs** Β· SpringDoc OpenAPI (Swagger UI)
**Build/Tools** Β· Gradle Wrapper Β· Lombok Β· JUnit 5
**AI/λ―Έλμ΄ μμ§**(λ³λ Python λ§μ΄ν¬λ‘μλΉμ€) Β· `faster-whisper`(μ μ¬) Β· `ffmpeg`(μ»·Β·λ°°μΒ·λ λ)
## π μμνκΈ°
> **μꡬμ¬ν**: JDK 21, PostgreSQL μ κ·Ό μ 보. μλ² ν¬νΈλ **8088**.
```bash
# μ€ν (UI + API λͺ¨λ http://localhost:8088 μμ μ 곡)
./gradlew bootRun # Windows: gradlew.bat bootRun
# λΉλ / ν
μ€νΈ
./gradlew build
./gradlew test
# λ¨μΌ ν
μ€νΈ
./gradlew test --tests "com.hlab.yanalyst.domain.channel.SrtFormatterTest"
```
| κ²½λ‘ | μ€λͺ
|
|------|------|
| `http://localhost:8088/` | λμ보λ |
| `http://localhost:8088/collection` Β· `/board` Β· `/discover` | μμ§ν¨ Β· μΉΈλ° Β· λ°κ΅΄ |
| `http://localhost:8088/rework/{id}` Β· `/publish` | μ¬κ°κ³΅ Β· λ°ν ν |
| `http://localhost:8088/swagger-ui.html` | Swagger UI |
### π νκ²½ λ³μ
μν¬λ¦Ώμ νκ²½ λ³μ λλ git-ignored `application-local.yml`λ‘ μ£Όμ
ν©λλ€ (μμ: `application-local.yml.example`).
| λ³μ | μ©λ |
|------|------|
| `DB_URL` Β· `DB_USERNAME` Β· `DB_PASSWORD` | PostgreSQL μ μ |
| `YOUTUBE_API_KEY` | YouTube Data API |
| `PYTHON_BASE_URL` | μλ§/μ μ¬/λ λ λ§μ΄ν¬λ‘μλΉμ€ (κΈ°λ³Έ `http://h-python.tolag.shop`) |
| `UPLOAD_MAX_FILE_SIZE` | μ
λ‘λ μμ μ΅λ ν¬κΈ° (κΈ°λ³Έ 200MB) |
## π νλ‘μ νΈ κ΅¬μ‘°
```
h-lab/
βββ src/main/java/com/hlab/yanalyst/
β βββ domain/ # DDD μ€νμΌ β Entity+Repository+Service+RestController
β β βββ channel/ # ChannelVideo(λ¨μΌ λ§μ€ν°)Β·μμ§Β·νλ μ΄μ
Β·μ¬κ°κ³΅Β·SRT/μ»· λ‘μ§
β β βββ category/ publish/ # λΆλ₯ Β· λ°ν ν¨ν€μ§
β β βββ production/ # n8n ν¬λ‘€ λνΉ μ€λ
μ·
β βββ web/ # Thymeleaf νμ΄μ§ 컨νΈλ‘€λ¬ + μΆκ° JSON API
β βββ service/ # ν‘λ¨ μλΉμ€ (YouTube κ²μ λ±)
β βββ global/ # config Β· κ³΅ν΅ μλ΅ Β· μμΈ Β· μ€μΌμ€λ¬
βββ src/main/resources/
βββ static/css/ # variables.css(ν
λ§ ν ν°) Β· style.css(μ»΄ν¬λνΈ)
βββ static/js/ # common.js(μ¬μ΄λλ°Β·ν
λ§ ν κΈ)
βββ templates/ # layout/baseΒ·sidebar + νμ΄μ§ HTML
```
## π μΈλΆ μ°λ
- **Python λ§μ΄ν¬λ‘μλΉμ€** β YouTube μλ§(`/transcript`), Whisper μ μ¬(`/transcribe`), ffmpeg λ λ(`/render`).
- **YouTube Data API** β μ±λΒ·μμ λ©ν μμ§ λ° μ‘°νμ κ²μ (μΌμΌ μΏΌν° κ°λ).
- **n8n Webhook** β `production` λλ©μΈμ λνΉ/ν¬λ‘€ λ°μ΄ν° μ°λ.
## π λΌμ΄μ μ€
κ°μΈμ© νλ‘μ νΈμ
λλ€.
Built with Spring Boot Β· Thymeleaf Β· faster-whisper Β· ffmpeg