๋ฉ”์ธ ์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

์‹œ์ž‘ํ•˜๊ธฐ

๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ์ƒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:
pnpm create sonamu
๋Œ€ํ™”ํ˜• ํ”„๋กฌํ”„ํŠธ์— ๋”ฐ๋ผ ํ”„๋กœ์ ํŠธ ์„ค์ •:
? Project name: my-app
? Would you like to set up pnpm? Yes
? Would you like to set up a database using Docker? Yes
? Enter the Docker project name: my-app-container
? Enter the database user: postgres
? Enter the container name: my-app-pg
? Enter the database name: my-app
? Enter the database password: ****
ํ”„๋กœ์ ํŠธ ์‹คํ–‰:
# 1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‹œ์ž‘
cd my-app/api
pnpm db:up

# 2. API ์„œ๋ฒ„ ์‹œ์ž‘ (Sonamu UI ํฌํ•จ)
pnpm dev

# 3. ์ƒˆ ํ„ฐ๋ฏธ๋„์—์„œ Web ์„œ๋ฒ„ ์‹œ์ž‘
cd my-app/web
pnpm dev
์ ‘์† ๊ฐ€๋Šฅํ•œ URL:
API: http://localhost:1028
Sonamu UI: http://localhost:1028/sonamu-ui
Web: http://localhost:3028
์š”๊ตฌ์‚ฌํ•ญ: Node.js >= 18, pnpm >= 10, Docker
๋ฐฑ์—”๋“œ: TypeScript, Node.js, Fastify, Knex.js, PostgreSQL, Zod
ํ”„๋ก ํŠธ์—”๋“œ: React, TanStack Query, Axios, Tailwind CSS
๊ฐœ๋ฐœ ๋„๊ตฌ: pnpm, Docker
์ž๋™ ์ƒ์„ฑ: ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ, TypeScript ํƒ€์ž…, API ์—”๋“œํฌ์ธํŠธ, React hooks

๊ฐœ๋ฐœ ํ™˜๊ฒฝ

  1. TypeScript ํŒŒ์ผ ์ฆ‰์‹œ ์‹คํ–‰
    • @sonamu-kit/ts-loader๋กœ TypeScript ํŒŒ์ผ์„ ๋Ÿฐํƒ€์ž„์— ์ฆ‰์‹œ ๋ณ€ํ™˜
    • Node.js์˜ --import ํ”Œ๋ž˜๊ทธ๋กœ ๋“ฑ๋ก
  2. Sonamu ์ดˆ๊ธฐํ™” (Sonamu.init)
    • DB ์„ค์ • ๋กœ๋“œ
    • Entity ํŒŒ์ผ ๋กœ๋“œ
    • Syncer ์ƒ์„ฑ ๋ฐ Types/Models/APIs ์ž๋™ ๋กœ๋“œ
    • ์ดˆ๊ธฐ ์‹ฑํฌ ์‹คํ–‰ (syncer.sync())
    • ํŒŒ์ผ ๊ฐ์‹œ ์‹œ์ž‘ (startWatcher())
  3. Fastify ์„œ๋ฒ„ ์ƒ์„ฑ ๋ฐ ์„ค์ •
    • ํ”Œ๋Ÿฌ๊ทธ์ธ ๋“ฑ๋ก (formbody, multipart, session ๋“ฑ)
    • Auth ์„ค์ • (ํ•„์š”์‹œ)
    • @api ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ๋ถ™์€ ๋ฉ”์„œ๋“œ๋“ค์„ API ์—”๋“œํฌ์ธํŠธ๋กœ ๋“ฑ๋ก
  4. ์„œ๋ฒ„ ๋ฆฌ์Šค๋‹ ์‹œ์ž‘
    • ์„ค์ •๋œ ํฌํŠธ์—์„œ Fastify ์„œ๋ฒ„ ์‹œ์ž‘
  5. HMR ํ™œ์„ฑํ™”
    • ํŒŒ์ผ ๋ณ€๊ฒฝ ๊ฐ์ง€ ์‹œ ์ž๋™์œผ๋กœ ํƒ€์ž… ์žฌ์ƒ์„ฑ ๋ฐ ๋ชจ๋“ˆ ๋ฆฌ๋กœ๋“œ
๋ฐฉ๋ฒ• 1: ํฌํŠธ ๋ณ€๊ฒฝ
// api/src/sonamu.config.ts
export default {
  server: {
    listen: {
      port: 2028,  // ์›ํ•˜๋Š” ํฌํŠธ๋กœ ๋ณ€๊ฒฝ
    }
  }
} satisfies SonamuConfig;
๋ฐฉ๋ฒ• 2: ๊ธฐ์กด ํ”„๋กœ์„ธ์Šค ์ข…๋ฃŒ (macOS/Linux)
lsof -ti:1028 | xargs kill -9

ํŒŒ์ผ ์ƒ์„ฑ

ํƒ€์ž… ํŒŒ์ผ: {entity}.types.ts, sonamu.generated.ts
๋งˆ์ด๊ทธ๋ ˆ์ด์…˜: migrations/{timestamp}_{entity}_create.ts
Model ํด๋ž˜์Šค: {entity}.model.ts (Scaffolding)
ํ…Œ์ŠคํŠธ ํŒŒ์ผ: {entity}.model.test.ts
View ํŒŒ์ผ: React ์ปดํฌ๋„ŒํŠธ (๋ชฉ๋ก, ์ƒ์„ธ, ํŽธ์ง‘)
ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ: Axios ํด๋ผ์ด์–ธํŠธ, TanStack Query hooks
1. ์ž๋™ ์ƒ์„ฑ (Entity ์ƒ์„ฑ/์ˆ˜์ • ์‹œ)
  • {entity}.entity.json
    • Entity์˜ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ, ํ•„๋“œ, ๊ด€๊ณ„, ์ธ๋ฑ์Šค, Subset ์ •์˜
    • ์ƒ์„ฑ: Sonamu UI์—์„œ Entity ์ƒ์„ฑ ์‹œ
  • {entity}.types.ts
    • Entity์˜ ์ปค์Šคํ…€ ํƒ€์ž… ์ •์˜
    • ์ƒ์„ฑ: Entity ์ƒ์„ฑ ์‹œ (์ดˆ๊ธฐ ํ…œํ”Œ๋ฆฟ๋งŒ, ์ดํ›„ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ˆ˜์ •)
  • sonamu.generated.ts
    • ๋ชจ๋“  Entity์˜ BaseSchema, Enum, Subset ํƒ€์ž… ์ž๋™ ์ƒ์„ฑ
    • ์ƒ์„ฑ: Entity ๋˜๋Š” ํƒ€์ž… ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ๋งˆ๋‹ค ์ž๋™ ์žฌ์ƒ์„ฑ
  • sonamu.generated.sso.ts
    • Subset๋ณ„ ์ฟผ๋ฆฌ ๋นŒ๋”์™€ ์„œ๋ฒ„ ์ „์šฉ ํƒ€์ž…
    • ์ƒ์„ฑ: Entity ๋ณ€๊ฒฝ ์‹œ๋งˆ๋‹ค ์ž๋™ ์žฌ์ƒ์„ฑ
2. ์ˆ˜๋™ ์ƒ์„ฑ (Sonamu UI์—์„œ Generate ํด๋ฆญ)
  • migrations/{timestamp}_{action}.ts
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ์„ ์œ„ํ•œ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ
    • ์ƒ์„ฑ: DB Migration ํƒญ์—์„œ Generate ํด๋ฆญ ์‹œ
  • {entity}.model.ts
    • Entity์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ @api ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ API ์ •์˜
    • ์ƒ์„ฑ: Scaffolding ํƒญ์—์„œ Model ํ…œํ”Œ๋ฆฟ Generate ์‹œ (1ํšŒ๋งŒ)
  • {entity}.model.test.ts
    • Model ๋ฉ”์„œ๋“œ์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ํŒŒ์ผ
    • ์ƒ์„ฑ: Scaffolding ํƒญ์—์„œ Test ํ…œํ”Œ๋ฆฟ Generate ์‹œ
  • *.view.tsx
    • React ๊ธฐ๋ฐ˜ ๋ฐฑ์˜คํ”ผ์Šค UI ์ปดํฌ๋„ŒํŠธ
    • ์ƒ์„ฑ: Scaffolding ํƒญ์—์„œ View ํ…œํ”Œ๋ฆฟ Generate ์‹œ
3. ์ž๋™ ์ƒ์„ฑ (API ์„œ๋ฒ„ ์‹คํ–‰ ์ค‘)
  • {entity}.service.ts
    • ๋ฐฑ์—”๋“œ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
    • ์ƒ์„ฑ: Model ํŒŒ์ผ ๋ณ€๊ฒฝ ๊ฐ์ง€ ์‹œ
  • sonamu.generated.http
    • VSCode REST Client์šฉ API ํ…Œ์ŠคํŠธ ํ…œํ”Œ๋ฆฟ
    • ์ƒ์„ฑ: @api ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๋ฉ”์„œ๋“œ ์ถ”๊ฐ€/๋ณ€๊ฒฝ ์‹œ
  • ํ”„๋ก ํŠธ์—”๋“œ ํŒŒ์ผ ๋ณต์‚ฌ
    • types.ts, generated.ts ๋“ฑ์ด web/src/services/๋กœ ์ž๋™ ๋ณต์‚ฌ
    • ์ƒ์„ฑ: ๋ฐฑ์—”๋“œ ํŒŒ์ผ ๋ณ€๊ฒฝ ์‹œ sync.targets์— ์ •์˜๋œ ๊ฒฝ๋กœ๋กœ ์ž๋™ ๋ณต์‚ฌ
  • sonamu.lock
    • ํŒŒ์ผ ๋ณ€๊ฒฝ ์ถ”์ ์„ ์œ„ํ•œ ์ฒดํฌ์„ฌ ํŒŒ์ผ
    • ์ƒ์„ฑ: ํŒŒ์ผ ๋ณ€๊ฒฝ ์ถ”์ ์„ ์œ„ํ•ด ์ž๋™ ๊ฐฑ์‹ 
ํ•ญ์ƒ ๋ฎ์–ด์“ฐ๊ธฐ (overwrite: true)๋‹ค์Œ ํŒŒ์ผ๋“ค์€ ์žฌ์ƒ์„ฑ ์‹œ ํ•ญ์ƒ ๋ฎ์–ด์“ฐ๊ธฐ๋˜๋ฏ€๋กœ ์ˆ˜์ •ํ•˜์ง€ ๋งˆ์„ธ์š”:
  • sonamu.generated.ts - Entity ๋ณ€๊ฒฝ ์‹œ ํ•ญ์ƒ ์žฌ์ƒ์„ฑ
  • sonamu.generated.sso.ts - Entity ๋ณ€๊ฒฝ ์‹œ ํ•ญ์ƒ ์žฌ์ƒ์„ฑ
  • {entity}.service.ts - Model ๋ณ€๊ฒฝ ์‹œ ํ•ญ์ƒ ์žฌ์ƒ์„ฑ
  • sonamu.generated.http - @api ๋ฉ”์„œ๋“œ ๋ณ€๊ฒฝ ์‹œ ํ•ญ์ƒ ์žฌ์ƒ์„ฑ
์žฌ์ƒ์„ฑ ์‹œ ๋ณดํ˜ธ (overwrite: false, ๊ธฐ๋ณธ๊ฐ’)๋‹ค์Œ ํŒŒ์ผ๋“ค์€ ์žฌ์ƒ์„ฑํ•ด๋„ ๋ฎ์–ด์“ฐ์ด์ง€ ์•Š์œผ๋ฏ€๋กœ ์ž์œ ๋กญ๊ฒŒ ์ˆ˜์ • ๊ฐ€๋Šฅ:
  • {entity}.types.ts - ์ปค์Šคํ…€ ํƒ€์ž… ์ถ”๊ฐ€ ๊ฐ€๋Šฅ
  • {entity}.model.ts - ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ์ถ”๊ฐ€/์ˆ˜์ • ๊ฐ€๋Šฅ
  • {entity}.model.test.ts - ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ถ”๊ฐ€/์ˆ˜์ • ๊ฐ€๋Šฅ
  • *.view.tsx - UI ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ€๋Šฅ
Best Practice:
  • ์ปค์Šคํ…€ ํƒ€์ž…์€ {entity}.types.ts์— ์ถ”๊ฐ€
  • ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ {entity}.model.ts์— ๊ตฌํ˜„
  • Entity ์ •์˜๋Š” Sonamu UI ์‚ฌ์šฉ
๋ฐ˜๋“œ์‹œ ์ปค๋ฐ‹ํ•ด์•ผ ํ•˜๋Š” ํŒŒ์ผ:
  • sonamu.lock - Entity ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์ถ”์ , ํŒ€ ๋™๊ธฐํ™”
  • sonamu.generated.ts - ํƒ€์ž… ๋™๊ธฐํ™”, ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • {entity}.types.ts - ์ปค์Šคํ…€ ํƒ€์ž… ์ •์˜
  • migrations/*.ts - DB ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์ด๋ ฅ
  • {entity}.model.ts - ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง
์ด์œ :
  • ํƒ€์ž… ๋™๊ธฐํ™”๋กœ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ๋นŒ๋“œ ์‹œ๊ฐ„ ๋‹จ์ถ•
  • ์ฝ”๋“œ ๋ฆฌ๋ทฐ ์šฉ์ด

์Šค์บํด๋”ฉ

ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ˜๋ณต์ ์ธ ๊ธฐ๋ณธ ์ฝ”๋“œ(๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ)๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.์ƒ์„ฑ ๊ฐ€๋Šฅํ•œ ๊ฒƒ:
  • Model ํด๋ž˜์Šค ({entity}.model.ts)
  • Test ํŒŒ์ผ ({entity}.model.test.ts)
  • View ์ปดํฌ๋„ŒํŠธ (*.view.tsx)
์‹คํ–‰ ๋ฐฉ๋ฒ•:
  1. Sonamu UI ์ ‘์†
  2. Scaffolding ํƒญ ์ด๋™
  3. Entity ์„ ํƒ
  4. Template ์„ ํƒ (model/model_test/view_list/view_form)
  5. Generate ํด๋ฆญ
ํ•„์š”ํ•œ ์ด์œ :
  • ๋ฐ˜๋ณต์ ์ธ CRUD ์ฝ”๋“œ ์ž๋™ ์ƒ์„ฑ์œผ๋กœ ๊ฐœ๋ฐœ ์‹œ๊ฐ„ ๋‹จ์ถ•
  • ์ผ๊ด€๋œ ์ฝ”๋“œ ๊ตฌ์กฐ ์œ ์ง€
  • ์˜คํƒ€๋‚˜ ๋ˆ„๋ฝ ๋ฐฉ์ง€
  • ์ดˆ๋ณด์ž๋„ ํ‘œ์ค€ ํŒจํ„ด ํ•™์Šต ๊ฐ€๋Šฅ
overwrite ์˜ต์…˜:
  • overwrite: false (๊ธฐ๋ณธ๊ฐ’): ํŒŒ์ผ์ด ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ (model, test, view ํ…œํ”Œ๋ฆฟ)
  • overwrite: true: ํŒŒ์ผ์ด ์กด์žฌํ•ด๋„ ํ•ญ์ƒ ๋ฎ์–ด์“ฐ๊ธฐ (generated, service ํ…œํ”Œ๋ฆฟ)

๊ด€๋ จ ๋ฌธ์„œ