Mimi / shadow-ai X-Ray

Mimi ๊ตฌ์กฐ๋ฅผ ๋ฐฑ์—”๋“œ๋ถ€ํ„ฐ ์ธํ”„๋ผ๊นŒ์ง€ ์„ค๋ช…ํ•˜๋Š” ํŽ˜์ด์ง€

๋ชฉํ‘œ๋Š” ๊ณผ์žฅ๋œ ํฌํŠธํด๋ฆฌ์˜ค ๋ฌธ์žฅ์ด ์•„๋‹ˆ๋‹ค. ์ œํ’ˆ ๋ชฉ์ , ์–ธ์–ด ์„ ํƒ ์ด์œ , backend domains, web/mobile flow, AI pipeline, data model, infra tradeoff๊นŒ์ง€ ๋„ค ์ž…์œผ๋กœ ์„ค๋ช…ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

72
cards
11
domains
10
flows
One sentence

Mimi is a full-stack English shadowing system: YouTube input becomes clips, cached AI explanations, voice recording, SM-2 review, and daily output drills across web and mobile.

์ œํ’ˆ์€ ๋‹จ์ˆœ AI wrapper๊ฐ€ ์•„๋‹ˆ๋ผ, Spring backend๊ฐ€ ์ƒํƒœ/๊ถŒํ•œ/ํŠธ๋žœ์žญ์…˜์„ ์žก๊ณ  Next/Expo๊ฐ€ ๋ฐ˜๋ณต ํ›ˆ๋ จ UI๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ•™์Šต ์‹œ์Šคํ…œ์ด๋‹ค.

Tech stack from absolute zero

์—ฌ๊ธฐ๋Š” Mimi๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•œ ์Šคํƒ ํ•ด๋ถ€๋‹ค. ๊ฐ ์นด๋“œ๋ฅผ ์—ด๋ฉด Java/Spring/Next/Expo/Postgres/JWT/Docker/Caddy ๊ฐ™์€ ๋‹จ์–ด๊ฐ€ ๋ญ”์ง€, ์™œ ์ผ๋Š”์ง€, ๋Œ€์•ˆ๊ณผ trade-off๊ฐ€ ๋ญ”์ง€, ์–ด๋”” ์ฝ”๋“œ์— ์žˆ๋Š”์ง€, ๋ฉด์ ‘ ์˜์–ด ๋ฌธ์žฅ๊นŒ์ง€ ๋ฐ”๋กœ ๋‚˜์˜จ๋‹ค.

47
stack cards

0. Product shape

3
0. Product shapeCore

Full-stack learning product

์œ ํŠœ๋ธŒ ์ž…๋ ฅ์„ ์˜์–ด ์ถœ๋ ฅ ํ›ˆ๋ จ์œผ๋กœ ๋ฐ”๊พธ๋Š” ์ œํ’ˆ ๊ตฌ์กฐ๋‹ค.

Mimi is a full-stack learning loop, not just a prompt UI: the system owns clips, analysis, recordings, review state, and daily drills.

What it does

์˜์ƒ import, clip, AI explanation, recording, review, daily drill์„ ํ•˜๋‚˜์˜ ํ•™์Šต loop๋กœ ์—ฐ๊ฒฐํ•œ๋‹ค.

Why this stack

๋‹จ์ˆœ chat wrapper๊ฐ€ ์•„๋‹ˆ๋ผ ์ƒํƒœ, ๊ถŒํ•œ, ๋ณต์Šต, ๋…น์Œ, ์ฝ˜ํ…์ธ  ์บ์‹œ๊ฐ€ ํ•„์š”ํ•œ ์ œํ’ˆ์ด๋ผ full-stack ๊ตฌ์กฐ๊ฐ€ ๋งž๋‹ค.

Trade-off

๊ตฌํ˜„ ๋ฒ”์œ„๊ฐ€ ๋„“๋‹ค. ๊ทธ๋ž˜์„œ domain boundary์™€ ํ•ต์‹ฌ loop๋ฅผ ๊ณ„์† ์ขํ˜€ ๋งํ•ด์•ผ ํ•œ๋‹ค.

Alternative

์ดˆ๊ธฐ MVP๋ผ๋ฉด mobile-only, web-only, ๋˜๋Š” ๋…ธํŠธ ๊ธฐ๋ฐ˜ ์•ฑ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

Terms you must know
  • full-stack = UI, backend, data, infra๊ฐ€ ๋‹ค ์žˆ๋Š” ์ œํ’ˆ
  • learning loop = ํ•™์Šต์ž๊ฐ€ ๋ฐ˜๋ณตํ•ด์„œ ๋Œ์•„์˜ค๋Š” ํ–‰๋™ ํ๋ฆ„
  • stateful product = ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉ์ž์˜ ์ƒํƒœ๋ฅผ ๊ธฐ์–ตํ•˜๋Š” ์ œํ’ˆ
Where

shadow-ai README, frontend, mobile, backend

Beginner trap

AI๊ฐ€ ์žˆ์œผ๋‹ˆ backend๊ฐ€ ํ•„์š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

0. Product shapeCore

@shadow-ai/core

์›น๊ณผ ๋ชจ๋ฐ”์ผ์ด ๊ณต์œ ํ•˜๋Š” TypeScript ํŒจํ‚ค์ง€๋‹ค.

The shared core package keeps platform-neutral learning content and helpers consistent across web and mobile.

What it does

practice content, parser, SRS helper ๊ฐ™์€ platform-independent logic์„ ๋‹ด๋Š”๋‹ค.

Why this stack

๊ฐ™์€ ํ›ˆ๋ จ ์ฝ˜ํ…์ธ ๋ฅผ web/mobile์—์„œ ๋ณต๋ถ™ํ•˜์ง€ ์•Š๊ณ  ํ•˜๋‚˜์˜ source๋กœ ์œ ์ง€ํ•œ๋‹ค.

Trade-off

๊ณต์œ  ๋ฒ”์œ„๋ฅผ ๋„ˆ๋ฌด ๋„“ํžˆ๋ฉด platform-specific UX๊นŒ์ง€ ์–ต์ง€๋กœ ๋ฌถ์ธ๋‹ค.

Alternative

๊ฐ app ๋‚ด๋ถ€ ์ค‘๋ณต์€ ๋‹จ์ˆœํ•˜์ง€๋งŒ drift๊ฐ€ ์ƒ๊ธฐ๊ณ , backend-generated content๋Š” ์ค‘์•™ ํ†ต์ œ๋Š” ์ข‹์ง€๋งŒ offline/UX ์ œ์•ฝ์ด ์žˆ๋‹ค.

Terms you must know
  • monorepo package = ๊ฐ™์€ repo ์•ˆ์˜ ์žฌ์‚ฌ์šฉ ํŒจํ‚ค์ง€
  • pure helper = platform API ์—†์ด ๊ณ„์‚ฐ๋งŒ ํ•˜๋Š” ํ•จ์ˆ˜
  • drift = ๋ณต์‚ฌ๋ณธ๋“ค์ด ์„œ๋กœ ๋‹ฌ๋ผ์ง€๋Š” ํ˜„์ƒ
Where

packages/core, frontend/mobile package.json

Beginner trap

๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์˜คํžˆ๋ ค ๊ตฌ์กฐ๊ฐ€ ๋ปฃ๋ปฃํ•ด์ง„๋‹ค.

0. Product shapeCore

SM-2 + Leitner SRS

๋ณต์Šต ๊ฐ„๊ฒฉ์„ ์ •ํ•˜๋Š” ํ•™์Šต ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.

Mimi uses different SRS models because clip review and binary practice drills produce different feedback signals.

What it does

clip review๋Š” 0-5 quality ๊ธฐ๋ฐ˜ SM-2, practice drill์€ ๋งž์Œ/ํ‹€๋ฆผ ๊ธฐ๋ฐ˜ Leitner๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

Why this stack

์ฑ„์  ์ž…๋ ฅ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ํ•˜๋‚˜์˜ ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ์–ต์ง€๋กœ ํ•ฉ์น˜์ง€ ์•Š์€ ์ ์ด ์„ค๊ณ„ ํฌ์ธํŠธ๋‹ค.

Trade-off

SRS๋Š” ์‹ค์ œ retention ์‹คํ—˜๊ณผ tuning์ด ํ•„์š”ํ•˜๋‹ค. ์•Œ๊ณ ๋ฆฌ์ฆ˜๋งŒ ๋„ฃ์œผ๋ฉด ํ•™์Šต ํšจ๊ณผ๊ฐ€ ์ฆ๋ช…๋˜์ง€๋Š” ์•Š๋Š”๋‹ค.

Alternative

๋‹จ์ˆœ daily queue, FSRS, spaced repetition SaaS API๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • SRS = spaced repetition system
  • SM-2 = SuperMemo ๊ณ„์—ด ๊ฐ„๊ฒฉ ๋ฐ˜๋ณต ์•Œ๊ณ ๋ฆฌ์ฆ˜
  • Leitner = box๋ฅผ ์˜ฌ๋ฆฌ๊ณ  ๋‚ด๋ฆฌ๋Š” ๋‹จ์ˆœ ๋ฐ˜๋ณต ์‹œ์Šคํ…œ
Where

ReviewService, Sm2Calculator, PracticeSrsService, packages/core/practice-srs

Beginner trap

SRS ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์žˆ์œผ๋ฉด ๊ต์œก ํšจ๊ณผ๊ฐ€ ๊ฒ€์ฆ๋๋‹ค๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

1. Languages

3
1. LanguagesCore

TypeScript

์›น, ๋ชจ๋ฐ”์ผ, ๊ณต์œ  ํŒจํ‚ค์ง€์˜ ์ฃผ ์–ธ์–ด๋‹ค.

TypeScript gives the UI and shared learning content a typed contract across web and mobile.

What it does

React/Next/Expo UI์™€ shared core content๋ฅผ ํƒ€์ž… ์žˆ๋Š” JavaScript๋กœ ์ž‘์„ฑํ•œ๋‹ค.

Why this stack

ํ”„๋ก ํŠธ์™€ ๋ชจ๋ฐ”์ผ์—์„œ ๊ฐ™์€ ์–ธ์–ด๋ฅผ ์“ฐ๋ฉด ๋ชจ๋ธ, helper, ํ•™์Šต ์ฝ˜ํ…์ธ ๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์‰ฝ๋‹ค.

Trade-off

๋Ÿฐํƒ€์ž„ ํƒ€์ž… ๋ณด์žฅ์€ ์•„๋‹ˆ๋‹ค. API boundary์—์„œ๋Š” validation์ด ๋”ฐ๋กœ ํ•„์š”ํ•˜๋‹ค.

Alternative

plain JavaScript๋Š” ๋น ๋ฅด์ง€๋งŒ ํฐ ์•ฑ์—์„œ refactor ์•ˆ์ •์„ฑ์ด ๋‚ฎ๊ณ , Kotlin/Swift native๋Š” ํ”Œ๋žซํผ๋ณ„ ์ค‘๋ณต์ด ๋Š˜์–ด๋‚œ๋‹ค.

Terms you must know
  • type = ๊ฐ’์˜ ๋ชจ์–‘์— ๋Œ€ํ•œ ์•ฝ์†
  • compile-time = ์‹คํ–‰ ์ „์— ์žก๋Š” ๋‹จ๊ณ„
  • runtime = ์‹ค์ œ ์•ฑ์ด ๋„๋Š” ์‹œ๊ฐ„
Where

frontend, mobile, packages/core

Beginner trap

TypeScript๊ฐ€ DB/API ์˜ค๋ฅ˜๊นŒ์ง€ ์ž๋™์œผ๋กœ ๋ง‰๋Š”๋‹ค๊ณ  ๋งํ•˜์ง€ ๋ง๋ผ.

1. LanguagesCore

Java 21

Spring backend์˜ ์–ธ์–ด๋‹ค.

I used Java 21 because the backend needs mature transactions, security, persistence, async jobs, and observability.

What it does

auth, transaction, JPA, async event, AI provider orchestration ๊ฐ™์€ ์„œ๋ฒ„ ๋กœ์ง์„ ์‹คํ–‰ํ•œ๋‹ค.

Why this stack

๋ฐฑ์—”๋“œ ๋ฉด์ ‘์—์„œ ์„ค๋ช… ๊ฐ€๋Šฅํ•œ ์„ฑ์ˆ™ํ•œ ์ƒํƒœ๊ณ„์ด๊ณ , Spring/JPA/Security/Actuator ์กฐํ•ฉ์ด ๊ฐ•ํ•˜๋‹ค.

Trade-off

boilerplate์™€ JVM ์šด์˜ ๋ณต์žก๋„๊ฐ€ ์žˆ๋‹ค. ์ž‘์€ prototype์ด๋ฉด Node/FastAPI๊ฐ€ ๋” ๋น ๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

Alternative

Node.js๋Š” TS ๊ณต์œ ๊ฐ€ ์‰ฝ๊ณ , Go๋Š” ๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ์™€ concurrency๊ฐ€ ์ข‹๊ณ , Python์€ AI prototype์ด ๋น ๋ฅด๋‹ค.

Terms you must know
  • JVM = Java ํ”„๋กœ๊ทธ๋žจ์„ ์‹คํ–‰ํ•˜๋Š” ๊ฐ€์ƒ ๋จธ์‹ 
  • LTS = ์žฅ๊ธฐ ์ง€์› ๋ฒ„์ „
  • backend service = ์„œ๋ฒ„์—์„œ ์ƒํƒœ์™€ ๊ทœ์น™์„ ์ฑ…์ž„์ง€๋Š” ํ”„๋กœ์„ธ์Šค
Where

backend/build.gradle.kts, backend/src/main/java

Beginner trap

Java๋ฅผ ์ผ๋‹ค๊ณ  ์ž๋™์œผ๋กœ enterprise-grade๋ผ๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

1. LanguagesCore

Gradle Kotlin DSL

Java backend ๋นŒ๋“œ ์„ค์ • ์–ธ์–ด๋‹ค.

Gradle Kotlin DSL pins the Java toolchain and dependency graph for the Spring backend.

What it does

Spring Boot plugin, dependency management, Java toolchain, test task๋ฅผ ์„ ์–ธํ•œ๋‹ค.

Why this stack

Kotlin DSL์€ ๋ฌธ์ž์—ด ๊ธฐ๋ฐ˜ Groovy๋ณด๋‹ค IDE ์ง€์›๊ณผ ํƒ€์ž… ํžŒํŠธ๊ฐ€ ์ข‹๋‹ค.

Trade-off

Gradle ์ž์ฒด๊ฐ€ ๋‚ฏ์„ค๋ฉด ๋ณต์žกํ•ด ๋ณด์ธ๋‹ค. ์ž‘์€ ํ”„๋กœ์ ํŠธ๋Š” Maven์ด ๋” ๋‹จ์ˆœํ•  ์ˆ˜ ์žˆ๋‹ค.

Alternative

Maven์€ convention์ด ๊ฐ•ํ•˜๊ณ  ๋‹จ์ˆœํ•˜์ง€๋งŒ, custom build logic์€ Gradle์ด ์œ ์—ฐํ•˜๋‹ค.

Terms you must know
  • build tool = ์ปดํŒŒ์ผ/ํ…Œ์ŠคํŠธ/ํŒจํ‚ค์ง•์„ ์ž๋™ํ™”ํ•˜๋Š” ๋„๊ตฌ
  • DSL = ํŠน์ • ๋ชฉ์ ์— ๋งž์ถ˜ ์ž‘์€ ์–ธ์–ด
  • toolchain = ์–ด๋–ค Java ๋ฒ„์ „์œผ๋กœ ๋นŒ๋“œํ• ์ง€ ๊ณ ์ •ํ•˜๋Š” ์„ค์ •
Where

backend/build.gradle.kts

Beginner trap

Gradle ์„ค์ •์„ application runtime ์ฝ”๋“œ์™€ ํ˜ผ๋™ํ•˜์ง€ ๋ง๋ผ.

3. Frontend

6
3. FrontendCore

Next.js 16

React ์›น์•ฑ์˜ ๋ผ์šฐํŒ…๊ณผ ๋นŒ๋“œ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.

Next gives the web app routing, build, and deployment structure around the React training screens.

What it does

App Router, routing, build, deployment, React ํ™”๋ฉด ๊ตฌ์„ฑ์„ ๋‹ด๋‹นํ•œ๋‹ค.

Why this stack

Mimi web์€ ๋กœ๊ทธ์ธ, library, clip player, review/practice screen์ด ์žˆ์–ด์„œ React ๊ธฐ๋ฐ˜ full app ๊ตฌ์กฐ๊ฐ€ ๋งž๋‹ค.

Trade-off

Next๋Š” framework weight์™€ server/client boundary ๋ณต์žก์„ฑ์ด ์žˆ๋‹ค.

Alternative

Vite SPA๋Š” ๋‹จ์ˆœํ•˜๊ณ  ๋น ๋ฅด๋ฉฐ, Remix๋Š” data mutation ํ๋ฆ„์ด ๊ฐ•ํ•˜๊ณ , plain React๋Š” framework ๊ธฐ๋Šฅ์ด ์ ๋‹ค.

Terms you must know
  • App Router = ํด๋” ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…
  • SSR = ์„œ๋ฒ„์—์„œ HTML์„ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹
  • client component = ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” React component
Where

frontend/package.json, frontend/app

Beginner trap

Next๋ฅผ ์ผ๋‹ค๊ณ  ๋ชจ๋“  ํŽ˜์ด์ง€๊ฐ€ SSR์ด๋ผ๋Š” ๋œป์€ ์•„๋‹ˆ๋‹ค.

3. FrontendCore

React 19

์›น UI๋ฅผ component๋กœ ๋งŒ๋“œ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค.

React lets the product compose repeated learning surfaces as state-driven components.

What it does

player, panels, cards, review flow, settings ๊ฐ™์€ UI๋ฅผ ์ƒํƒœ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ Œ๋”๋งํ•œ๋‹ค.

Why this stack

์ƒํ˜ธ์ž‘์šฉ ๋งŽ์€ ํ•™์Šต ์•ฑ์— component model์ด ์ž˜ ๋งž๊ณ  web/mobile mental model๋„ ๊ณต์œ ๋œ๋‹ค.

Trade-off

์ƒํƒœ๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด re-render์™€ data ownership์„ ์‹ ๊ฒฝ ์จ์•ผ ํ•œ๋‹ค.

Alternative

Vue/Svelte๋Š” ๋” ๋‹จ์ˆœํ•œ syntax๋ฅผ ์ œ๊ณตํ•˜๊ณ , native template framework๋Š” ํ”Œ๋žซํผ ๋ฐ€์ฐฉ๋„๊ฐ€ ๋†’๋‹ค.

Terms you must know
  • component = UI ์กฐ๊ฐ
  • state = ํ™”๋ฉด์„ ๋ฐ”๊พธ๋Š” ๋ฐ์ดํ„ฐ
  • render = state๋ฅผ ํ™”๋ฉด์œผ๋กœ ๊ทธ๋ฆฌ๋Š” ๊ณผ์ •
Where

frontend components/app

Beginner trap

React๋Š” UI library์ด์ง€ backend๋‚˜ DB๋ฅผ ๋Œ€์ฒดํ•˜์ง€ ์•Š๋Š”๋‹ค.

3. FrontendCore

TanStack Query

์„œ๋ฒ„ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์บ์‹œํ•˜๋Š” client state ๋„๊ตฌ๋‹ค.

TanStack Query manages server state, caching, and refetching for the web client.

What it does

clips, analysis, review queue ๊ฐ™์€ API ๊ฒฐ๊ณผ๋ฅผ fetch/cache/refetchํ•œ๋‹ค.

Why this stack

server state์™€ local UI state๋ฅผ ๋ถ„๋ฆฌํ•ด loading/error/retry๋ฅผ ์ผ๊ด€๋˜๊ฒŒ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Trade-off

cache invalidation์„ ์ž˜๋ชปํ•˜๋ฉด ์˜ค๋ž˜๋œ ํ™”๋ฉด์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.

Alternative

SWR์€ ๋” ๋‹จ์ˆœํ•˜๊ณ , ์ง์ ‘ fetch/useEffect๋Š” ์ž‘์ง€๋งŒ ๋ฐ˜๋ณต๊ณผ edge case๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค.

Terms you must know
  • server state = ์„œ๋ฒ„์—์„œ ์˜จ ๋ฐ์ดํ„ฐ
  • cache = ๋‹ค์‹œ ์“ฐ๋ ค๊ณ  ์ €์žฅํ•œ ์‘๋‹ต
  • invalidation = ์บ์‹œ๋ฅผ ์˜ค๋ž˜๋๋‹ค๊ณ  ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ
Where

frontend package.json, API hooks

Beginner trap

React Query๋ฅผ ์“ฐ๋ฉด backend consistency ๋ฌธ์ œ๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค.

3. FrontendCore

Zustand

๋ธŒ๋ผ์šฐ์ € ์•ˆ์˜ ์ž‘์€ UI ์ƒํƒœ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” ์ €์žฅ์†Œ๋‹ค.

Zustand is used for lightweight local UI state, while server data belongs in TanStack Query.

What it does

player ์ƒํƒœ, local UI preference, transient interaction state๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ด€๋ฆฌํ•œ๋‹ค.

Why this stack

Redux๋ณด๋‹ค ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์ ๊ณ , ๋ณต์žกํ•˜์ง€ ์•Š์€ ์•ฑ ์ƒํƒœ์— ๋น ๋ฅด๋‹ค.

Trade-off

ํฐ ์•ฑ์—์„œ store๊ฐ€ ๋ฌด์งˆ์„œํ•ด์ง€๋ฉด ์ถ”์ ์ด ์–ด๋ ต๋‹ค. ์„œ๋ฒ„ ์ƒํƒœ๊นŒ์ง€ ๋„ฃ์œผ๋ฉด ๊ผฌ์ธ๋‹ค.

Alternative

Redux Toolkit์€ ๊ทœ์น™์ด ๊ฐ•ํ•˜๊ณ , Jotai๋Š” atom ๊ธฐ๋ฐ˜์ด๋ฉฐ, React context๋Š” ์ž‘์€ ๋ฒ”์œ„์— ์ ํ•ฉํ•˜๋‹ค.

Terms you must know
  • client state = ๋ธŒ๋ผ์šฐ์ € ์•ˆ์—์„œ๋งŒ ํ•„์š”ํ•œ ์ƒํƒœ
  • store = ์—ฌ๋Ÿฌ component๊ฐ€ ๊ณต์œ ํ•˜๋Š” ์ƒํƒœ ์ €์žฅ์†Œ
  • transient = ์ž ๊น ํ•„์š”ํ•œ ์ƒํƒœ
Where

frontend package.json, frontend stores

Beginner trap

๋ชจ๋“  ์ƒํƒœ๋ฅผ Zustand์— ๋„ฃ์œผ๋ฉด ์•ˆ ๋œ๋‹ค.

3. FrontendCore

next-intl

๋‹ค๊ตญ์–ด routing๊ณผ message๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.

next-intl keeps localized UI text and routes explicit for a language-learning product.

What it does

์˜์–ด/ํ•œ๊ตญ์–ด UI text์™€ locale path๋ฅผ Next ๊ตฌ์กฐ ์•ˆ์—์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Why this stack

์˜์–ด ํ•™์Šต ์ œํ’ˆ์ด๋ผ language surface๊ฐ€ ์ œํ’ˆ ํ•ต์‹ฌ์ด๋ฏ€๋กœ i18n ๊ตฌ์กฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Trade-off

๋ฒˆ์—ญ key ๊ด€๋ฆฌ๊ฐ€ ๋Š˜๊ณ , dynamic text์™€ ์ฝ˜ํ…์ธ  ๋ฒˆ์—ญ ๊ฒฝ๊ณ„๊ฐ€ ํ—ท๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

Alternative

next-i18next, custom dictionary, CMS ๊ธฐ๋ฐ˜ translation์ด ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • i18n = internationalization, ๋‹ค๊ตญ์–ด ์ค€๋น„
  • locale = en/ko ๊ฐ™์€ ์ง€์—ญ/์–ธ์–ด ์„ค์ •
  • message key = ๋ฒˆ์—ญ ๋ฌธ์žฅ์„ ์ฐพ๋Š” ์ด๋ฆ„
Where

frontend package.json, locale routes/messages

Beginner trap

i18n์€ ๋ฒˆ์—ญ ํŒŒ์ผ๋งŒ์ด ์•„๋‹ˆ๋ผ route, formatting, product copy ๋ฌธ์ œ๋‹ค.

3. FrontendCore

Tailwind + shadcn/ui

UI ์Šคํƒ€์ผ๊ณผ component ๊ธฐ๋ณธํ˜•์„ ๋งŒ๋“ ๋‹ค.

Tailwind and shadcn let the app ship consistent UI quickly without building every primitive from scratch.

What it does

๋ฐ˜๋ณต ํ™”๋ฉด, ์นด๋“œ, ๋ฒ„ํŠผ, dialog, form์„ ๋น ๋ฅด๊ฒŒ ์กฐ๋ฆฝํ•œ๋‹ค.

Why this stack

ํ•™์Šต ์•ฑ์€ ํ™”๋ฉด์ด ๋งŽ์•„์„œ utility CSS์™€ component recipe๊ฐ€ ์ƒ์‚ฐ์„ฑ์„ ์ค€๋‹ค.

Trade-off

ํด๋ž˜์Šค๊ฐ€ ๊ธธ์–ด์ง€๊ณ  ๋””์ž์ธ ์ฒด๊ณ„ ์—†์ด ์“ฐ๋ฉด ํ™”๋ฉด์ด ์‚ฐ๋งŒํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

Alternative

CSS Modules, MUI, Chakra, Radix ์ง์ ‘ ์กฐํ•ฉ, custom design system์ด ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • utility CSS = ์ž‘์€ class๋ฅผ ์กฐํ•ฉํ•˜๋Š” ์Šคํƒ€์ผ ๋ฐฉ์‹
  • component recipe = ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ UI ์กฐํ•ฉ
  • design system = ์ผ๊ด€๋œ UI ๊ทœ์น™ ๋ฌถ์Œ
Where

frontend package.json, components/ui

Beginner trap

shadcn์€ ์„ค์น˜ํ˜• ์ฝ”๋“œ์ด์ง€ ์™„์„ฑ๋œ ๋””์ž์ธ ์‹œ์Šคํ…œ์ด ์•„๋‹ˆ๋‹ค.

4. Mobile

6
4. MobileCore

Expo 56

React Native ์•ฑ ๊ฐœ๋ฐœ/๋นŒ๋“œ ํ”Œ๋žซํผ์ด๋‹ค.

Expo gives Mimi a fast path to a real mobile training surface with native audio, storage, and routing.

What it does

iOS/Android dev server, native module integration, release script, Expo Router entry๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

Why this stack

๊ฑธ์œผ๋ฉด์„œ ๋ฐ˜๋ณต ํ›ˆ๋ จํ•˜๋Š” phone-first ์ œํ’ˆ์ด๋ผ mobile app์ด ํ•„์š”ํ•˜๊ณ  Expo๊ฐ€ ๋น ๋ฅธ iteration์„ ์ค€๋‹ค.

Trade-off

native edge case๊ฐ€ ์ƒ๊ธฐ๋ฉด Expo abstraction์„ ์ดํ•ดํ•ด์•ผ ํ•˜๊ณ , bare/native ์„ค์ • ์ง€์‹๋„ ํ•„์š”ํ•˜๋‹ค.

Alternative

์ˆœ์ˆ˜ React Native CLI๋Š” native ์ œ์–ด๊ฐ€ ๊ฐ•ํ•˜๊ณ , Swift/Kotlin native๋Š” UX ๋ฐ€์ฐฉ๋„๊ฐ€ ๋†’์ง€๋งŒ ๊ฐœ๋ฐœ๋Ÿ‰์ด ๋Š˜์–ด๋‚œ๋‹ค.

Terms you must know
  • managed workflow = Expo๊ฐ€ native ์„ค์ • ์ผ๋ถ€๋ฅผ ๊ด€๋ฆฌ
  • native module = OS ๊ธฐ๋Šฅ์„ ์“ฐ๋Š” bridge
  • dev client = ๊ฐœ๋ฐœ์šฉ ์•ฑ shell
Where

mobile/package.json, mobile app config

Beginner trap

Expo๋Š” ์›น์•ฑ ํฌ์žฅ์ง€๊ฐ€ ์•„๋‹ˆ๋ผ native app build system์ด๋‹ค.

4. MobileCore

React Native

React ๋ฐฉ์‹์œผ๋กœ native mobile UI๋ฅผ ๋งŒ๋“œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.

React Native lets Mimi reuse React patterns while delivering a phone-native training experience.

What it does

ํƒญ, ํ™”๋ฉด, gesture, safe area, WebView, recording UI๋ฅผ iOS/Android component๋กœ ๋งŒ๋“ ๋‹ค.

Why this stack

React mental model์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์‹ค์ œ phone UX๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Trade-off

ํ”Œ๋žซํผ๋ณ„ ์ฐจ์ด์™€ native module ๋ฌธ์ œ๋Š” ํ”ผํ•  ์ˆ˜ ์—†๋‹ค.

Alternative

SwiftUI/Kotlin native๋Š” ์„ฑ๋Šฅ๊ณผ platform fit์ด ์ข‹๊ณ , Flutter๋Š” ์ž์ฒด rendering stack์ด ๊ฐ•ํ•˜๋‹ค.

Terms you must know
  • bridge = JS์™€ native ์‚ฌ์ด ์—ฐ๊ฒฐ
  • native view = OS๊ฐ€ ๊ทธ๋ฆฌ๋Š” ์‹ค์ œ UI ์š”์†Œ
  • safe area = notch/home indicator๋ฅผ ํ”ผํ•˜๋Š” ํ™”๋ฉด ์˜์—ญ
Where

mobile/src/app, mobile components

Beginner trap

React Native๊ฐ€ ๋ธŒ๋ผ์šฐ์ € React์™€ ์™„์ „ํžˆ ๊ฐ™๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

4. MobileCore

Expo Router

๋ชจ๋ฐ”์ผ ํ™”๋ฉด ์ด๋™์„ ํŒŒ์ผ ๊ตฌ์กฐ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋ผ์šฐํ„ฐ๋‹ค.

Expo Router gives the mobile app a file-based navigation model similar to modern web routing.

What it does

mobile/src/app ํด๋” ๊ตฌ์กฐ๋กœ tabs, stack screens, dynamic routes๋ฅผ ๋งŒ๋“ ๋‹ค.

Why this stack

Next App Router์™€ ๋น„์Šทํ•œ mental model์ด๋ผ web/mobile navigation์„ ์„ค๋ช…ํ•˜๊ธฐ ์‰ฝ๋‹ค.

Trade-off

๋ณต์žกํ•œ deep link์™€ auth guard๋Š” ๊ตฌ์กฐ๋ฅผ ์ž˜ ์žก์•„์•ผ ํ•œ๋‹ค.

Alternative

React Navigation ์ง์ ‘ ๊ตฌ์„ฑ์€ ๋” ์œ ์—ฐํ•˜์ง€๋งŒ boilerplate๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค.

Terms you must know
  • route = ์ด๋™ ๊ฐ€๋Šฅํ•œ ํ™”๋ฉด ๊ฒฝ๋กœ
  • stack = ํ™”๋ฉด์„ ์Œ“๋Š” navigation ๋ฐฉ์‹
  • tabs = ํ•˜๋‹จ ํƒญ navigation
Where

mobile/src/app

Beginner trap

๋ผ์šฐํŒ…์€ ํ™”๋ฉด ์ „ํ™˜์ด์ง€ ์„œ๋ฒ„ API ์„ค๊ณ„๊ฐ€ ์•„๋‹ˆ๋‹ค.

4. MobileCore

Expo SecureStore

๋ชจ๋ฐ”์ผ์— ๋ฏผ๊ฐํ•œ ๊ฐ’์„ ์ €์žฅํ•˜๋Š” secure storage๋‹ค.

SecureStore is used so mobile auth tokens are not stored as plain local app data.

What it does

JWT ๊ฐ™์€ session token์„ ์ผ๋ฐ˜ AsyncStorage๋ณด๋‹ค ์•ˆ์ „ํ•œ OS storage์— ๋„ฃ๋Š”๋‹ค.

Why this stack

๋ชจ๋ฐ”์ผ token์€ ํƒˆ์ทจ ์œ„ํ—˜์ด ์žˆ์œผ๋ฏ€๋กœ OS keychain/keystore ๊ณ„์ธต์„ ์จ์•ผ ํ•œ๋‹ค.

Trade-off

๊ธฐ๊ธฐ ๋ฐฑ์—…/์ƒ์ฒด์ธ์ฆ/์‚ญ์ œ ๋™์ž‘์€ ํ”Œ๋žซํผ๋ณ„๋กœ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๋‹ค.

Alternative

Keychain ์ง์ ‘ ์‚ฌ์šฉ, MMKV encrypted storage, server session cookie๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • keychain = iOS ๋ณด์•ˆ ์ €์žฅ์†Œ
  • keystore = Android ๋ณด์•ˆ ์ €์žฅ์†Œ
  • token storage = ๋กœ๊ทธ์ธ ์ฆ๋ช…์„ ์–ด๋””์— ๋ณด๊ด€ํ•˜๋Š”์ง€
Where

mobile package.json, auth storage

Beginner trap

SecureStore๋ฅผ ์“ด๋‹ค๊ณ  jailbroken/rooted device risk๊ฐ€ ์—†์–ด์ง€์ง€๋Š” ์•Š๋Š”๋‹ค.

4. MobileCore

React Native WebView

์•ฑ ์•ˆ์—์„œ ์›น ํŽ˜์ด์ง€/์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” native view๋‹ค.

The mobile WebView path exists because transcript ingestion sometimes works better from the user's device than from a server IP.

What it does

๋ชจ๋ฐ”์ผ์—์„œ YouTube caption endpoint๋ฅผ device network๋กœ fetchํ•˜๋Š” ๋ณด์กฐ ๊ฒฝ๋กœ์— ์‚ฌ์šฉ๋œ๋‹ค.

Why this stack

์„œ๋ฒ„ datacenter IP๊ฐ€ ๋ง‰ํž ๋•Œ ์‚ฌ์šฉ์ž device path๊ฐ€ import reliability๋ฅผ ๋†’์ธ๋‹ค.

Trade-off

WebView injection์€ brittleํ•˜๊ณ  policy/๋ณด์•ˆ/๋””๋ฒ„๊น… ๋ฆฌ์Šคํฌ๊ฐ€ ์žˆ๋‹ค.

Alternative

server-only yt-dlp, paid transcript API, user-uploaded caption์ด ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • WebView = ์•ฑ ์•ˆ์˜ ์ž‘์€ ๋ธŒ๋ผ์šฐ์ €
  • injected JS = WebView ์•ˆ์— ๋„ฃ์–ด ์‹คํ–‰ํ•˜๋Š” script
  • device IP = ์‚ฌ์šฉ์ž์˜ ํฐ ๋„คํŠธ์›Œํฌ IP
Where

mobile/src/lib/youtube-transcript-webview.tsx

Beginner trap

WebView ์ˆ˜์ง‘์ด ๊ณต์‹ API์ฒ˜๋Ÿผ ์•ˆ์ •์ ์ด๋ผ๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

4. MobileCore

expo-audio / speech recognition

์‚ฌ์šฉ์ž ์Œ์„ฑ ๋…น์Œ๊ณผ ๋ฐœํ™” ํ›ˆ๋ จ์— ์“ฐ๋Š” native ๊ธฐ๋Šฅ์ด๋‹ค.

Native audio support is central because Mimi trains spoken output, not only reading comprehension.

What it does

์‰๋„์ž‰ ๋…น์Œ, ์žฌ์ƒ, ๋ฐœํ™” ์ž…๋ ฅ/์ธ์‹ flow๋ฅผ mobile์—์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Why this stack

์˜์–ด ํ•™์Šต ์ œํ’ˆ์€ ์ฝ๊ธฐ๋ณด๋‹ค ๋งํ•˜๊ธฐ output์ด ์ค‘์š”ํ•ด์„œ audio capability๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค.

Trade-off

๊ถŒํ•œ ์š”์ฒญ, device compatibility, background behavior, file size ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

Alternative

์›น MediaRecorder, native AVFoundation/MediaRecorder, third-party speech SDK๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • audio recording = ๋งˆ์ดํฌ ์ž…๋ ฅ์„ ํŒŒ์ผ/stream์œผ๋กœ ์ €์žฅ
  • permission = OS๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ—ˆ๋ฝ๋ฐ›๋Š” ๊ถŒํ•œ
  • speech recognition = ์Œ์„ฑ์„ text๋กœ ๋ฐ”๊พธ๋Š” ๊ธฐ๋Šฅ
Where

mobile/package.json, recording screens

Beginner trap

๋งˆ์ดํฌ ๊ธฐ๋Šฅ์€ UI ๋ฒ„ํŠผ๋งŒ์œผ๋กœ ๋๋‚˜์ง€ ์•Š๊ณ  ๊ถŒํ•œ/์ €์žฅ/์—…๋กœ๋“œ/์‚ญ์ œ๊ฐ€ ๋”ฐ๋ผ์˜จ๋‹ค.

5. Backend

5
5. BackendCore

Spring Boot

Java backend๋ฅผ ๋น ๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.

Spring Boot lets the backend focus on domain logic while relying on mature HTTP, security, persistence, and observability modules.

What it does

HTTP API, dependency injection, security, validation, JPA, actuator ์„ค์ •์„ ์ž๋™ ๊ตฌ์„ฑํ•œ๋‹ค.

Why this stack

Mimi๋Š” ์‚ฌ์šฉ์ž ์ƒํƒœ์™€ ํŠธ๋žœ์žญ์…˜์ด ๋งŽ์•„์„œ Spring์˜ convention๊ณผ ์ƒํƒœ๊ณ„๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒŒ ์ด๋“์ด๋‹ค.

Trade-off

์ถ”์ƒํ™”๊ฐ€ ๋งŽ์•„ ๋‚ด๋ถ€ ๋™์ž‘์„ ๋ชจ๋ฅด๋ฉด magic์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค. cold start์™€ memory๋„ Node/Go๋ณด๋‹ค ๋ฌด๊ฑฐ์šธ ์ˆ˜ ์žˆ๋‹ค.

Alternative

Express/NestJS๋Š” TS ์ผ๊ด€์„ฑ์ด ์ข‹๊ณ , FastAPI๋Š” AI prototype์ด ๋น ๋ฅด๊ณ , Go HTTP๋Š” ๋‹จ์ˆœํ•˜๊ณ  ๊ฐ€๋ณ๋‹ค.

Terms you must know
  • framework = ๊ณตํ†ต ๊ตฌ์กฐ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํ‹€
  • dependency injection = ํ•„์š”ํ•œ ๊ฐ์ฒด๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹
  • auto-configuration = ์„ค์ •์„ ๊ด€๋ก€๋Œ€๋กœ ์ž๋™ ์กฐ๋ฆฝํ•˜๋Š” ๊ธฐ๋Šฅ
Where

backend/build.gradle.kts, backend/src/main/java/com/tubeshadow

Beginner trap

Spring Boot๊ฐ€ business design์„ ๋Œ€์‹  ํ•ด์ฃผ์ง€๋Š” ์•Š๋Š”๋‹ค.

5. BackendCore

Spring MVC REST API

๋ธŒ๋ผ์šฐ์ €/์•ฑ์ด ํ˜ธ์ถœํ•˜๋Š” HTTP API layer๋‹ค.

The REST API is the contract between the clients and the backend-owned domain state.

What it does

Controller๊ฐ€ request๋ฅผ ๋ฐ›๊ณ  Service๊ฐ€ domain logic์„ ์ฒ˜๋ฆฌํ•˜๋ฉฐ DTO๋กœ ์‘๋‹ตํ•œ๋‹ค.

Why this stack

web/mobile์ด ๊ฐ™์€ backend contract๋ฅผ ์“ฐ๊ธฐ ๋•Œ๋ฌธ์— REST API๊ฐ€ ๋ช…ํ™•ํ•œ integration point๊ฐ€ ๋œ๋‹ค.

Trade-off

REST๊ฐ€ ๋ณต์žกํ•œ ์‹ค์‹œ๊ฐ„ ํ˜‘์—…์—๋Š” ๋ถˆํŽธํ•  ์ˆ˜ ์žˆ๋‹ค. over-fetching๋„ ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค.

Alternative

GraphQL์€ ํด๋ผ์ด์–ธํŠธ query ์ž์œ ๋„๊ฐ€ ๋†’๊ณ , gRPC๋Š” ๋‚ด๋ถ€ service ๊ฐ„ contract๊ฐ€ ๊ฐ•ํ•˜๋‹ค.

Terms you must know
  • REST = URL๊ณผ HTTP method๋กœ resource๋ฅผ ๋‹ค๋ฃจ๋Š” API ์Šคํƒ€์ผ
  • DTO = API ์ž…์ถœ๋ ฅ ์ „์šฉ ๊ฐ์ฒด
  • controller = HTTP boundary
Where

backend/*/api/*Controller.java

Beginner trap

Controller์— ๋ชจ๋“  ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋„ฃ์œผ๋ฉด ๊ตฌ์กฐ๊ฐ€ ๋ฌด๋„ˆ์ง„๋‹ค.

5. BackendCore

Bean Validation

์ž…๋ ฅ๊ฐ’์„ ์„œ๋ฒ„์—์„œ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

Server-side validation protects the API boundary even if the client is buggy or bypassed.

What it does

DTO์— NotBlank, Size ๊ฐ™์€ ์ œ์•ฝ์„ ๋‘๊ณ  ์ž˜๋ชป๋œ ์š”์ฒญ์„ ๋น ๋ฅด๊ฒŒ ๊ฑฐ์ ˆํ•œ๋‹ค.

Why this stack

ํด๋ผ์ด์–ธํŠธ UI ๊ฒ€์ฆ์€ ์šฐํšŒ ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ backend validation์ด ์ง„์งœ ๊ฒฝ๊ณ„๋‹ค.

Trade-off

๋ณต์žกํ•œ cross-field rule์€ annotation๋งŒ์œผ๋กœ ๋ถ€์กฑํ•ด์„œ service validation์ด ํ•„์š”ํ•˜๋‹ค.

Alternative

์ˆ˜๋™ if๋ฌธ์€ ๋ช…ํ™•ํ•˜์ง€๋งŒ ๋ฐ˜๋ณต์ด ๋Š˜๊ณ , JSON schema validation์€ API boundary์— ๊ฐ•ํ•˜๋‹ค.

Terms you must know
  • validation = ์ž…๋ ฅ์ด ๊ทœ์น™์— ๋งž๋Š”์ง€ ๊ฒ€์‚ฌ
  • constraint = ๊ฐ’์ด ์ง€์ผœ์•ผ ํ•˜๋Š” ์กฐ๊ฑด
  • boundary = ์‹ ๋ขฐํ•  ์ˆ˜ ์—†๋Š” ์ž…๋ ฅ์ด ๋“ค์–ด์˜ค๋Š” ๊ฒฝ๊ณ„
Where

spring-boot-starter-validation, request DTOs

Beginner trap

ํ”„๋ก ํŠธ ๊ฒ€์ฆ์ด ์žˆ์œผ๋‹ˆ ์„œ๋ฒ„ ๊ฒ€์ฆ์ด ํ•„์š” ์—†๋‹ค๊ณ  ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค.

5. BackendCore

AFTER_COMMIT async event

ํด๋ฆฝ ์ €์žฅ ํ›„ AI ๋ถ„์„์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ๋„˜๊ธฐ๋Š” ๊ตฌ์กฐ๋‹ค.

The AI job runs after commit and outside the database transaction so slow provider calls do not hold DB connections.

What it does

ClipService๊ฐ€ DB commit ํ›„ event๋ฅผ ๋ฐœํ–‰ํ•˜๊ณ  ClipAnalysisService๊ฐ€ @Async๋กœ provider ํ˜ธ์ถœ์„ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Why this stack

LLM ํ˜ธ์ถœ์€ ๋А๋ฆฌ๊ณ  ์‹คํŒจ ๊ฐ€๋Šฅํ•ด์„œ request transaction ์•ˆ์—์„œ ์ฒ˜๋ฆฌํ•˜๋ฉด connection๊ณผ UX๊ฐ€ ๋ง๊ฐ€์ง„๋‹ค.

Trade-off

in-process async๋ผ ์„œ๋ฒ„๊ฐ€ ์ฃฝ์œผ๋ฉด ์ž‘์—… ์œ ์‹ค ์œ„ํ—˜์ด ์žˆ๋‹ค. retry queue๊ฐ€ ๋” ๊ฐ•ํ•˜๋‹ค.

Alternative

SQS, RabbitMQ, Kafka, BullMQ ๊ฐ™์€ durable queue๋ฅผ ์“ฐ๋ฉด ๋ณต๊ตฌ๋ ฅ์ด ์ข‹์•„์ง„๋‹ค.

Terms you must know
  • transaction = DB ์ž‘์—…์„ ํ•œ ๋ฌถ์Œ์œผ๋กœ commit/rollbackํ•˜๋Š” ๋‹จ์œ„
  • AFTER_COMMIT = DB commit ์„ฑ๊ณต ํ›„ ์‹คํ–‰
  • async = ์š”์ฒญ ํ๋ฆ„๊ณผ ๋ถ„๋ฆฌํ•ด์„œ ๋‚˜์ค‘์— ์‹คํ–‰
Where

ClipService.java, ClipAnalysisService.java

Beginner trap

๋น„๋™๊ธฐ๋ผ๊ณ  ํ•ด์„œ ์ž๋™์œผ๋กœ durable job queue๊ฐ€ ๋˜๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค.

5. BackendCore

RestClient / WebClient

์™ธ๋ถ€ HTTP API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” Spring client๋‹ค.

External API calls are isolated behind clients so provider-specific failures do not leak into domain code.

What it does

LLM provider, YouTube-related service, Supadata ๊ฐ™์€ ์™ธ๋ถ€ endpoint์™€ ํ†ต์‹ ํ•œ๋‹ค.

Why this stack

Spring ์ƒํƒœ๊ณ„ ์•ˆ์—์„œ timeout, serialization, error handling์„ ๊ตฌ์„ฑํ•˜๊ธฐ ์ข‹๋‹ค.

Trade-off

blocking client๋ฅผ async path์—์„œ ์ž˜๋ชป ์“ฐ๋ฉด thread๋ฅผ ์žก์•„๋จน์„ ์ˆ˜ ์žˆ๋‹ค.

Alternative

OkHttp, Feign, raw Java HttpClient, Node fetch๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • HTTP client = ๋‹ค๋ฅธ ์„œ๋ฒ„๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ
  • timeout = ๋„ˆ๋ฌด ์˜ค๋ž˜ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋Š๋Š” ์‹œ๊ฐ„
  • retry = ์‹คํŒจํ•œ ํ˜ธ์ถœ์„ ๋‹ค์‹œ ์‹œ๋„
Where

AI clients, YoutubeTranscriptClient

Beginner trap

์™ธ๋ถ€ ํ˜ธ์ถœ์—๋Š” timeout๊ณผ failure handling์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๋‹ค.

6. Data

5
6. DataCore

Spring Data JPA / Hibernate

Java ๊ฐ์ฒด์™€ DB table์„ ์—ฐ๊ฒฐํ•˜๋Š” persistence layer๋‹ค.

JPA is useful for domain persistence, but I still need to watch query shape and transaction boundaries.

What it does

Repository์™€ entity๋กœ users/videos/clips/reviews ๊ฐ™์€ row๋ฅผ ์ฝ๊ณ  ์“ด๋‹ค.

Why this stack

CRUD์™€ transaction ์ž‘์—…์ด ๋งŽ์•„ ์ƒ์‚ฐ์„ฑ์ด ์ข‹๊ณ , domain code๊ฐ€ SQL ๋ฌธ์ž์—ด์— ๋ฌปํžˆ์ง€ ์•Š๋Š”๋‹ค.

Trade-off

N+1, lazy loading, transaction boundary๋ฅผ ๋ชจ๋ฅด๋ฉด ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋‚œ๋‹ค.

Alternative

jOOQ๋Š” SQL ์ œ์–ด๊ฐ€ ๊ฐ•ํ•˜๊ณ , MyBatis๋Š” ๋ช…์‹œ SQL์ด ์‰ฝ๊ณ , raw SQL์€ ๋‹จ์ˆœํ•˜์ง€๋งŒ ๋ฐ˜๋ณต์ด ๋Š˜์–ด๋‚œ๋‹ค.

Terms you must know
  • ORM = ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• DB๋ฅผ ๋งคํ•‘ํ•˜๋Š” ๊ธฐ์ˆ 
  • entity = DB row์™€ ์—ฐ๊ฒฐ๋˜๋Š” Java ๊ฐ์ฒด
  • lazy loading = ํ•„์š”ํ•  ๋•Œ ์—ฐ๊ด€ ๋ฐ์ดํ„ฐ๋ฅผ ๋Šฆ๊ฒŒ ์ฝ๋Š” ๋ฐฉ์‹
Where

backend repositories/entities

Beginner trap

JPA๋ฅผ ์“ฐ๋ฉด SQL์„ ๋ชฐ๋ผ๋„ ๋œ๋‹ค๊ณ  ๋งํ•˜์ง€ ๋ง๋ผ.

6. DataCore

PostgreSQL

๊ถŒํ•œ๊ณผ ํ•™์Šต ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ์ฃผ DB๋‹ค.

Postgres fits because Mimi needs relational ownership and constraints plus JSONB for semi-structured transcripts and AI analysis.

What it does

users, videos, library, clips, analyses, recordings, review/practice state๋ฅผ ์ €์žฅํ•œ๋‹ค.

Why this stack

๊ด€๊ณ„ํ˜• ์†Œ์œ ๊ถŒ๊ณผ constraint๊ฐ€ ์ค‘์š”ํ•˜๊ณ , transcript/analysis๋Š” JSONB๋กœ ๋‹ด์„ ์ˆ˜ ์žˆ์–ด ์ž˜ ๋งž๋‹ค.

Trade-off

์ˆ˜์ง ํ™•์žฅ๊ณผ connection ์ˆ˜ ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ๋ฌด๊ฑฐ์šด analytics/search๋Š” ๋ณ„๋„ ๊ณ„์ธต์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค.

Alternative

MongoDB๋Š” flexible document์— ๊ฐ•ํ•˜๊ณ , DynamoDB๋Š” managed scale์ด ์ข‹์ง€๋งŒ relational query์™€ constraint๊ฐ€ ์•ฝํ•ด์ง„๋‹ค.

Terms you must know
  • RDBMS = table ๊ด€๊ณ„์™€ transaction์„ ์ œ๊ณตํ•˜๋Š” DB
  • constraint = DB๊ฐ€ ๊ฐ•์ œํ•˜๋Š” ๊ทœ์น™
  • JSONB = JSON์„ binary ํ˜•ํƒœ๋กœ ์ €์žฅ/๊ฒ€์ƒ‰ํ•˜๋Š” Postgres ํƒ€์ž…
Where

backend migrations, docker-compose

Beginner trap

Postgres๋ฅผ ์“ฐ๋ฉด ํ™•์žฅ ๋ฌธ์ œ๊ฐ€ ๋๋‚œ๋‹ค๊ณ  ๋งํ•˜์ง€ ๋ง๋ผ.

6. DataCore

Flyway migrations

DB schema ๋ณ€๊ฒฝ์„ ๋ฒ„์ „ ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

Flyway makes the database schema versioned and reproducible across local, test, and production.

What it does

V1, V2 ๊ฐ™์€ SQL migration์œผ๋กœ table/index/constraint ๋ณ€ํ™”๋ฅผ ์žฌํ˜„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.

Why this stack

์ฝ”๋“œ์™€ DB schema๊ฐ€ ๊ฐ™์ด ์ง„ํ™”ํ•ด์•ผ ํ•˜๋ฏ€๋กœ migration history๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Trade-off

์ž˜๋ชป ๋ฐฐํฌํ•œ migration์€ ๋˜๋Œ๋ฆฌ๊ธฐ ์–ด๋ ต๋‹ค. backward-compatible migration discipline์ด ํ•„์š”ํ•˜๋‹ค.

Alternative

Liquibase๋Š” ๋” ๋งŽ์€ ํ˜•์‹๊ณผ rollback ๊ธฐ๋Šฅ์ด ์žˆ๊ณ , ์ˆ˜๋™ SQL์€ ์ž‘์ง€๋งŒ ์ถ”์ ์„ฑ์ด ์•ฝํ•˜๋‹ค.

Terms you must know
  • migration = DB ๊ตฌ์กฐ ๋ณ€๊ฒฝ ๊ธฐ๋ก
  • schema = table/index/constraint ์„ค๊ณ„
  • backward-compatible = ๊ตฌ๋ฒ„์ „ ์ฝ”๋“œ์™€๋„ ๊นจ์ง€์ง€ ์•Š๋Š” ๋ณ€๊ฒฝ
Where

backend/src/main/resources/db/migration

Beginner trap

entity๋งŒ ๋ฐ”๊พธ๊ณ  DB๊ฐ€ ์ž๋™์œผ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐ”๋€๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

6. DataCore

JSONB

์ž๋ง‰๊ณผ AI ๊ฒฐ๊ณผ ๊ฐ™์€ ๋ฐ˜๊ตฌ์กฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” Postgres ํƒ€์ž…์ด๋‹ค.

JSONB is a pragmatic fit for AI and transcript payloads that are structured but still evolving.

What it does

transcript segments, analysis payload์ฒ˜๋Ÿผ nested structure๋ฅผ column ํ•˜๋‚˜์— ๋ณด๊ด€ํ•œ๋‹ค.

Why this stack

AI ๊ฒฐ๊ณผ schema๊ฐ€ ๋ณ€ํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋ชจ๋“  ํ•„๋“œ๋ฅผ table๋กœ ์ฐข๊ธฐ๋ณด๋‹ค JSONB๊ฐ€ ์œ ์—ฐํ•˜๋‹ค.

Trade-off

query๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด index ์„ค๊ณ„๊ฐ€ ์–ด๋ ต๊ณ , DB constraint๊ฐ€ ์•ฝํ•ด์ง„๋‹ค.

Alternative

์ •๊ทœํ™” table์€ query/constraint๊ฐ€ ๊ฐ•ํ•˜๊ณ , object storage๋Š” ํฐ blob์— ์ ํ•ฉํ•˜์ง€๋งŒ transactional query๊ฐ€ ์•ฝํ•˜๋‹ค.

Terms you must know
  • semi-structured = ๋ชจ์–‘์€ ์žˆ์ง€๋งŒ ์ž์ฃผ ๋ฐ”๋€Œ๋Š” ๋ฐ์ดํ„ฐ
  • normalized = ์ค‘๋ณต์„ ์ค„์ด๋ ค๊ณ  table๋กœ ๋‚˜๋ˆ„๋Š” ์„ค๊ณ„
  • index = ๊ฒ€์ƒ‰์„ ๋น ๋ฅด๊ฒŒ ํ•˜๋Š” DB ๊ตฌ์กฐ
Where

clip_analyses, videos transcript JSON columns

Beginner trap

JSONB๋ฅผ ์•„๋ฌด ๋ฐ์ดํ„ฐ๋‚˜ ๋„ฃ๋Š” ์“ฐ๋ ˆ๊ธฐํ†ต์œผ๋กœ ์“ฐ๋ฉด ์•ˆ ๋œ๋‹ค.

6. DataCore

HikariCP

DB connection pool์ด๋‹ค.

Connection pooling matters because slow AI calls must not hold database connections.

What it does

Spring Boot๊ฐ€ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ backend thread๊ฐ€ Postgres connection์„ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

Why this stack

๋งค ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ connection์„ ๋งŒ๋“ค๋ฉด ๋А๋ฆฌ๊ณ  ๋น„์‹ธ๋‹ค. pool์ด latency์™€ DB ๋ถ€ํ•˜๋ฅผ ์ค„์ธ๋‹ค.

Trade-off

pool ํฌ๊ธฐ๋ฅผ ํ‚ค์šด๋‹ค๊ณ  ๋ฌด์กฐ๊ฑด ์ฒ˜๋ฆฌ๋Ÿ‰์ด ๋Š˜์ง€ ์•Š๋Š”๋‹ค. DB max connection๊ณผ slow query๊ฐ€ ๋ณ‘๋ชฉ์ด๋‹ค.

Alternative

PgBouncer ๊ฐ™์€ external pooler๋ฅผ ๋‘˜ ์ˆ˜๋„ ์žˆ๊ณ , serverless DB๋Š” provider pool์„ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

Terms you must know
  • connection pool = DB ์—ฐ๊ฒฐ์„ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ์žฌ์‚ฌ์šฉํ•˜๋Š” ๋ฌถ์Œ
  • leak = ๋นŒ๋ฆฐ ์—ฐ๊ฒฐ์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ๋ฌธ์ œ
  • pool exhaustion = ๋ชจ๋“  connection์ด ์‚ฌ์šฉ ์ค‘์ด๋ผ ๋Œ€๊ธฐํ•˜๋Š” ์ƒํƒœ
Where

Spring Boot datasource defaults, backend runtime

Beginner trap

AI HTTP ํ˜ธ์ถœ ์ค‘ DB transaction์„ ์˜ค๋ž˜ ์žก์œผ๋ฉด pool์ด ๊ณ ๊ฐˆ๋  ์ˆ˜ ์žˆ๋‹ค.

7. AI

4
7. AICore

AI provider fallback

Gemini/OpenAI/Claude ์ค‘ ๊ฐ€๋Šฅํ•œ provider๋กœ ์ˆœ์ฐจ ์‹œ๋„ํ•˜๋Š” ๊ตฌ์กฐ๋‹ค.

The provider interface lets Mimi degrade across configured LLM providers instead of hard-failing on one vendor.

What it does

CompositeAiClient๊ฐ€ configured provider๋ฅผ order๋Œ€๋กœ ํ˜ธ์ถœํ•˜๊ณ  ์‹คํŒจํ•˜๋ฉด ๋‹ค์Œ provider๋กœ ๋„˜์–ด๊ฐ„๋‹ค.

Why this stack

LLM provider๋Š” quota, latency, outage๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ interface์™€ fallback์ด ์‹ค์ œ ์šด์˜์— ์ค‘์š”ํ•˜๋‹ค.

Trade-off

fallback์€ ๋น„์šฉ๊ณผ ๊ฒฐ๊ณผ ์ผ๊ด€์„ฑ์„ ํ”๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋ชจ๋ธ๋ณ„ output schema ์ฐจ์ด๋ฅผ ๊ฒ€์ฆํ•ด์•ผ ํ•œ๋‹ค.

Alternative

๋‹จ์ผ provider๋Š” ๋‹จ์ˆœํ•˜๊ณ , gateway ์„œ๋น„์Šค๋Š” routing/observability๋ฅผ ๋งก๊ธธ ์ˆ˜ ์žˆ์ง€๋งŒ vendor dependency๊ฐ€ ์ƒ๊ธด๋‹ค.

Terms you must know
  • provider = ๋ชจ๋ธ API๋ฅผ ์ œ๊ณตํ•˜๋Š” ํšŒ์‚ฌ/์„œ๋น„์Šค
  • adapter = ๊ณตํ†ต interface์— ๋งž๊ฒŒ ๊ฐ์‹ธ๋Š” ์ฝ”๋“œ
  • fallback = ์‹คํŒจ ์‹œ ๋Œ€์ฒด ๊ฒฝ๋กœ
Where

CompositeAiClient.java, Gemini/OpenAi/Claude clients

Beginner trap

fallback์ด ์žˆ์œผ๋ฉด ํ’ˆ์งˆ๊ณผ ๋น„์šฉ ๋ฌธ์ œ๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

7. AICore

yt-dlp

YouTube metadata/caption์„ ๊ฐ€์ ธ์˜ค๋Š” CLI ๋„๊ตฌ๋‹ค.

yt-dlp is a pragmatic ingestion tool, but the system must treat it as a fragile external dependency.

What it does

์„œ๋ฒ„์—์„œ video info์™€ transcript๋ฅผ ๋ฝ‘๋Š” ๊ฒฝ๋กœ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

Why this stack

YouTube API๊ฐ€ ์ฃผ์ง€ ์•Š๋Š” caption/metadata ์ ‘๊ทผ์— ํ˜„์‹ค์ ์œผ๋กœ ๊ฐ•ํ•˜๋‹ค.

Trade-off

YouTube ๋ณ€๊ฒฝ๊ณผ IP ์ฐจ๋‹จ์— ์ทจ์•ฝํ•˜๊ณ , CLI dependency๋ผ ์šด์˜ ํ™˜๊ฒฝ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Alternative

Official YouTube Data API๋Š” ์•ˆ์ •์ ์ด์ง€๋งŒ caption ์ ‘๊ทผ ์ œ์•ฝ์ด ์žˆ๊ณ , Supadata ๊ฐ™์€ API๋Š” ๋น„์šฉ๊ณผ vendor dependency๊ฐ€ ์žˆ๋‹ค.

Terms you must know
  • CLI = ํ„ฐ๋ฏธ๋„์—์„œ ์‹คํ–‰ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ
  • metadata = ์ œ๋ชฉ/๊ธธ์ด ๊ฐ™์€ ์„ค๋ช… ๋ฐ์ดํ„ฐ
  • caption = ์ž๋ง‰
Where

YoutubeTranscriptClient.java, Docker/runtime docs

Beginner trap

yt-dlp๊ฐ€ ํ•ญ์ƒ ํ•ฉ๋ฒ•์ /์•ˆ์ •์  API์ฒ˜๋Ÿผ ๋™์ž‘ํ•œ๋‹ค๊ณ  ๋งํ•˜์ง€ ๋ง๋ผ.

7. AICore

POToken sidecar

YouTube ์ ‘๊ทผ ์ œํ•œ์„ ์šฐํšŒ/๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•œ ๋ณ„๋„ helper service๋‹ค.

The POToken sidecar exists because YouTube ingestion can fail from server IPs, so ingestion needs multiple paths.

What it does

์„œ๋ฒ„ IP์—์„œ ๋ง‰ํžˆ๋Š” caption ์ ‘๊ทผ์„ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด token/provider sidecar๋ฅผ ๋‘”๋‹ค.

Why this stack

๋ฐ์ดํ„ฐ์„ผํ„ฐ IP ์ฐจ๋‹จ ๊ฐ™์€ ํ˜„์‹ค ์šด์˜ ๋ฌธ์ œ๋ฅผ ์ œํ’ˆ ๊ตฌ์กฐ๋กœ ๋‹ค๋ฃฌ ํ”์ ์ด๋‹ค.

Trade-off

sidecar๊ฐ€ ์ถ”๊ฐ€๋˜๋ฉด ๋ฐฐํฌ, ๋ชจ๋‹ˆํ„ฐ๋ง, ์žฅ์•  ์ง€์ ๋„ ๋Š˜์–ด๋‚œ๋‹ค.

Alternative

๋ชจ๋ฐ”์ผ WebView ์ˆ˜์ง‘, ์œ ๋ฃŒ transcript API, ์‚ฌ์šฉ์ž ์—…๋กœ๋“œ ์ž๋ง‰์ด ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • sidecar = ์ฃผ ์„œ๋น„์Šค ์˜†์—์„œ ๋ณด์กฐ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์ž‘์€ ์„œ๋น„์Šค
  • datacenter IP = ํด๋ผ์šฐ๋“œ ์„œ๋ฒ„ IP
  • token = ์ ‘๊ทผ์— ํ•„์š”ํ•œ ์ฆ๋ช…๊ฐ’
Where

infrastructure/ncp, docker-compose, YoutubeTranscriptClient

Beginner trap

์ด ๊ตฌ์กฐ๊ฐ€ YouTube ์ •์ฑ… ๋ฆฌ์Šคํฌ๋ฅผ ์—†์•ค๋‹ค๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

7. AICore

Supadata fallback

์ž๋ง‰์„ ๊ฐ€์ ธ์˜ค๋Š” ์™ธ๋ถ€ API fallback์ด๋‹ค.

Supadata is a resilience fallback for transcript ingestion, not the core product logic.

What it does

yt-dlp/POToken path๊ฐ€ ์‹คํŒจํ•  ๋•Œ transcript vendor๋ฅผ ํ†ตํ•ด ๋Œ€์ฒด ์ˆ˜์ง‘ํ•œ๋‹ค.

Why this stack

ํ•ต์‹ฌ import flow๊ฐ€ ํ•œ ๊ฒฝ๋กœ ์‹คํŒจ๋กœ ์™„์ „ํžˆ ์ฃฝ์ง€ ์•Š๊ฒŒ ๋งŒ๋“ ๋‹ค.

Trade-off

๋น„์šฉ, quota, vendor lock-in, ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ ์ฐจ์ด๊ฐ€ ์ƒ๊ธด๋‹ค.

Alternative

์ง์ ‘ crawler ์œ ์ง€, ๋ชจ๋ฐ”์ผ device fetch, ์‚ฌ์šฉ์ž ์ž๋ง‰ ์—…๋กœ๋“œ๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • fallback API = ์ฃผ ๊ฒฝ๋กœ ์‹คํŒจ ์‹œ ์“ฐ๋Š” ์™ธ๋ถ€ ์„œ๋น„์Šค
  • quota = ํ˜ธ์ถœ ๊ฐ€๋Šฅ ํ•œ๋„
  • vendor lock-in = ํŠน์ • ์—…์ฒด ์˜์กด๋„๊ฐ€ ๋†’์•„์ง€๋Š” ๋ฌธ์ œ
Where

YoutubeTranscriptClient.java config

Beginner trap

fallback์ด ์žˆ์œผ๋ฉด monitoring์ด ๋œ ํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

8. Infra

7
8. InfraCore

Docker

์•ฑ ์‹คํ–‰ ํ™˜๊ฒฝ์„ ์ด๋ฏธ์ง€๋กœ ํฌ์žฅํ•œ๋‹ค.

Docker makes the runtime reproducible enough to move between local, NCP, and cloud deployment paths.

What it does

backend, pot-provider, ds-forge deployment ๊ฐ™์€ runtime์„ ๋™์ผํ•œ ํŒŒ์ผ ์‹œ์Šคํ…œ/command๋กœ ์‹คํ–‰ํ•œ๋‹ค.

Why this stack

์„œ๋ฒ„๋งˆ๋‹ค ์„ค์น˜ ์ƒํƒœ๊ฐ€ ๋‹ฌ๋ผ๋„ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€๋กœ ์žฌํ˜„์„ฑ์„ ๋†’์ธ๋‹ค.

Trade-off

์ด๋ฏธ์ง€ ๋นŒ๋“œ/๋ณด์•ˆ ํŒจ์น˜/volume/network ์ดํ•ด๊ฐ€ ํ•„์š”ํ•˜๊ณ , local๊ณผ prod๊ฐ€ ์™„์ „ํžˆ ๊ฐ™์ง€๋Š” ์•Š๋‹ค.

Alternative

VM ์ง์ ‘ ๋ฐฐํฌ๋Š” ๋‹จ์ˆœํ•˜์ง€๋งŒ drift๊ฐ€ ํฌ๊ณ , serverless๋Š” ์šด์˜ ๋ถ€๋‹ด์€ ๋‚ฎ์ง€๋งŒ runtime ์ œ์•ฝ์ด ์žˆ๋‹ค.

Terms you must know
  • image = ์‹คํ–‰ ํŒŒ์ผ๊ณผ dependency๋ฅผ ๋ฌถ์€ template
  • container = image๋ฅผ ์‹คํ–‰ํ•œ ํ”„๋กœ์„ธ์Šค ๊ฒฉ๋ฆฌ ๋‹จ์œ„
  • volume = container ๋ฐ–์— ๋ณด์กด๋˜๋Š” ์ €์žฅ์†Œ
Where

Dockerfile, docker-compose.yml, infrastructure

Beginner trap

Docker๊ฐ€ security๋‚˜ scaling์„ ์ž๋™ ํ•ด๊ฒฐํ•œ๋‹ค๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

8. InfraCore

Docker Compose

์—ฌ๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ•œ ํŒŒ์ผ๋กœ ๊ฐ™์ด ์‹คํ–‰ํ•œ๋‹ค.

Compose is a pragmatic single-box orchestration layer for early deployment and debugging.

What it does

backend, Postgres, Caddy, pot-provider ๊ฐ™์€ ์„œ๋น„์Šค๋ฅผ local/NCP ๋‹จ์ผ ๋ฐ•์Šค์—์„œ ๋ฌถ๋Š”๋‹ค.

Why this stack

์ดˆ๊ธฐ ์šด์˜์—์„œ ์ดํ•ด์™€ ๋ณต๊ตฌ๊ฐ€ ์‰ฝ๊ณ  ๋น„์šฉ์ด ๋‚ฎ๋‹ค.

Trade-off

๋‹จ์ผ ์„œ๋ฒ„ HA๊ฐ€ ์•ฝํ•˜๊ณ  rolling deploy/auto scale์€ ์ง์ ‘ ์ฑ™๊ฒจ์•ผ ํ•œ๋‹ค.

Alternative

Kubernetes๋Š” orchestration์ด ๊ฐ•ํ•˜์ง€๋งŒ ๋ณต์žกํ•˜๊ณ , ECS๋Š” managed container ๋ฐฐํฌ์— ์ข‹๋‹ค.

Terms you must know
  • service = compose ์•ˆ์˜ ์ปจํ…Œ์ด๋„ˆ ๋‹จ์œ„
  • network = ์ปจํ…Œ์ด๋„ˆ๋ผ๋ฆฌ ํ†ต์‹ ํ•˜๋Š” ๊ฐ€์ƒ๋ง
  • single box = ํ•œ VM ์•ˆ์— ์—ฌ๋Ÿฌ ์„œ๋น„์Šค๊ฐ€ ์žˆ๋Š” ๊ตฌ์กฐ
Where

docker-compose.yml, infrastructure/ncp

Beginner trap

Compose๋ฅผ ์“ด๋‹ค๊ณ  production-grade HA๊ฐ€ ๋˜๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค.

8. InfraCore

Caddy

reverse proxy์™€ HTTPS termination์„ ๋‹ด๋‹นํ•œ๋‹ค.

Caddy keeps the NCP deployment simple by handling HTTPS and routing in front of the containers.

What it does

๋„๋ฉ”์ธ ์š”์ฒญ์„ container๋กœ ๋ณด๋‚ด๊ณ  TLS ์ธ์ฆ์„œ๋ฅผ ์ž๋™ ์ฒ˜๋ฆฌํ•œ๋‹ค.

Why this stack

NCP ๋‹จ์ผ ์„œ๋ฒ„์—์„œ nginx๋ณด๋‹ค ์„ค์ •์ด ๋‹จ์ˆœํ•˜๊ณ  Let's Encrypt ์ž๋™ํ™”๊ฐ€ ํŽธํ•˜๋‹ค.

Trade-off

๋ณต์žกํ•œ enterprise routing์ด๋‚˜ custom module ecosystem์€ nginx/Envoy๊ฐ€ ๋” ๊ฐ•ํ•  ์ˆ˜ ์žˆ๋‹ค.

Alternative

nginx๋Š” ๋„๋ฆฌ ์“ฐ์ด๊ณ , Traefik์€ container discovery๊ฐ€ ๊ฐ•ํ•˜๊ณ , AWS ALB๋Š” managed L7 proxy๋‹ค.

Terms you must know
  • reverse proxy = ์•ž์—์„œ ์š”์ฒญ์„ ๋ฐ›์•„ ๋’ค ์„œ๋น„์Šค๋กœ ๋„˜๊ธฐ๋Š” ์„œ๋ฒ„
  • TLS termination = HTTPS ์•”ํ˜ธํ™”๋ฅผ proxy์—์„œ ๋๋‚ด๋Š” ๊ฒƒ
  • Caddyfile = Caddy ์„ค์ • ํŒŒ์ผ
Where

infrastructure/ncp, deploy-ncp.sh

Beginner trap

Caddy๊ฐ€ ์•ฑ ์ธ์ฆ์„ ๋Œ€์‹ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

8. InfraCore

NCP single VM

ํ˜„์žฌ ๋น„์šฉ/์ง€์—ฐ์‹œ๊ฐ„ ์ค‘์‹ฌ์˜ ๋‹จ์ผ ์„œ๋ฒ„ ์šด์˜ ๊ฒฝ๋กœ๋‹ค.

The NCP single-box deployment is a cost and latency trade-off, not a high-availability architecture.

What it does

Caddy, backend, Postgres, sidecar๋ฅผ ํ•œ VM/Docker network์— ์˜ฌ๋ฆฌ๋Š” ๋ฐฉ์‹์ด๋‹ค.

Why this stack

์ดˆ๊ธฐ ์ œํ’ˆ์—์„œ ๋น„์šฉ๊ณผ ํ•œ๊ตญ latency๋ฅผ ์ค„์ด๊ณ  ์šด์˜์„ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•˜๊ธฐ ์ข‹๋‹ค.

Trade-off

HA๊ฐ€ ์•ฝํ•˜๋‹ค. VM ์žฅ์• , disk ์žฅ์• , backup/restore, deploy rollback์„ ์ง์ ‘ ์ฑ…์ž„์ ธ์•ผ ํ•œ๋‹ค.

Alternative

AWS ECS/RDS์ฒ˜๋Ÿผ managed service๋ฅผ ์“ฐ๋ฉด HA/backup์€ ์ข‹์•„์ง€์ง€๋งŒ ๋น„์šฉ๊ณผ ๋ณต์žก๋„๊ฐ€ ๋Š˜์–ด๋‚œ๋‹ค.

Terms you must know
  • VM = ๊ฐ€์ƒ ์„œ๋ฒ„
  • HA = ์žฅ์• ๊ฐ€ ๋‚˜๋„ ์„œ๋น„์Šค๊ฐ€ ๊ณ„์† ๋˜๋Š” ๊ตฌ์กฐ
  • backup/restore = ๋ฐ์ดํ„ฐ ๋ณต๊ตฌ ๊ณ„ํš
Where

infrastructure/ncp, deploy scripts

Beginner trap

๋‹จ์ผ ๋ฐ•์Šค๋ฅผ production์ด๋ผ๊ณ  ๋งํ•  ์ˆ˜๋Š” ์žˆ์–ด๋„ HA๋ผ๊ณ  ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

8. InfraCore

AWS ECS / RDS / S3 path

๋” managedํ•œ AWS ๋ฐฐํฌ ์„ค๊ณ„ ๊ฒฝ๋กœ๋‹ค.

The AWS path separates compute, database, storage, and secrets for a more managed production architecture.

What it does

ECS๋Š” backend container, RDS๋Š” Postgres, S3๋Š” recording object storage, Secrets Manager๋Š” secret์„ ๋งก๋Š”๋‹ค.

Why this stack

์šด์˜ ์•ˆ์ •์„ฑ, backup, IAM, managed DB๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ™•์žฅ ๊ฒฝ๋กœ๋‹ค.

Trade-off

๋น„์šฉ๊ณผ Terraform/IAM/๋„คํŠธ์›Œํฌ ๋ณต์žก๋„๊ฐ€ ์ปค์ง„๋‹ค.

Alternative

NCP single box๋Š” ๋‹จ์ˆœ/์ €๋ ดํ•˜๊ณ , Fly.io/Render/Railway๋Š” ๋” ์‰ฌ์šด PaaS ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • ECS = AWS container orchestration
  • RDS = managed relational DB
  • S3 = object storage
  • IAM = AWS ๊ถŒํ•œ ์‹œ์Šคํ…œ
Where

infrastructure/aws, INFRA_DEEP_DIVE.md

Beginner trap

AWS๋ฅผ ๋ฌธ์„œํ™”ํ–ˆ๋‹ค๊ณ  ์ง€๊ธˆ ๋ชจ๋“  ์šด์˜์ด AWS์—์„œ ๋„๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋งํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

8. InfraCore

Vercel web deployment

Next.js ์›น ๋ฐฐํฌ ํ”Œ๋žซํผ์ด๋‹ค.

Vercel is a good fit for the web surface, while stateful backend concerns stay in the backend infrastructure.

What it does

frontend๋ฅผ build/deployํ•˜๊ณ  CDN/preview deployment์™€ ์—ฐ๊ฒฐํ•œ๋‹ค.

Why this stack

Next.js ์•ฑ์„ ๋น ๋ฅด๊ฒŒ ๋ฐฐํฌํ•˜๊ณ  preview URL๋กœ ๊ฒ€์ฆํ•˜๊ธฐ ์ข‹๋‹ค.

Trade-off

backend/stateful workload์—๋Š” ๋งž์ง€ ์•Š๊ณ  vendor platform behavior์— ์˜์กดํ•œ๋‹ค.

Alternative

Netlify, Cloudflare Pages, self-hosted Next, Docker deployment๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • CDN = ์ •์  ์ž์‚ฐ์„ ๊ฐ€๊นŒ์šด edge์—์„œ ์ฃผ๋Š” ๋„คํŠธ์›Œํฌ
  • preview deployment = PR/branch๋ณ„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ฐฐํฌ
  • serverless = ์š”์ฒญ๋งˆ๋‹ค ๊ด€๋ฆฌํ˜• ์‹คํ–‰ ํ™˜๊ฒฝ์—์„œ ๋„๋Š” ๋ฐฉ์‹
Where

frontend deployment, Vercel project

Beginner trap

Vercel์— ๋ฐฐํฌ๋๋‹ค๊ณ  backend๋„ Vercel์— ์žˆ๋‹ค๋Š” ๋œป์€ ์•„๋‹ˆ๋‹ค.

8. InfraCore

S3-compatible recording storage

๋…น์Œ ํŒŒ์ผ bytes๋ฅผ ์ €์žฅํ•˜๋Š” object storage ๊ณ„์ธต์ด๋‹ค.

The storage seam keeps recording bytes out of Postgres and allows local or S3-compatible storage behind the same interface.

What it does

DB์—๋Š” metadata๋งŒ ์ €์žฅํ•˜๊ณ  ์‹ค์ œ audio bytes๋Š” Local/S3 ๊ตฌํ˜„์ฒด๊ฐ€ ๋ณด๊ด€ํ•œ๋‹ค.

Why this stack

ํŒŒ์ผ์„ DB์— ์ง์ ‘ ๋„ฃ์ง€ ์•Š์•„ DB ๋ถ€ํ•˜๋ฅผ ์ค„์ด๊ณ  storage backend๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค.

Trade-off

object lifecycle, access control, signed URL/streaming, delete policy๋ฅผ ์„ค๊ณ„ํ•ด์•ผ ํ•œ๋‹ค.

Alternative

local disk๋Š” ๋‹จ์ˆœํ•˜์ง€๋งŒ ์„œ๋ฒ„ ์žฅ์• ์— ์•ฝํ•˜๊ณ , DB blob์€ transaction์€ ์‰ฝ์ง€๋งŒ DB๊ฐ€ ๋ฌด๊ฑฐ์›Œ์ง„๋‹ค.

Terms you must know
  • object storage = ํŒŒ์ผ ๊ฐ™์€ ํฐ ๊ฐ์ฒด๋ฅผ key๋กœ ์ €์žฅํ•˜๋Š” ์„œ๋น„์Šค
  • metadata = ํŒŒ์ผ ์„ค๋ช… ์ •๋ณด
  • streaming = ํŒŒ์ผ์„ ์กฐ๊ธˆ์”ฉ ์ฝ์–ด ๋ณด๋‚ด๋Š” ๋ฐฉ์‹
Where

RecordingStorage, S3RecordingStorage, LocalRecordingStorage

Beginner trap

S3๋ฅผ ์“ฐ๋ฉด privacy๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋˜๋Š” ๊ฑด ์•„๋‹ˆ๋‹ค.

9. Security

5
9. SecurityCore

Spring Security

์ธ์ฆ๊ณผ ๊ถŒํ•œ ๊ฒ€์‚ฌ๋ฅผ ๋‹ด๋‹นํ•˜๋Š” ํ•„ํ„ฐ ์ฒด์ธ์ด๋‹ค.

Spring Security is the backend gate that authenticates requests and enforces protected API access.

What it does

JWT filter, password hashing, CORS, protected routes๋ฅผ ๊ฐ•์ œํ•œ๋‹ค.

Why this stack

์‚ฌ์šฉ์ž๋ณ„ clips/recordings/reviews๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ backend์—์„œ identity์™€ authorization์„ ์•ˆ์ •์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

Trade-off

์„ค์ •์ด ๋ณต์žกํ•˜๊ณ  filter order๋ฅผ ๋ชจ๋ฅด๋ฉด ๋””๋ฒ„๊น…์ด ์–ด๋ ต๋‹ค.

Alternative

NextAuth ๊ฐ™์€ web ์ค‘์‹ฌ auth, Supabase Auth ๊ฐ™์€ managed auth, custom middleware ๋ฐฉ์‹์ด ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • authentication = ๋ˆ„๊ตฌ์ธ์ง€ ํ™•์ธ
  • authorization = ๋ฌด์—‡์„ ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธ
  • filter chain = request๊ฐ€ ์—ฌ๋Ÿฌ ๊ฒ€๋ฌธ์†Œ๋ฅผ ํ†ต๊ณผํ•˜๋Š” ๊ตฌ์กฐ
Where

SecurityConfig, JwtAuthenticationFilter

Beginner trap

๋กœ๊ทธ์ธ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด authorization์ด ๋๋‚œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

9. SecurityCore

JWT

๋กœ๊ทธ์ธ ์‚ฌ์šฉ์ž๋ฅผ ์ฆ๋ช…ํ•˜๋Š” ์„œ๋ช…๋œ ํ† ํฐ์ด๋‹ค.

JWT gives web and mobile a common stateless authentication mechanism, with token_version for revocation.

What it does

client๊ฐ€ Authorization header๋กœ ๋ณด๋‚ด๊ณ  backend๊ฐ€ signature์™€ claims๋ฅผ ๊ฒ€์ฆํ•œ๋‹ค.

Why this stack

๋ชจ๋ฐ”์ผ/์›น์ด ๊ฐ™์€ API๋ฅผ ์“ฐ๊ธฐ ์‰ฝ๊ณ , ์„œ๋ฒ„๊ฐ€ session table์„ ๋งค ์š”์ฒญ ์กฐํšŒํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

Trade-off

ํƒˆ์ทจ๋˜๋ฉด ๋งŒ๋ฃŒ ์ „๊นŒ์ง€ ์œ„ํ—˜ํ•˜๋‹ค. token_version, short expiry, secure storage๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Alternative

server-side session์€ ์ฆ‰์‹œ revoke๊ฐ€ ์‰ฝ๊ณ , opaque token์€ ์ •๋ณด ๋…ธ์ถœ์ด ์ ์ง€๋งŒ ์กฐํšŒ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Terms you must know
  • claim = ํ† ํฐ ์•ˆ์˜ ์ •๋ณด ์กฐ๊ฐ
  • signature = ์œ„๋ณ€์กฐ๋ฅผ ๋ง‰๋Š” ์„œ๋ช…
  • stateless = ์„œ๋ฒ„๊ฐ€ ์„ธ์…˜ ์ƒํƒœ๋ฅผ ๋งŽ์ด ๋“ค๊ณ  ์žˆ์ง€ ์•Š๋Š” ๋ฐฉ์‹
Where

JwtTokenProvider, JwtAuthenticationFilter

Beginner trap

JWT payload๋Š” ์•”ํ˜ธํ™”๊ฐ€ ์•„๋‹ˆ๋ผ base64url ์ธ์ฝ”๋”ฉ์ผ ๋ฟ์ด๋‹ค.

9. SecurityCore

BCrypt

๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ hashํ•˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.

BCrypt protects stored passwords by hashing them with salt and a configurable work factor.

What it does

์›๋ฌธ password๋ฅผ ์ €์žฅํ•˜์ง€ ์•Š๊ณ  salt์™€ cost๊ฐ€ ์žˆ๋Š” hash๋ฅผ ์ €์žฅํ•œ๋‹ค.

Why this stack

DB๊ฐ€ ์œ ์ถœ๋ผ๋„ ์›๋ฌธ password๋ฅผ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ ๋‹ค.

Trade-off

cost๊ฐ€ ๋„ˆ๋ฌด ๋‚ฎ์œผ๋ฉด ์•ฝํ•˜๊ณ  ๋„ˆ๋ฌด ๋†’์œผ๋ฉด login latency์™€ CPU ๋ถ€ํ•˜๊ฐ€ ์ปค์ง„๋‹ค.

Alternative

Argon2๋Š” memory-hard๋ผ ๋” ํ˜„๋Œ€์ ์ด๊ณ , PBKDF2๋Š” ํ‘œ์ค€์„ฑ์ด ๊ฐ•ํ•˜๋‹ค.

Terms you must know
  • hash = ์›๋ฌธ์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ธฐ ์–ด๋ ค์šด ๋ณ€ํ™˜
  • salt = ๊ฐ™์€ password๋„ ๋‹ค๋ฅธ hash๊ฐ€ ๋˜๊ฒŒ ํ•˜๋Š” ๋žœ๋ค๊ฐ’
  • cost = ๊ณ„์‚ฐ์„ ์ผ๋ถ€๋Ÿฌ ๋А๋ฆฌ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฐ•๋„
Where

AuthService, User passwordHash

Beginner trap

password๋ฅผ ์•”ํ˜ธํ™”ํ•ด์„œ ๋ณตํ˜ธํ™”ํ•œ๋‹ค๊ณ  ๋งํ•˜๋ฉด ํ‹€๋ฆฐ ํ‘œํ˜„์ด๋‹ค.

9. SecurityCore

token_version revocation

์ด๋ฏธ ๋ฐœ๊ธ‰๋œ JWT๋ฅผ ๋ฌดํšจํ™”ํ•˜๋Š” ๋ฒ„์ „ ๊ฐ’์ด๋‹ค.

The token_version claim lets the backend reject old JWTs after password or account security changes.

What it does

์‚ฌ์šฉ์ž row์˜ token_version๊ณผ JWT claim์ด ๋‹ค๋ฅด๋ฉด ์š”์ฒญ์„ ๊ฑฐ์ ˆํ•œ๋‹ค.

Why this stack

JWT๋Š” stateless๋ผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฆ‰์‹œ revoke๊ฐ€ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— DB check๋ฅผ ์„ž์€ ํ˜„์‹ค์  ์žฅ์น˜๋‹ค.

Trade-off

๋งค ์š”์ฒญ DB ์กฐํšŒ ๋น„์šฉ์ด ์žˆ๋‹ค. cache๋‚˜ short-lived access token๊ณผ ์กฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Alternative

refresh token rotation, session table, blacklist cache๊ฐ€ ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • revocation = ๊ถŒํ•œ์„ ์ทจ์†Œํ•˜๋Š” ๊ฒƒ
  • claim mismatch = ํ† ํฐ ๋‚ด์šฉ๊ณผ ์„œ๋ฒ„ ์ƒํƒœ๊ฐ€ ์•ˆ ๋งž์Œ
  • blacklist = ํ๊ธฐ ํ† ํฐ ๋ชฉ๋ก
Where

users.token_version, JwtAuthenticationFilter

Beginner trap

JWT๋งŒ ์“ฐ๋ฉด logout/revoke ๋ฌธ์ œ๊ฐ€ ์ž๋™์œผ๋กœ ํ•ด๊ฒฐ๋œ๋‹ค๊ณ  ๋งํ•˜์ง€ ๋ง๋ผ.

9. SecurityCore

Rate limiting

์š”์ฒญ ํšŸ์ˆ˜๋ฅผ ์ œํ•œํ•˜๋Š” ๋ณดํ˜ธ ์žฅ์น˜๋‹ค.

Rate limiting protects costly AI paths and sensitive auth endpoints from abuse and accidental loops.

What it does

auth ์‹œ๋„๋‚˜ AI composition ๊ฐ™์€ ๋น„์‹ผ/์œ„ํ—˜ํ•œ API๋ฅผ ์‚ฌ์šฉ์ž๋ณ„๋กœ ์ œํ•œํ•œ๋‹ค.

Why this stack

๋น„์šฉ ํญ์ฃผ, brute force, accidental spam์„ ๋ง‰๋Š” ๊ธฐ๋ณธ ์šด์˜ ์žฅ์น˜๋‹ค.

Trade-off

in-memory limit๋Š” ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ ๋Œ€๋ฉด ์ผ๊ด€์„ฑ์ด ๊นจ์ง„๋‹ค.

Alternative

Redis ๊ธฐ๋ฐ˜ rate limiter, API gateway/WAF, provider quota๊ฐ€ ๋Œ€์•ˆ/๋ณด์™„์ฑ…์ด๋‹ค.

Terms you must know
  • rate limit = ์ผ์ • ์‹œ๊ฐ„์— ํ—ˆ์šฉํ•˜๋Š” ์š”์ฒญ ์ˆ˜
  • brute force = ๋น„๋ฐ€๋ฒˆํ˜ธ ๋“ฑ์„ ๋ฐ˜๋ณต ์‹œ๋„ํ•˜๋Š” ๊ณต๊ฒฉ
  • quota = ์‚ฌ์šฉ ํ•œ๋„
Where

Auth rate limit, ComposeRateLimitInterceptor

Beginner trap

rate limit๋งŒ์œผ๋กœ abuse ๋ฐฉ์–ด๊ฐ€ ๋๋‚œ๋‹ค๊ณ  ๋งํ•˜์ง€ ๋ง๋ผ.

11. Testing

1
11. TestingCore

JUnit, Testcontainers, Vitest, Playwright

backend/frontend/e2e ๊ฒ€์ฆ ๋„๊ตฌ๋“ค์ด๋‹ค.

The test stack is layered: unit tests for logic, integration tests for database behavior, and e2e tests for user flows.

What it does

Spring service/API๋Š” JUnit, DB integration์€ Testcontainers, UI/shared logic์€ Vitest, browser flow๋Š” Playwright๋กœ ๊ฒ€์ฆํ•œ๋‹ค.

Why this stack

๊ณ„์ธต๋ณ„ ์œ„ํ—˜์ด ๋‹ฌ๋ผ์„œ ํ…Œ์ŠคํŠธ ๋„๊ตฌ๋„ ๋‹ค๋ฅด๊ฒŒ ์“ด๋‹ค.

Trade-off

ํ…Œ์ŠคํŠธ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ๋А๋ ค์ง€๊ณ  flaky test ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Alternative

์ˆ˜๋™ QA๋Š” ๋น ๋ฅธ ํ™•์ธ์—๋Š” ์ข‹์ง€๋งŒ ํšŒ๊ท€ ๋ฐฉ์ง€๊ฐ€ ์•ฝํ•˜๊ณ , Cypress๋Š” e2e ๋Œ€์•ˆ์ด๋‹ค.

Terms you must know
  • unit test = ์ž‘์€ ํ•จ์ˆ˜/์„œ๋น„์Šค ํ…Œ์ŠคํŠธ
  • integration test = DB/API ๋“ฑ ์‹ค์ œ ์˜์กด์„ฑ ํฌํ•จ ํ…Œ์ŠคํŠธ
  • e2e = ์‚ฌ์šฉ์ž ํ๋ฆ„ ์ „์ฒด ํ…Œ์ŠคํŠธ
Where

backend tests, frontend tests, e2e

Beginner trap

ํ…Œ์ŠคํŠธ ๋„๊ตฌ ์ด๋ฆ„๋งŒ ๋‚˜์—ดํ•˜์ง€ ๋ง๊ณ  ์–ด๋–ค ์œ„ํ—˜์„ ์žก๋Š”์ง€ ๋งํ•ด์•ผ ํ•œ๋‹ค.

12. Ops

2
12. OpsCore

Actuator + Micrometer + Prometheus

์ƒํƒœ์™€ metric์„ ๋ณด๋Š” ์šด์˜ ๊ณ„์ธต์ด๋‹ค.

Metrics make AI latency and provider failure observable instead of relying only on logs.

What it does

health endpoint์™€ AI analysis latency/failure metric์„ ๋…ธ์ถœํ•œ๋‹ค.

Why this stack

๋ฉด์ ‘์—์„œ๋Š” ๊ธฐ๋Šฅ๋ณด๋‹ค ์žฅ์•  ์‹œ ์›์ธ์„ ์ฐพ๋Š” ๋Šฅ๋ ฅ์ด ์ค‘์š”ํ•ด์„œ observability๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Trade-off

metric๋งŒ ์žˆ๊ณ  alert/dashboard๊ฐ€ ์—†์œผ๋ฉด ๋ฐ˜์ชฝ์ด๋‹ค.

Alternative

OpenTelemetry, Datadog, Grafana Cloud, CloudWatch๊ฐ€ ๋Œ€์•ˆ/ํ™•์žฅ์ด๋‹ค.

Terms you must know
  • metric = ์ˆซ์ž๋กœ ๋‚จ๊ธฐ๋Š” ์ƒํƒœ
  • p95/p99 = ๋А๋ฆฐ ์š”์ฒญ ์ƒ์œ„ percentile latency
  • alert = ์กฐ๊ฑด์ด ๋‚˜๋น ์ง€๋ฉด ์•Œ๋ฆฌ๋Š” ๊ทœ์น™
Where

spring-boot-starter-actuator, micrometer-registry-prometheus

Beginner trap

Prometheus endpoint๊ฐ€ ์žˆ๋‹ค๊ณ  ์šด์˜ ์ฒด๊ณ„๊ฐ€ ์™„์„ฑ๋œ ๊ฑด ์•„๋‹ˆ๋‹ค.

12. OpsCore

Structured JSON logging

๋กœ๊ทธ๋ฅผ ๊ธฐ๊ณ„๊ฐ€ ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ๋‚จ๊ธฐ๋Š” ๋ฐฉ์‹์ด๋‹ค.

Structured logs help tie user actions, requests, and failures together during incident analysis.

What it does

request id, user id, error, latency ๊ฐ™์€ ํ•„๋“œ๋ฅผ JSON์œผ๋กœ ๊ธฐ๋กํ•œ๋‹ค.

Why this stack

์žฅ์•  ๋ถ„์„๊ณผ correlation์ด ์‰ฌ์›Œ์ง€๊ณ  container/cloud logging๊ณผ ์ž˜ ๋งž๋Š”๋‹ค.

Trade-off

PII/secret์ด ๋กœ๊ทธ์— ์ƒˆ๋ฉด ์œ„ํ—˜ํ•˜๋‹ค. redaction์ด ํ•„์š”ํ•˜๋‹ค.

Alternative

plain text log๋Š” ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฝ๊ณ , OpenTelemetry trace๋Š” request ํ๋ฆ„ ๋ถ„์„์— ๋” ๊ฐ•ํ•˜๋‹ค.

Terms you must know
  • structured log = key/value ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ๋กœ๊ทธ
  • correlation id = ์—ฌ๋Ÿฌ ๋กœ๊ทธ๋ฅผ ํ•œ ์š”์ฒญ์œผ๋กœ ๋ฌถ๋Š” id
  • redaction = ๋ฏผ๊ฐ๊ฐ’ ์ œ๊ฑฐ
Where

logstash-logback-encoder, RequestLoggingFilter

Beginner trap

๋กœ๊ทธ๋ฅผ ๋งŽ์ด ๋‚จ๊ธฐ๋Š” ๊ฒƒ๊ณผ ์•ˆ์ „ํ•˜๊ฒŒ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์€ ๋‹ค๋ฅด๋‹ค.

Start from zero path

์ด ์ˆœ์„œ๋Œ€๋กœ ๋งํ•˜๋ฉด ์ œํ’ˆ ๋ชฉ์ ์—์„œ infra ํ•œ๊ณ„๊นŒ์ง€ ๋Š๊ธฐ์ง€ ์•Š๋Š”๋‹ค.

12
steps
1

Product loop

Mimi๋ฅผ ๊ธฐ๋Šฅ ๋‚˜์—ด์ด ์•„๋‹ˆ๋ผ loop๋กœ ์„ค๋ช…ํ•œ๋‹ค.

์œ ํŠœ๋ธŒ ์ž…๋ ฅ์„ ํด๋ฆฝ์œผ๋กœ ์ž๋ฅด๊ณ  AI ์„ค๋ช…๊ณผ SRS/๋…น์Œ์œผ๋กœ output practice๊นŒ์ง€ ์—ฐ๊ฒฐํ•˜๋Š” ์•ฑ์ด๋‹ค.

Mimi turns YouTube input into repeatable English output practice: clip, analyze, record, review, repeat.

Mimi productFriction removalFull system

Code focus: README.md product walkthrough

2

Repo map

frontend/backend/mobile/packages/infra์˜ ์—ญํ• ์„ ํ•œ ๋ฒˆ์— ๋งํ•œ๋‹ค.

์›น์€ Next, ๋ชจ๋ฐ”์ผ์€ Expo, ๋ฐฑ์—”๋“œ๋Š” Spring, ๊ณตํ†ต ํ•™์Šต ๋กœ์ง์€ packages/core, ๋ฐฐํฌ๋Š” infrastructure์— ์žˆ๋‹ค.

The repo is split into web, mobile, backend, shared core, and infrastructure.

Next.jsExpo RouterSpring BootShared TypeScript core

Code focus: root package.json + folder tree

3

Language choices

์™œ Java/TypeScript/SQL/Docker์ธ์ง€ ๋งํ•œ๋‹ค.

์„œ๋ฒ„ ํŠธ๋žœ์žญ์…˜/๋ณด์•ˆ์€ Java/Spring, ํด๋ผ์ด์–ธํŠธ์™€ shared content๋Š” TypeScript, schema๋Š” SQL migration, ์‹คํ–‰ ์žฌํ˜„์„ฑ์€ Docker๋‹ค.

Java owns the transactional backend, TypeScript owns client and shared learning logic, SQL owns schema, and Docker owns runtime packaging.

Java 21TypeScriptSQL + FlywayDocker

Code focus: backend/build.gradle.kts, frontend/package.json, mobile/package.json

4

Backend domains

๋„๋ฉ”์ธ ํŒจํ‚ค์ง€ 11๊ฐœ๋ฅผ ์ฑ…์ž„ ์ค‘์‹ฌ์œผ๋กœ ์„ค๋ช…ํ•œ๋‹ค.

ํ•˜๋‚˜์˜ Spring ์•ฑ์ด์ง€๋งŒ auth/video/clip/analysis/recording/review/practice/deck/library/billing/common์œผ๋กœ ๊ธฐ๋Šฅ ๊ฒฝ๊ณ„๋ฅผ ๋‚˜๋ˆด๋‹ค.

It is a modular monolith: one backend deployment, split internally by feature domains.

Modular monolithFeature-sliced packagesAuth domainClip domainAnalysis domain

Code focus: backend/src/main/java/com/tubeshadow/*

5

Data model

users/videos/clips/analyses/review/practice/library ๊ด€๊ณ„๋ฅผ ๊ทธ๋ฆฐ๋‹ค.

videos๋Š” ์ „์—ญ ์บ์‹œ๊ณ , user๋ณ„ ์†Œ์œ ๋Š” clips/library_videos/review/practice rows๋กœ ํ‘œํ˜„ํ•œ๋‹ค.

The database combines relational ownership with JSONB for transcripts and AI results.

PostgreSQLGlobal video cachePostgres JSONBFlyway migrations

Code focus: V1-V20 migration files

6

Video import

YouTube import๊ฐ€ ์™œ ๋ณต์žกํ•œ์ง€ ์„ค๋ช…ํ•œ๋‹ค.

์„œ๋ฒ„๋Š” datacenter IP์™€ token ๋ฌธ์ œ๋ฅผ ๋งŒ๋‚˜๋ฏ€๋กœ yt-dlp/POToken/Supadata๋ฅผ ์“ฐ๊ณ , ๋ชจ๋ฐ”์ผ์€ ํฐ IP๋กœ ์ง์ ‘ caption์„ ๊ฐ€์ ธ์˜จ๋‹ค.

Transcript import has both a server path and a device path because YouTube blocks or changes server-side caption access.

yt-dlp transcript pathPOToken sidecarDevice WebView transcriptRace-safe import

Code focus: VideoImportService + YoutubeTranscriptClient + mobile WebView

7

Clip to AI

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๋น„๋™๊ธฐ AI ํŒŒ์ดํ”„๋ผ์ธ์„ ํ™”์ดํŠธ๋ณด๋“œ๋กœ ์„ค๋ช…ํ•œ๋‹ค.

ํด๋ฆฝ ์ €์žฅ์€ ๋จผ์ € ์ปค๋ฐ‹ํ•˜๊ณ , AI ํ˜ธ์ถœ์€ ํŠธ๋žœ์žญ์…˜ ๋ฐ– @Async ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด DB ์ปค๋„ฅ์…˜์„ ๋ฌถ์ง€ ์•Š๋Š”๋‹ค.

I commit the clip first, then run the slow AI call after commit, on a background thread, outside the transaction.

Async AI pipelineConnection pool protectionPENDING READY FAILEDSpring self proxy

Code focus: ClipService + ClipAnalysisService

8

AI provider fallback

vendor independence์™€ cost control์„ ๋งํ•œ๋‹ค.

ํ˜ธ์ถœ๋ถ€๋Š” interface๋งŒ ์•Œ๊ณ , Composite๊ฐ€ Gemini/OpenAI/Claude๋ฅผ ์„ค์ • ์ˆœ์„œ๋Œ€๋กœ ์‹œ๋„ํ•œ๋‹ค.

The backend depends on one AI interface; the composite implementation handles provider order and fallback.

AiAnalysisClient interfaceCompositeAiClientAI JSON parsingCost control

Code focus: CompositeAiClient + provider clients

9

Learning engines

SM-2์™€ Leitner๋ฅผ ์™œ ๋‚˜๋ˆด๋Š”์ง€ ๋งํ•œ๋‹ค.

ํด๋ฆฝ ๋ฆฌ๋ทฐ๋Š” 0-5 ํ’ˆ์งˆ ์‹ ํ˜ธ๋ผ SM-2, ๋ฐ์ผ๋ฆฌ ๋“œ๋ฆด์€ binary๋ผ Leitner๋ฅผ ์“ด๋‹ค.

Clip review uses SM-2, while binary daily drills use Leitner boxes.

Two SRS enginesSM-2Practice drills Leitner

Code focus: Sm2Calculator + PracticeCard + practice-srs.ts

10

Security

JWT, ownership, rate limit, private recordings๋ฅผ ๋ฌถ์–ด ์„ค๋ช…ํ•œ๋‹ค.

JWT๋Š” token_version์œผ๋กœ revokeํ•˜๊ณ , ๋ชจ๋“  user resource๋Š” userId๋กœ ๊ฑธ๋Ÿฌ ์ฝ๊ณ , ๋…น์Œ์€ public URL ์—†์ด ๋ฐฑ์—”๋“œ๊ฐ€ streamํ•œ๋‹ค.

Security is built around JWT with token-version revocation, user-scoped queries, rate limits, and private recording streams.

JWT token versionOwnership queriesAI per-user rate limitPrivate recording stream

Code focus: security + recording packages

11

Frontend and mobile

์›น๊ณผ ๋ชจ๋ฐ”์ผ์ด ๊ฐ™์€ loop๋ฅผ ๋‹ค๋ฅธ ํ”Œ๋žซํผ์— ๋งž์ถ˜๋‹ค๋Š” ์ ์„ ์„ค๋ช…ํ•œ๋‹ค.

์›น์€ ํŽธ์ง‘/๊ด€๋ฆฌ์™€ ํ’๋ถ€ํ•œ ํ™”๋ฉด, ๋ชจ๋ฐ”์ผ์€ walking/recording/device transcript์— ๊ฐ•ํ•˜๋‹ค.

Web and mobile share the product loop, but mobile adds native recording and device-side transcript fetching.

Next.jsTanStack QueryExpo mobile appWeb/mobile parityExpo audio

Code focus: frontend/app + mobile/src/app + packages/core

12

Infra and limits

๋ฐฐํฌ ๊ตฌ์กฐ์™€ ํ•œ๊ณ„๋ฅผ ์ •์งํ•˜๊ฒŒ ๋งํ•œ๋‹ค.

์›น์€ Vercel, ๋ฐฑ์—”๋“œ๋Š” container path๊ฐ€ ์žˆ๊ณ , AWS managed ๊ฒฝ๋กœ์™€ NCP ์ €๋น„์šฉ ๊ฒฝ๋กœ์˜ tradeoff๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค.

The deployment story has tradeoffs: managed AWS is more resilient; a Seoul NCP box is cheaper and lower latency but less HA.

Vercel web deploymentAWS ECS Fargate pathNCP Seoul boxCaddyHonest limits

Code focus: infrastructure + docker-compose + runbooks

Architecture layers

๊ฐ layer๋Š” โ€œ๋ฌด์—‡์„ ํ•˜๊ณ , ์™œ ๊ทธ ๊ธฐ์ˆ ์ธ์ง€โ€๊นŒ์ง€ ๊ฐ™์ด ๋ด์•ผ ํ•œ๋‹ค.

1

Learner product surface

The user imports videos, clips lines, records, reviews, and drills weak English patterns.

์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ์ œํ’ˆ์ด๋‹ค. ์œ ํŠœ๋ธŒ ์˜์ƒ ๊ฐ€์ ธ์˜ค๊ธฐ, ๊ตฌ๊ฐ„ ์ž๋ฅด๊ธฐ, ์‰๋„์ž‰, ๋…น์Œ ๋น„๊ต, ๋ณต์Šต, ๋ฌธ์žฅ ํ›ˆ๋ จ์„ ํ•œ๋‹ค.

Why: ์›น๊ณผ ๋ชจ๋ฐ”์ผ์„ ๊ฐ™์ด ๋‘” ์ด์œ ๋Š” ์‚ฌ์šฉ ๋งฅ๋ฝ์ด ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์›น์€ ํŽธ์ง‘/๊ด€๋ฆฌ, ๋ชจ๋ฐ”์ผ์€ ๊ฑท๊ฑฐ๋‚˜ ์นจ๋Œ€์—์„œ ๋ฐ˜๋ณต ํ›ˆ๋ จ์— ๋งž๋‹ค.

libraryimportplayerreview

frontend/app/[locale]/(app), mobile/src/app

2

Next.js web app

Browser UI with SSR-capable routing, locale paths, and rich training screens.

๋ธŒ๋ผ์šฐ์ €์šฉ UI๋‹ค. ๋กœ๊ทธ์ธ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ํด๋ฆฝ ํ”Œ๋ ˆ์ด์–ด, ๋ถ„์„ ํŒจ๋„, ๋ณต์Šต/ํ›ˆ๋ จ ํ™”๋ฉด์„ ์ œ๊ณตํ•œ๋‹ค.

Why: Next.js๋Š” ๋ผ์šฐํŒ…, ๋ฐฐํฌ, i18n, React ๊ธฐ๋ฐ˜ UI๋ฅผ ๋น ๋ฅด๊ฒŒ ๋ฌถ๊ธฐ ์ข‹๋‹ค. Vercel ๋ฐฐํฌ์™€๋„ ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.

App Routernext-intlTanStack QueryZustand

frontend/app, frontend/components, frontend/lib

3

Expo mobile app

Native training surface for phone-first shadowing, recording, and device-side YouTube transcript fetch.

ํฐ์—์„œ ์“ฐ๋Š” ์•ฑ์ด๋‹ค. ํƒญ ๊ตฌ์กฐ, ๋น„๋””์˜ค ํ”Œ๋ ˆ์ด์–ด, ๋…น์Œ, ์Œ์„ฑ ์ž…๋ ฅ, WebView ์ž๋ง‰ ์ˆ˜์ง‘์„ ๋‹ด๋‹นํ•œ๋‹ค.

Why: Expo๋Š” React ์ง€์‹์œผ๋กœ iOS ์•ฑ์„ ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ค๊ณ  TestFlight๊นŒ์ง€ ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๋„ค์ดํ‹ฐ๋ธŒ ์˜ค๋””์˜ค/WebView๊ฐ€ ํ•„์š”ํ•ด์„œ ๋‹จ์ˆœ ์›น์•ฑ๋ณด๋‹ค ๋งž๋‹ค.

expo-routerexpo-audiosecure-storereact-native-webview

mobile/src/app, mobile/src/components, mobile/src/lib

4

Shared TypeScript core

Static drills and pure helpers shared by web and mobile.

์›น๊ณผ ๋ชจ๋ฐ”์ผ์ด ๊ฐ™์ด ์“ฐ๋Š” ์ˆœ์ˆ˜ TS ํŒจํ‚ค์ง€๋‹ค. ํŒจํ„ด, ์ฝœ๋กœ์ผ€์ด์…˜, preposition ์ž๋ฃŒ์™€ SRS helper๊ฐ€ ๋“ค์–ด ์žˆ๋‹ค.

Why: ํ•™์Šต ์ฝ˜ํ…์ธ ์™€ ์ˆœ์ˆ˜ ๋กœ์ง์€ ํ”Œ๋žซํผ๊ณผ ๋ฌด๊ด€ํ•˜๋‹ค. ๋ณต๋ถ™์„ ๋ง‰๊ณ  web/mobile parity๋ฅผ ์œ ์ง€ํ•˜๋ ค๊ณ  shared package๋กœ ๋บ๋‹ค.

patternscollocationspractice-srsyoutube parser

packages/core/src

5

Spring Boot API

Owns auth, user data, video import, clips, AI analysis, recordings, review, practice, decks, and billing entitlements.

์ƒํƒœ์™€ ๋น„์ฆˆ๋‹ˆ์Šค ๊ทœ์น™์˜ ์ค‘์‹ฌ์ด๋‹ค. ์‚ฌ์šฉ์ž์˜ ๋ฐ์ดํ„ฐ, ํŠธ๋žœ์žญ์…˜, ๊ถŒํ•œ, AI ํ˜ธ์ถœ, ์ €์žฅ์†Œ ๊ฒฝ๊ณ„๋ฅผ ์ฑ…์ž„์ง„๋‹ค.

Why: Java/Spring์€ ์ธ์ฆ, ํŠธ๋žœ์žญ์…˜, JPA, async, actuator, ํ…Œ์ŠคํŠธ ์ƒํƒœ๊ณ„๊ฐ€ ๊ฐ•ํ•˜๋‹ค. ๋ฉด์ ‘์—์„œ๋„ ๋ฐฑ์—”๋“œ ์„ค๊ณ„๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์ข‹๋‹ค.

Java 21Spring Boot 3.3Spring SecurityJPA

backend/src/main/java/com/tubeshadow

6

PostgreSQL data core

Stores users, videos, clips, JSONB transcripts, AI results, recordings metadata, review state, and practice state.

์ œํ’ˆ์˜ ์˜์† ์ƒํƒœ๋‹ค. ์œ ์ €๋ณ„ ๋ฐ์ดํ„ฐ์™€ ์ „์—ญ ๋น„๋””์˜ค ์บ์‹œ๋ฅผ ์ €์žฅํ•˜๊ณ , JSONB๋กœ ์ž๋ง‰/AI ๊ฒฐ๊ณผ ๊ฐ™์€ ๋ฐ˜๊ตฌ์กฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š”๋‹ค.

Why: ๊ด€๊ณ„ํ˜• ์†Œ์œ ๊ถŒ/์ œ์•ฝ/ํŠธ๋žœ์žญ์…˜์ด ํ•„์š”ํ•˜๋ฉด์„œ transcript์™€ analysis๋Š” JSON ๊ตฌ์กฐ๋ผ Postgres + JSONB๊ฐ€ ์ž˜ ๋งž๋Š”๋‹ค.

Flyway migrationsunique youtube_idJSONBCHECK

backend/src/main/resources/db/migration

7

AI analysis pipeline

Turns clipped transcript into translation, chunk gloss, vocabulary, grammar notes, scenario, and preposition notes.

ํด๋ฆฝ ์ €์žฅ ํ›„ AI ์„ค๋ช…์„ ๋งŒ๋“ ๋‹ค. ๋А๋ฆฐ provider ํ˜ธ์ถœ์€ ํŠธ๋žœ์žญ์…˜ ๋ฐ– ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋Œ๋ฆฌ๊ณ  ๊ฒฐ๊ณผ๋งŒ DB์— ์ €์žฅํ•œ๋‹ค.

Why: AI ํ˜ธ์ถœ์€ ๋А๋ฆฌ๊ณ  ์‹คํŒจํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ž˜์„œ event + async + status machine + cached result ๊ตฌ์กฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

AFTER_COMMIT@AsyncAiAnalysisClientCompositeAiClient

ClipService.java, ClipAnalysisService.java, CompositeAiClient.java

8

YouTube transcript ingestion

Imports metadata, dimensions, captions, and stores a global video cache.

์œ ํŠœ๋ธŒ URL์—์„œ ์˜์ƒ ์ •๋ณด์™€ ์ž๋ง‰์„ ์–ป๋Š”๋‹ค. ์„œ๋ฒ„๋Š” yt-dlp/POToken/Supadata๋ฅผ ์“ฐ๊ณ , ๋ชจ๋ฐ”์ผ์€ ํฐ WebView๋กœ ์ง์ ‘ ์ž๋ง‰์„ ๊ฐ€์ ธ์˜จ๋‹ค.

Why: YouTube๋Š” datacenter IP๋ฅผ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค. ์„œ๋ฒ„ ๊ฒฝ๋กœ์™€ ๋””๋ฐ”์ด์Šค ๊ฒฝ๋กœ๋ฅผ ๋‘˜ ๋‹ค ๋‘ฌ์•ผ ์‹ค์ œ ์„œ๋น„์Šค๊ฐ€ ์‚ฐ๋‹ค.

oEmbedyt-dlpPOToken sidecarSupadata fallback

VideoImportService.java, YoutubeTranscriptClient.java, mobile youtube-transcript-webview.tsx

9

Recording storage seam

Stores learner voice recordings through an interface: local disk in dev/NCP, S3-compatible storage when configured.

์‚ฌ์šฉ์ž ๋…น์Œ ํŒŒ์ผ ์ €์žฅ ๊ณ„์ธต์ด๋‹ค. DB์—๋Š” metadata๋งŒ, bytes๋Š” storage์— ์ €์žฅํ•˜๊ณ  ๋ฐฑ์—”๋“œ๊ฐ€ ์†Œ์œ ๊ถŒ ํ™•์ธ ํ›„ streamํ•œ๋‹ค.

Why: ํŒŒ์ผ bytes๋ฅผ DB์— ๋„ฃ์ง€ ์•Š๊ณ , storage ๊ตฌํ˜„์„ env๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๊ฒŒ ๋ถ„๋ฆฌํ–ˆ๋‹ค. ๊ณต๊ฐœ URL์„ ์ฃผ์ง€ ์•Š์•„ ๊ฐœ์ธ์ •๋ณด๋„ ์ง€ํ‚จ๋‹ค.

RecordingStorageLocalRecordingStorageS3RecordingStorageInputStreamResource

backend/src/main/java/com/tubeshadow/recording

10

Two SRS engines

SM-2 for clip review, Leitner for binary daily drill cards.

๋ณต์Šต ์Šค์ผ€์ค„๋ง์ด๋‹ค. ํด๋ฆฝ ๋ฆฌ๋ทฐ๋Š” Again/Hard/Good/Easy๋ผ SM-2, ํŒจํ„ด ๋“œ๋ฆด์€ ๋งž์Œ/ํ‹€๋ฆผ์ด๋ผ Leitner๋ฅผ ์“ด๋‹ค.

Why: ์ฑ„์  ๋ชจ์–‘์ด ๋‹ค๋ฅด๋ฉด ์•Œ๊ณ ๋ฆฌ์ฆ˜๋„ ๋‹ฌ๋ผ์•ผ ํ•œ๋‹ค. 0-5 ํ’ˆ์งˆ ์ ์ˆ˜์™€ binary recall์„ ์–ต์ง€๋กœ ํ•œ ๋ชจ๋ธ์— ๋„ฃ์ง€ ์•Š์•˜๋‹ค.

ReviewItemSm2CalculatorPracticeCardPracticeSrsService

review, practice domain, packages/core/src/practice-srs.ts

11

Deployment layer

Runs web on Vercel and backend on container infrastructure; current runbook includes NCP/Caddy and older AWS ECS path.

๋ฐฐํฌ ๊ณ„์ธต์ด๋‹ค. ์›น์€ Vercel, ๋ฐฑ์—”๋“œ๋Š” Docker ์ปจํ…Œ์ด๋„ˆ๋กœ ์šด์˜ํ•œ๋‹ค. AWS ECS ์„ค๊ณ„์™€ NCP ๋‹จ์ผ ๋ฐ•์Šค ์ „ํ™˜ ๋ฌธ์„œ๊ฐ€ ๊ฐ™์ด ์žˆ๋‹ค.

Why: ์ดˆ๊ธฐ์—๋Š” managed AWS๊ฐ€ ์•ˆ์ •์ ์ด๊ณ , ๋น„์šฉ/ํ•œ๊ตญ ์ง€์—ฐ์‹œ๊ฐ„ ๋•Œ๋ฌธ์— NCP ๋‹จ์ผ ๋ฐ•์Šค๋กœ ์ค„์ด๋Š” ์„ ํƒ์ง€๊ฐ€ ์ƒ๊ฒผ๋‹ค.

DockerCaddyPostgres containerpot-provider

infrastructure, Dockerfile, docker-compose, DEVOPS.md

12

Observability and audit trail

Request logs, user/request MDC, Actuator/Prometheus metrics, and written troubleshooting logs.

์šด์˜ ์ค‘ ์›์ธ์„ ์ฐพ๊ธฐ ์œ„ํ•œ ๊ณ„์ธต์ด๋‹ค. ๋กœ๊ทธ์— request/user id๋ฅผ ํƒœ์šฐ๊ณ , AI ์ง€์—ฐ/์‹คํŒจ๋ฅผ metric์œผ๋กœ ๋‚จ๊ธด๋‹ค.

Why: ๋ฉด์ ‘์—์„œ ์ค‘์š”ํ•œ ๊ฑด ๋งŒ๋“  ๊ธฐ๋Šฅ๋ณด๋‹ค ๊ณ ์žฅ ๋‚ฌ์„ ๋•Œ ์ฐพ๋Š” ๋Šฅ๋ ฅ์ด๋‹ค. ๋กœ๊ทธ์™€ metric, decision log๊ฐ€ ๊ทธ ์ฆ๊ฑฐ๋‹ค.

RequestLoggingFilterMicrometerActuatordocs/troubleshooting.md

common/web, analysis/application, docs

Runtime flows

์ •์  ๊ตฌ์กฐ๋ณด๋‹ค request/data flow๋ฅผ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

Selected flow

Signup and authenticated API call

์ด๋ฉ”์ผ/๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ๊ฐ€์ž…ํ•˜๊ณ  JWT๋ฅผ ๋ฐ›์•„ ์ดํ›„ ์š”์ฒญ๋งˆ๋‹ค Authorization header๋กœ ๋ณด๋‚ธ๋‹ค.

1POST /api/auth/signup
2BCrypt hash saved in users
3POST /api/auth/login
4JWT issued with user id + token_version
5Frontend/mobile store token
6API call sends Bearer token
7JwtAuthenticationFilter checks signature + DB token_version
Failure checks
  • JWT secret must not be dev default in prod
  • Password change must bump token_version
  • User-scoped queries must use userId filters
Say it

Mimi uses stateless JWT for API auth, but it still supports revocation by checking a token_version claim against the database on every request.

Backend domains

๋„๋ฉ”์ธ์„ ์™ธ์šฐ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ฑ…์ž„, ํŒŒ์ผ, ํ…Œ์ด๋ธ”, ๋ฉด์ ‘ ๋ฌธ์žฅ์œผ๋กœ ์—ฐ๊ฒฐํ•ด๋ผ.

auth

auth

ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ, JWT ๋ฐœ๊ธ‰/๊ฒ€์ฆ, password hash, token-version revocation.

Auth is stateless JWT, but password change still revokes old tokens through a token_version claim checked against the DB.

Files: AuthController, AuthService, User, JwtTokenProvider, JwtAuthenticationFilter, SecurityConfig
Tables: users
video

video

YouTube metadata/transcript import, global video cache, curated collections.

Videos are global cache rows keyed by youtube_id, and per-user ownership is modeled separately through library/clips.

Files: VideoImportService, YoutubeTranscriptClient, YoutubeProbe, CollectionService, Video
Tables: videos, collections, collection_videos
library

library

์‚ฌ์šฉ์ž๋ณ„ saved videos ๋ชฉ๋ก. ๊ฐ™์€ global video๋ฅผ ์—ฌ๋Ÿฌ ์œ ์ €๊ฐ€ ์ž๊ธฐ library์— ์ €์žฅ.

The videos table is shared cache; library_videos is the user-specific saved-video relation and makes re-import idempotent.

Files: LibraryVideoController, LibraryVideoService, LibraryVideo
Tables: library_videos
clip

clip

์˜์ƒ ๊ตฌ๊ฐ„ ์ž๋ฅด๊ธฐ, transcript slicing, tag/note/deck ์—ฐ๊ฒฐ, AI/review ์ด๋ฒคํŠธ ๋ฐœํ–‰.

A clip is the user's study unit; creating it publishes a domain event that seeds review and AI analysis after commit.

Files: ClipController, ClipService, TranscriptSlicer, Clip, ClipRepository
Tables: clips
analysis

analysis

AI ๋ถ„์„ ์ƒํƒœ/๊ฒฐ๊ณผ ์ €์žฅ, provider fallback, grammar/vocab/chunk/preposition/scenario ์ƒ์„ฑ.

The slow provider call is outside the DB transaction; only PENDING and READY/FAILED writes are transactional.

Files: ClipAnalysisService, ClipAnalysis, CompositeAiClient, GeminiClient, OpenAiClient, ClaudeClient
Tables: clip_analyses
recording

recording

์‚ฌ์šฉ์ž ์Œ์„ฑ ๋…น์Œ ์—…๋กœ๋“œ, MIME/size ๊ฒ€์ฆ, local/S3 ์ €์žฅ, ์†Œ์œ ๊ถŒ ๊ธฐ๋ฐ˜ stream/delete.

Recordings are never public URLs; the backend verifies ownership and streams the bytes.

Files: RecordingController, RecordingService, RecordingStorage, LocalRecordingStorage, S3RecordingStorage
Tables: recordings
review

review

ํด๋ฆฝ ๊ธฐ๋ฐ˜ SM-2 ๋ณต์Šต queue, quality grade, streak.

Clip review uses SM-2 because it has a 0-to-5 quality signal like Again/Hard/Good/Easy.

Files: ReviewController, ReviewService, ReviewItem, Sm2Calculator
Tables: review_items
practice

practice

ํŒจํ„ด/์ฝœ๋กœ์ผ€์ด์…˜/์˜์ž‘/์‹œ๋‚˜๋ฆฌ์˜ค/์ธํ„ฐ๋ทฐ/์ „์‚ฌ/๋ฌธ์žฅ ๋ณ€ํ˜• ํ›ˆ๋ จ๊ณผ Leitner SRS.

Daily drills use a simpler Leitner system because the grading signal is binary: got it or missed it.

Files: PracticeController, CompositionService, PracticeSrsService, TransformService, TranscriptionClient
Tables: practice_progress, practice_card, sentence_transform_set
deck

deck

Anki์‹ ๋ฑ ๊ตฌ์„ฑ. ํด๋ฆฝ์„ ๊ทธ๋ฃนํ™”ํ•˜๋˜ deck ์‚ญ์ œ๋Š” clip ์‚ญ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ Inbox๋กœ ๋˜๋Œ๋ฆผ.

A deck is organization, not ownership; deleting a deck keeps the clips by setting deck_id to null.

Files: DeckController, DeckService, Deck
Tables: decks, clips.deck_id
billing

billing

์™ธ๋ถ€ ๊ฒฐ์ œ ํ”Œ๋žซํผ์ด plan entitlement๋ฅผ webhook์œผ๋กœ ๋ฐ˜์˜ํ•˜๋Š” skeleton.

Mimi does not process payments itself; it only stores entitlement state from an external billing source.

Files: BillingController, BillingService
Tables: users.plan, users.plan_valid_until, users.billing_customer_id
common

common

๊ณตํ†ต ์‘๋‹ต, ์˜ˆ์™ธ, logging, CORS, async config, S3 config, current-user resolver.

Cross-cutting concerns live in common, but business logic stays feature-sliced by domain.

Files: ApiResponse, GlobalExceptionHandler, RequestLoggingFilter, WebMvcConfig, AsyncConfig
Tables: none

Concept cards

๋ชจ๋ฅด๋Š” ๋‹จ์–ด๋ฅผ ๋ˆ„๋ฅด๋ฉด ์˜๋ฏธ, repo ์œ„์น˜, ์„ ํƒ ์ด์œ , ์˜์–ด ๋‹ต๋ณ€, ํ•จ์ •, ์ฝ”๋“œ ์˜ˆ์‹œ๊ฐ€ ์—ด๋ฆฐ๋‹ค.

72
visible

Source map

์ด ํŽ˜์ด์ง€๊ฐ€ ์–ด๋–ค shadow-ai ๋ฌธ์„œ/์ฝ”๋“œ์—์„œ ์™”๋Š”์ง€ ๊ธฐ์–ตํ•ด๋ผ.

Product README
shadow-ai/README.md

High-level product, stack, architecture, security, testing

Interview prep
shadow-ai/INTERVIEW_PREP.md

Existing verified scripts for async AI and provider fallback

Architecture doc
shadow-ai/ARCHITECTURE.md

C4, data model, flows, failure modes, scaling

Backend source
shadow-ai/backend/src/main/java/com/tubeshadow

Actual domain/service/controller/security code

DB migrations
shadow-ai/backend/src/main/resources/db/migration

Source of truth for tables, constraints, JSONB columns

Web app
shadow-ai/frontend/app + components + lib

Next screens, API client, React Query, i18n

Mobile app
shadow-ai/mobile/src/app + components + lib

Expo screens, recording, WebView transcript, phone-first flows

Shared core
shadow-ai/packages/core/src

Shared drill content and pure helpers

Infra
shadow-ai/infrastructure + docker-compose

AWS/NCP, Caddy, pot-provider, Docker deployment shape

Troubleshooting log
shadow-ai/docs/troubleshooting.md

Real incidents, fixes, and operational learning