0. Product Built
Mimi product ์ ํ๋ธ ๊ธฐ๋ฐ ์์ด ์๋์/๋ณต์ต/๋ฌธ์ฅํ๋ จ ์ฑ์ด๋ค.
Mimi turns YouTube input into repeatable English output practice: clips, AI explanation, recording, and spaced repetition.
0. Product Built
Friction removal ํต์ฌ ์ฒ ํ์ ๊ธฐ๋ฅ ์๊ฐ ์๋๋ผ ๋ง์ฐฐ ์ ๊ฑฐ๋ค.
The product thesis is friction removal: keep the learner inside one loop from input to output to review.
0. Product Built
Full system ์น, ๋ชจ๋ฐ์ผ, ๋ฐฑ์๋, DB, AI, infra๊ฐ ์๋ ์ ์ฒด ์์คํ
์ด๋ค.
This is a full system: web, mobile, Spring backend, Postgres, AI providers, storage, deployment, and tests.
1. Language choices Built
Java 21 ๋ฐฑ์๋ ์ฃผ ์ธ์ด๋ค.
I used Java 21 for the backend because the system needs strong server-side transactions, security, persistence, and mature testing support.
2. Backend Built
Spring Boot ๋ฐฑ์๋ ํ๋ ์์ํฌ๋ค.
Spring Boot gives me the backend platform: REST controllers, security, transactions, JPA, async events, and observability.
1. Language choices Built
TypeScript ์น/๋ชจ๋ฐ์ผ/shared core์ ์ฃผ ์ธ์ด๋ค.
I use TypeScript on the client side because the web app, mobile app, and shared drill content need typed contracts.
1. Language choices Built
Gradle Kotlin DSL ๋ฐฑ์๋ build ์ค์ ์ธ์ด๋ค.
Kotlin DSL is only for the Gradle build file; the application code is Java.
1. Language choices Built
SQL + Flyway DB schema ๋ณ๊ฒฝ์ versioned SQL๋ก ๊ด๋ฆฌํ๋ค.
Flyway migrations make database schema changes explicit, versioned, and reviewable.
2. Backend Built
Modular monolith ํ๋์ Spring Boot ์ฑ ์์ ๋๋ฉ์ธ์ ๋๋ ๋ ๊ตฌ์กฐ๋ค.
Mimi is a modular monolith: one deployable backend, split internally by feature domains.
2. Backend Built
Feature-sliced packages layer๋ณ์ด ์๋๋ผ ๊ธฐ๋ฅ๋ณ ํจํค์ง ๊ตฌ์กฐ๋ค.
The backend is organized by feature, not by technical layer, so each domain owns its API, service, entity, and repository.
2. Backend Built
REST API ํด๋ผ์ด์ธํธ์ ๋ฐฑ์๋ ์ฌ์ด ๊ณ์ฝ์ด๋ค.
The clients talk to the Spring backend through REST endpoints, with JWT in the Authorization header.
2. Backend Built
ApiResponse wrapper ์ฑ๊ณต/์คํจ ์๋ต ํํ๋ฅผ ํต์ผํ๋ค.
The API wraps responses so clients can handle success and failure consistently.
6. AI path Built
Async AI pipeline ํด๋ฆฝ ์์ฑ ํ AI ๋ถ์์ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ๋๋ฆฐ๋ค.
When a clip is saved, the AI analysis runs after commit on a background thread, outside any DB transaction.
2. Backend Built
Connection pool protection ๋๋ฆฐ AI/YouTube ํธ์ถ ์ค DB ์ปค๋ฅ์
์ ์ก์ง ์๊ฒ ๋ง๋ ๋ค.
I avoid holding a database connection during slow network calls, because that can exhaust the connection pool under concurrency.
2. Backend Built
PENDING READY FAILED AI ๋ถ์์ ์ํ ๋จธ์ ์ด๋ค.
Each analysis has a clear state: PENDING, READY, or FAILED, so the UI can show loading, result, or retry.
2. Backend Built
Spring self proxy @Async/@Transactional self-invocation ๋ฌธ์ ๋ฅผ ํผํ๋ค.
Because Spring AOP is proxy-based, internal async or transactional calls go through a self-injected proxy instead of this.method().
6. AI path Built
CompositeAiClient AI provider fallback ๋ํ ๊ตฌํ์ฒด๋ค.
A primary composite AI client tries configured providers in order and falls back when one fails.
6. AI path Built
AiAnalysisClient interface AI vendor๋ฅผ ์จ๊ธฐ๋ ํฌํธ๋ค.
The rest of the backend depends on one AI interface, not vendor-specific client classes.
6. AI path Built
AI JSON parsing LLM ์๋ต์ ๊ตฌ์กฐํํด์ domain object๋ก ๋ฐ๊พผ๋ค.
The AI path expects structured JSON and treats parse failure as a real integration failure.
6. AI path Built
yt-dlp transcript path ์๋ฒ์์ ์ ํ๋ธ ์๋ง์ ๊ฐ์ ธ์ค๋ ๋๊ตฌ๋ค.
The server uses yt-dlp because direct scraping of YouTube caption URLs is fragile and token-dependent.
8. Infra Built
POToken sidecar yt-dlp๊ฐ YouTube caption์ ๋ฐ๊ธฐ ์ํด ํ์ํ ๋ณด์กฐ ์ปจํ
์ด๋๋ค.
The POToken sidecar helps yt-dlp satisfy YouTube's client token requirement for transcript fetching.
6. AI path Built
Supadata fallback ์๋ฒ ์๋ง ์์ง ์คํจ ์ ์ธ๋ถ transcript API๋ก fallbackํ๋ค.
If yt-dlp cannot fetch captions and Supadata is configured, the server can fall back to Supadata.
4. Mobile Built
Device WebView transcript ๋ชจ๋ฐ์ผ์์ ํฐ์ด ์ง์ ์๋ง์ ๊ฐ์ ธ์ค๋ ๊ฒฝ๋ก๋ค.
On mobile, the device can fetch YouTube captions directly through a WebView and send parsed segments to the backend.
7. Data model Built
Global video cache videos table์ ์ ์ ๋ณ ์์ ๋ฌผ์ด ์๋๋ผ ์ ์ญ ์บ์๋ค.
The videos table is a global cache keyed by youtube_id; user-specific ownership lives in library_videos and clips.
2. Backend Built
Race-safe import ๋ ์ฌ์ฉ์๊ฐ ๊ฐ์ ์ ์์์ ๋์์ importํด๋ 500์ ํผํ๋ค.
Concurrent imports are safe because youtube_id is unique; the loser of the insert race re-reads and reuses the row.
7. Data model Built
Postgres JSONB ์๋ง๊ณผ AI ๋ถ์ ๊ฒฐ๊ณผ ๊ฐ์ ๋ฐ๊ตฌ์กฐ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ค.
Postgres gives relational constraints for ownership and JSONB for transcript and AI-result structures.
7. Data model Built
Flyway migrations DB schema ๋ณํ์ ๊ธฐ๋ก์ด๋ค.
The schema is managed through Flyway migrations, so changes are explicit and reproducible.
9. Security Built
JWT token version stateless JWT์ revocation์ ๋ฃ๋ ๋ฐฉ๋ฒ์ด๋ค.
The token is stateless, but revocation works by comparing a token_version claim with the user's current version in the DB.
9. Security Built
BCrypt password hashing ๋น๋ฐ๋ฒํธ๋ฅผ ํ๋ฌธ ์ ์ฅํ์ง ์๋๋ค.
Passwords are hashed with BCrypt, never stored in plain text.
9. Security Built
CORS allowed origins ๋ธ๋ผ์ฐ์ ๊ฐ ํ์ฉ๋ frontend origin์์๋ง credentialed ์์ฒญ์ ํ๊ฒ ํ๋ค.
CORS is configured from allowed origins and fails fast in production instead of falling back to a dangerous wildcard.
9. Security Built
Ownership queries ๋ค๋ฅธ ์ ์ ๋ฐ์ดํฐ ์ ๊ทผ์ ๋ง๋ query ํจํด์ด๋ค.
User-scoped resources are fetched with both id and userId, so another user's id returns not found or forbidden.
9. Security Built
Auth rate limit ๋ก๊ทธ์ธ/๊ฐ์
brute force๋ฅผ ๋ง๋ IP ๊ธฐ๋ฐ ์ ํ์ด๋ค.
Auth endpoints have an IP-based fixed-window rate limiter to reduce credential stuffing risk.
9. Security Built
AI per-user rate limit ๋ ๋๋ AI endpoint๋ฅผ user ๊ธฐ์ค์ผ๋ก ์ ํํ๋ค.
The AI composition check has a per-user limiter because it costs real provider calls.
9. Security Built
Private recording stream ๋
น์ ํ์ผ์ public URL๋ก ๋
ธ์ถํ์ง ์๋๋ค.
Recording files are private; the backend checks ownership and streams the bytes instead of exposing a public object URL.
3. Web frontend Built
Next.js ์น frontend framework๋ค.
The web frontend uses Next.js for routing, locale-aware pages, and a deploy path that fits Vercel.
3. Web frontend Built
TanStack Query ์๋ฒ ์ํ fetching/cache/mutation ๋๊ตฌ๋ค.
React Query owns server state: fetching, caching, mutations, and refresh after writes.
3. Web frontend Built
Zustand ์์ ํด๋ผ์ด์ธํธ ์ํ ์ ์ฅ์๋ค.
Zustand holds small client state, while React Query handles server state.
3. Web frontend Partial
next-intl ์น ๋ค๊ตญ์ด ๋ผ์ฐํ
/๋ฉ์์ง ์์คํ
์ด๋ค.
The web app uses path-based locale routing so the same app can render Korean and English surfaces.
3. Web frontend Built
Tailwind + shadcn/ui ์น UI styling/component stack์ด๋ค.
The web UI uses Tailwind and shadcn-style primitives for consistent, fast component work.
4. Mobile Built
Expo Router ๋ชจ๋ฐ์ผ ํ๋ฉด ๋ผ์ฐํ
์ด๋ค.
The mobile app uses Expo Router so screens are organized as file-based routes, similar in spirit to Next.
4. Mobile Built
Expo audio ๋ชจ๋ฐ์ผ ๋
น์/์ฌ์์ ์ํ native capability๋ค.
Expo audio gives the mobile app native recording and playback capabilities for shadowing practice.
4. Mobile Built
Expo SecureStore ๋ชจ๋ฐ์ผ token ์ ์ฅ์๋ค.
On mobile, tokens are stored through Expo SecureStore instead of plain local storage.
4. Mobile Built
4-tab IA ๋ชจ๋ฐ์ผ ์ฑ์ ์ ๋ณด๊ตฌ์กฐ๋ค.
The mobile app is organized around a small tab structure so repeated training stays reachable on a phone.
4. Mobile Built
Web/mobile parity ์น ๊ธฐ๋ฅ์ ๋ชจ๋ฐ์ผ์์๋ ํต์ฌ ๋ฃจํ๋ก ๊ฐ์ ธ์จ๋ค.
Web and mobile share the learning core and API contracts, while mobile adds native audio and device-side transcript fetching.
5. Domains Built
Auth domain ์ฌ์ฉ์ ์ ์๊ณผ session ๋ณด์ ์ฑ
์์ด๋ค.
The auth domain owns identity: signup, login, password hashing, JWT issuing, and token revocation.
5. Domains Built
Video domain ์ ์ญ ์์ cache์ ์๋ง ์์ง ์ฑ
์์ด๋ค.
The video domain owns the global YouTube cache and transcript ingestion.
5. Domains Built
Clip domain ์ฌ์ฉ์ ํ์ต ๋จ์ ์ฑ
์์ด๋ค.
A clip is the user's study unit: a time range, transcript slice, tags, note, and deck assignment.
5. Domains Built
Analysis domain AI ์ค๋ช
๊ฒฐ๊ณผ ์ฑ
์์ด๋ค.
The analysis domain turns a clip transcript into cached learning material with a visible status.
5. Domains Built
Review domain ํด๋ฆฝ ๋ณต์ต queue ์ฑ
์์ด๋ค.
The review domain schedules saved clips using SM-2 and returns due items to the learner.
5. Domains Built
Practice domain ์ ์ /AI ๋ฌธ์ฅํ๋ จ ์ฑ
์์ด๋ค.
The practice domain handles daily output drills: patterns, collocations, composition checks, scenarios, interview practice, and SRS state.
5. Domains Built
Deck domain ํด๋ฆฝ ์ ๋ฆฌ ์ฑ
์์ด๋ค.
Decks organize clips without owning them; deleting a deck moves clips back to Inbox.
5. Domains Built
Recording domain ์ฌ์ฉ์ ์์ฑ ๋ฐ์ดํฐ ์ฑ
์์ด๋ค.
The recording domain stores the user's spoken attempts and protects them with ownership checks.
5. Domains Partial
Billing entitlement skeleton ๊ฒฐ์ ์์ฒด๊ฐ ์๋๋ผ plan ์ํ ์ ์ฅ ์ฑ
์์ด๋ค.
Billing is an entitlement skeleton: external billing systems can update the user's plan, but Mimi does not process payments itself.
7. Data model Built
PostgreSQL ๊ถํ๊ณผ ํ์ต ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๋ฉ์ธ DB๋ค.
PostgreSQL fits because Mimi needs relational ownership and constraints, plus JSONB for transcripts and AI results.
7. Data model Built
Indexes ์กฐํ/์ ์ฝ ์ฑ๋ฅ์ ์ํ DB ๊ตฌ์กฐ๋ค.
Indexes are tied to access patterns: user queues, unique YouTube imports, tag search, and deck/video lookups.
7. Data model Built
Cascade and cleanup DB row ์ญ์ ์ ํ์ผ ์ญ์ ๋ฅผ ๊ตฌ๋ถํ๋ค.
Database cascades delete rows, but external files need explicit cleanup logic.
7. Data model Built
N+1 avoidance ๋ชฉ๋ก ์กฐํ์์ video๋ฅผ batch๋ก ๊ฐ์ ธ์จ๋ค.
For clip lists, the service batches video lookups to avoid an N+1 query pattern.
8. Infra Built
Docker ์๋น์ค ์คํ ๋จ์๋ฅผ image/container๋ก ๊ณ ์ ํ๋ค.
Docker makes the backend and supporting services reproducible across local and production environments.
8. Infra Built
Caddy NCP ๋ฐฐํฌ์์ TLS/reverse proxy๋ฅผ ๋ด๋นํ๋ค.
Caddy terminates HTTPS and reverse-proxies the public API domain to the backend container.
8. Infra Partial
NCP Seoul box ์ ๋น์ฉ/์ ์ง์ฐ ์ด์ ๊ฒฝ๋ก๋ค.
The NCP path is a cost and latency tradeoff: one Seoul VM instead of managed AWS services.
8. Infra Partial
AWS ECS Fargate path ๊ธฐ์กด/๋ฌธ์ํ๋ managed container ๋ฐฐํฌ ๊ฒฝ๋ก๋ค.
The AWS path uses ECS Fargate for the backend, RDS for Postgres, S3 for recordings, and Secrets Manager for credentials.
8. Infra Built
Vercel web deployment Next.js frontend ๋ฐฐํฌ ๊ฒฝ๋ก๋ค.
The web frontend can be deployed on Vercel while the Spring API runs separately as a container.
8. Infra Partial
Terraform AWS/NCP infra๋ฅผ ์ฝ๋๋ก ํํํ๋ค.
Terraform documents and provisions infrastructure resources instead of relying only on manual console clicks.
8. Infra Partial
GitHub Actions CI/CD push๋ง๋ค ๊ฒ์ฆ/๋ฐฐํฌ๋ฅผ ์๋ํํ๋ ๊ฒฝ๋ก๋ค.
GitHub Actions runs validation and can deploy through keyless OIDC into AWS.
11. Ops & limits Built
Micrometer + Prometheus metric ์์ง ๊ฒฝ๋ก๋ค.
Mimi exposes operational metrics through Micrometer and Prometheus, including custom AI-analysis timing.
11. Ops & limits Built
MDC request/user logging ๋ก๊ทธ์ request id/user id๋ฅผ ํ์ฐ๋ ๊ตฌ์กฐ๋ค.
Logs are tagged with request and user context so production issues can be traced per request.
11. Ops & limits Built
Cost control AI ๋น์ฉ๊ณผ infra ๋น์ฉ์ ์ค์ด๋ ์ค๊ณ๋ค์ด๋ค.
Cost control comes from free captions, cached AI results, provider fallback, and a cheaper deployment path when managed AWS is too expensive.
10. Testing Built
Testcontainers ์ง์ง Postgres๋ฅผ ๋์ ํตํฉ ํ
์คํธํ๋ค.
Backend integration tests use Testcontainers so database behavior is tested against real PostgreSQL.
10. Testing Built
Vitest ํ๋ก ํธ/shared TS ๋ก์ง ํ
์คํธ๋ค.
Vitest covers TypeScript-side pure logic and frontend helpers.
10. Testing Built
Playwright E2E ๋ธ๋ผ์ฐ์ ์ฌ์ฉ์ ํ๋ฆ ํ
์คํธ๋ค.
Playwright tests exercise real browser flows across the app, beyond isolated unit tests.
11. Ops & limits Partial
Mobile release path Expo/iOS release ๊ด๋ จ ํ์ผ์ด ์๋ ์ํ๋ค.
The mobile app has an Expo/iOS release path, separate from the web deployment pipeline.
11. Ops & limits Risk
Honest limits ํ์ฌ ๊ตฌ์กฐ์ ํ๊ณ๋ฅผ ์ ์งํ๊ฒ ๋งํ๋ ์นด๋๋ค.
The current system is practical, but not perfect: async is in-process, NCP is single-box, and YouTube transcript fetching is inherently brittle.