Skip to main content

μ„œλ²„ μž¬μ‹œμž‘ 없이 κ°œλ°œν•˜κΈ°

Sonamu둜 κ°œλ°œν•  λ•ŒλŠ” μ΄λ ‡μŠ΅λ‹ˆλ‹€:
  1. μ½”λ“œ μˆ˜μ •
  2. μ €μž₯ (Cmd+S)
  3. 끝! πŸ‘ˆ λ°”λ‘œ 반영됨
λΈŒλΌμš°μ €λ₯Ό μƒˆλ‘œκ³ μΉ¨ν•˜κ±°λ‚˜, μ„œλ²„λ₯Ό μž¬μ‹œμž‘ν•˜κ±°λ‚˜, λΉŒλ“œ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•  ν•„μš”κ°€ μ—†μŠ΅λ‹ˆλ‹€.

μ‹€μ œ 개발 속도 차이

HMR 없이 (전톡적 방식):
HMR μžˆμ„ λ•Œ (Sonamu 방식):
ν•˜λ£¨μ— Entity/APIλ₯Ό 50번 μˆ˜μ •ν•œλ‹€λ©΄?
  • HMR 없이: 50 Γ— 30초 = 25λΆ„ λ‚­λΉ„
  • HMR μžˆμ„ λ•Œ: 50 Γ— 2초 = 1.7λΆ„

λ‹€λ₯Έ ν”„λ ˆμž„μ›Œν¬μ™€μ˜ 차이

Sonamu만의 νŠΉλ³„ν•¨

일반적인 Node.js HMR:
  • λ‹¨μˆœνžˆ 파일 μž¬λ‘œλ“œλ§Œ 함
  • μžλ™ 생성 μ½”λ“œλŠ” 직접 관리 ν•„μš”
  • Entity λ³€κ²½ μ‹œ μ—¬λŸ¬ 파일 μˆ˜λ™ μˆ˜μ •
Sonamu HMR:
  • 파일 μž¬λ‘œλ“œ + Syncer μžλ™ μ‹€ν–‰
  • Entity λ³€κ²½ β†’ λͺ¨λ“  κ΄€λ ¨ μ½”λ“œ μžλ™ 생성/μ—…λ°μ΄νŠΈ
  • μ˜μ‘΄ν•˜λŠ” λͺ¨λ“  파일 μžλ™ μž¬λ‘œλ“œ
  • API μžλ™ μž¬λ“±λ‘

μ‹€μ œ 개발 μ‹œλ‚˜λ¦¬μ˜€

μ‹œλ‚˜λ¦¬μ˜€ 1: Entity ν•„λ“œ μΆ”κ°€ν•˜κΈ°

User Entity에 nickname ν•„λ“œλ₯Ό μΆ”κ°€ν•˜λŠ” κ³Όμ •: 1단계: Sonamu UIμ—μ„œ ν•„λ“œ μΆ”κ°€
2단계: μ €μž₯ν•˜λ©΄ μ¦‰μ‹œ
  • user.entity.ts μžλ™ μ—…λ°μ΄νŠΈ
  • user.types.ts νƒ€μž… μžλ™ 생성
  • user.zod.ts Zod μŠ€ν‚€λ§ˆ μžλ™ 생성
  • UserModel μžλ™ μž¬λ‘œλ“œ
  • λͺ¨λ“  UserApi λ©”μ„œλ“œ μžλ™ μž¬λ“±λ‘
  • ν”„λ‘ νŠΈμ—”λ“œ UserService μžλ™ μ—…λ°μ΄νŠΈ
3단계: μ½˜μ†” 확인
4단계: ν…ŒμŠ€νŠΈ
⏱️ HMR μžˆμ„ λ•Œ: 2초
⏱️ HMR 없을 λ•Œ: 30초 (μ„œλ²„ μž¬μ‹œμž‘ + 재컴파일)

μ‹œλ‚˜λ¦¬μ˜€ 2: API 둜직 μˆ˜μ •ν•˜κΈ°

UserApi.list()에 νŽ˜μ΄μ§€λ„€μ΄μ…˜κ³Ό 검색 κΈ°λŠ₯ μΆ”κ°€:
μ €μž₯ν•˜λ©΄:
μ¦‰μ‹œ Postman/Thunder Clientμ—μ„œ ν…ŒμŠ€νŠΈ κ°€λŠ₯!

μ‹œλ‚˜λ¦¬μ˜€ 3: Model λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μΆ”κ°€

User의 ν™œμ„± μƒνƒœ 체크 둜직 μΆ”κ°€:
μ €μž₯ β†’ 2초 ν›„ β†’ λ‹€λ₯Έ APIμ—μ„œ λ°”λ‘œ μ‚¬μš© κ°€λŠ₯:

Sonamu HMR의 ν˜μ‹ : Syncer 톡합

일반적인 Node.js HMR은 β€œνŒŒμΌμ΄ λ°”λ€Œμ—ˆλ„€? λ‹€μ‹œ λΆˆλŸ¬μ˜€μžβ€κ°€ μ „λΆ€μž…λ‹ˆλ‹€. ν•˜μ§€λ§Œ SonamuλŠ” λ‹€λ¦…λ‹ˆλ‹€.

문제: μžλ™ 생성 μ½”λ“œμ˜ λ”œλ ˆλ§ˆ

SonamuλŠ” Entity 기반으둜 μˆ˜λ§Žμ€ μ½”λ“œλ₯Ό μžλ™ μƒμ„±ν•©λ‹ˆλ‹€:
  • TypeScript νƒ€μž… 파일 (*.types.ts)
  • Zod μŠ€ν‚€λ§ˆ (*.zod.ts)
  • API 라우트 등둝
  • ν”„λ‘ νŠΈμ—”λ“œ Service 클래슀
λ§Œμ•½ Syncer와 HMR이 λ”°λ‘œ λ™μž‘ν•œλ‹€λ©΄:

Sonamu의 ν•΄κ²°μ±…

Watcherκ°€ λͺ¨μ€ 파일 μ΄λ²€νŠΈλ“€μ„ Syncerκ°€ ν•œ μ§„μž…μ (hmrAndSync)μ—μ„œ λ°›μ•„ HMRκ³Ό syncλ₯Ό μ˜μ—­λ³„λ‘œ λΆ„λ¦¬ν•΄μ„œ μ²˜λ¦¬ν•©λ‹ˆλ‹€:
μˆœμ„œκ°€ 보μž₯λ˜λ―€λ‘œ:
  1. μ½”λ“œ 생성이 μ™„μ „νžˆ λλ‚œ ν›„
  2. μž¬λ‘œλ“œκ°€ μ‹œμž‘λ¨
  3. 항상 μ΅œμ‹  μ½”λ“œκ°€ λ‘œλ“œλ¨ βœ…
WatcherλŠ” 100ms μ „μ—­ batch νμž‰μœΌλ‘œ λ³€κ²½ 이벀트λ₯Ό λͺ¨μ€ λ’€ hmrAndSyncλ₯Ό ν•œ 번 ν˜ΈμΆœν•©λ‹ˆλ‹€. ν•œ μ‹œμ μ— 사이클은 ν•˜λ‚˜λ§Œ λŒλ„λ‘ μ§λ ¬ν™”λ©λ‹ˆλ‹€.
이것이 Sonamu HMR이 λ‹¨μˆœνžˆ β€œλΉ λ₯Έβ€ 것을 λ„˜μ–΄ μ•ˆμ „ν•˜κ³  믿을 수 μžˆλŠ” μ΄μœ μž…λ‹ˆλ‹€.

HMR μ•„ν‚€ν…μ²˜

μ£Όμš” μ»΄ν¬λ„ŒνŠΈ

Sonamu의 HMR μ‹œμŠ€ν…œμ€ μ„Έ κ°€μ§€ 핡심 μ»΄ν¬λ„ŒνŠΈλ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€: 1. @sonamu-kit/hmr-hook hot-hook을 forkν•˜μ—¬ Sonamu에 맞게 μ»€μŠ€ν„°λ§ˆμ΄μ§•ν•œ νŒ¨ν‚€μ§€μž…λ‹ˆλ‹€.
Node.js의 Module Loader APIλ₯Ό μ‚¬μš©ν•˜μ—¬ λͺ¨λ“ˆ λ‘œλ”© 과정에 κ°œμž…ν•©λ‹ˆλ‹€. 2. Dependency Tree 파일 κ°„ μ˜μ‘΄μ„±μ„ 트리 ꡬ쑰둜 μΆ”μ ν•©λ‹ˆλ‹€:
user.model.tsκ°€ λ³€κ²½λ˜λ©΄ μ˜μ‘΄ν•˜λŠ” 3개 νŒŒμΌλ„ ν•¨κ»˜ μž¬λ‘œλ“œλ©λ‹ˆλ‹€. μ˜ˆμ‹œ: user.model.tsκ°€ λ³€κ²½λ˜λ©΄ μ˜μ‘΄ν•˜λŠ” 3개 파일(user.api.ts, post.api.ts, admin.api.ts)도 ν•¨κ»˜ μž¬λ‘œλ“œλ©λ‹ˆλ‹€. 3. Watcher + Syncer syncer/watcher.ts(chokidar 글루)κ°€ 파일 이벀트λ₯Ό 4단 1μ°¨ κ°€λ“œλ‘œ κ±°λ₯Έ λ’€ 100ms μ „μ—­ batch둜 νμž‰, ν•œ λ²ˆμ— λͺ¨μ•„μ„œ Syncer의 hmrAndSyncλ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€:

HMR ν”„λ‘œμ„ΈμŠ€ 상세

κ°œλ°œμžκ°€ νŒŒμΌμ„ μˆ˜μ •ν•˜λ©΄ λ‹€μŒ 과정이 μžλ™μœΌλ‘œ μ‹€ν–‰λ©λ‹ˆλ‹€:

1. 파일 λ³€κ²½ 감지

2. λͺ¨λ“ˆ λ¬΄νš¨ν™”

λ³€κ²½λœ 파일의 ESM import μΊμ‹œλ₯Ό μ œκ±°ν•˜κ³ , Dependency Treeλ₯Ό νƒμƒ‰ν•˜μ—¬ μ˜μ‘΄ν•˜λŠ” λͺ¨λ“  파일의 μΊμ‹œλ„ μ œκ±°ν•©λ‹ˆλ‹€.

3. μžλ™ 생성 μ½”λ“œ 싱크

Entityλ‚˜ Model이 λ³€κ²½λœ 경우, Syncerκ°€ κ΄€λ ¨ μ½”λ“œλ₯Ό μžλ™ μƒμ„±ν•©λ‹ˆλ‹€:
μƒμ„±λ˜λŠ” 파일:
  • TypeScript νƒ€μž… 파일 (user.types.ts)
  • Zod μŠ€ν‚€λ§ˆ (user.zod.ts)
  • ν”„λ‘ νŠΈμ—”λ“œ Service (UserService.ts)

4. λͺ¨λ“ˆ μž¬λ‘œλ“œ

λ¬΄νš¨ν™”λœ λͺ¨λ“ˆμ€ μΊμ‹œκ°€ 제거된 μƒνƒœμ΄λ―€λ‘œ μ΅œμ‹  μ½”λ“œκ°€ λ‘œλ“œλ©λ‹ˆλ‹€.

5. API μž¬λ“±λ‘

Model 파일이 λ³€κ²½λ˜λ©΄ ν•΄λ‹Ή Model의 λͺ¨λ“  APIλ₯Ό μž¬λ“±λ‘ν•©λ‹ˆλ‹€:
μ½˜μ†” 좜λ ₯:

원본 hot-hook과의 차이점

Sonamu의 @sonamu-kit/hmr-hook은 원본 hot-hook을 forkν•˜μ—¬ λ‹€μŒκ³Ό 같이 κ°œμ„ ν–ˆμŠ΅λ‹ˆλ‹€:

1. λ³€μˆ˜ 기반 동적 Import ν—ˆμš©

SonamuλŠ” Entity 기반 ꡬ쑰라 경둜λ₯Ό λ™μ μœΌλ‘œ 생성해야 ν•˜λ―€λ‘œ 이 κΈ°λŠ₯이 ν•„μˆ˜μ μž…λ‹ˆλ‹€.

2. Boundary κ°„ 정적 Import ν—ˆμš©

Model κ°„ μ°Έμ‘°κ°€ 자주 λ°œμƒν•˜λ―€λ‘œ 정적 importλ₯Ό ν—ˆμš©ν–ˆμŠ΅λ‹ˆλ‹€.

3. νŒŒμΌμ‹œμŠ€ν…œ μ›Œμ²˜ λΉ„ν™œμ„±ν™”

원본은 자체 파일 μ›Œμ²˜λ₯Ό μ‚¬μš©ν•˜μ§€λ§Œ, SonamuλŠ” Syncer의 μ›Œμ²˜λ§Œ μ‚¬μš©ν•©λ‹ˆλ‹€:
이λ₯Ό 톡해 μ½”λ“œ 생성과 λͺ¨λ“ˆ λ¬΄νš¨ν™”μ˜ μˆœμ„œλ₯Ό μ •ν™•νžˆ μ œμ–΄ν•©λ‹ˆλ‹€.

4. ts-loaderμ™€μ˜ 톡합 κ°œμ„ 

TypeScript 컴파일 ν›„ dist/*.js κ²½λ‘œμ™€ 원본 src/*.ts 경둜의 뢈일치λ₯Ό ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.

μ„±λŠ₯ μ΅œμ ν™”

선택적 λ¬΄νš¨ν™”

λ³€κ²½λœ 파일과 κ·Έ μ˜μ‘΄μ„±λ§Œ λ¬΄νš¨ν™”ν•˜λ―€λ‘œ λΆˆν•„μš”ν•œ μž¬λ‘œλ“œλ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€:

체크섬 기반 λ³€κ²½ 감지

μ‹€μ œ λ‚΄μš©μ΄ λ³€κ²½λœ 파일만 μ²˜λ¦¬ν•©λ‹ˆλ‹€:
파일이 μ €μž₯λ˜μ—ˆμ–΄λ„ λ‚΄μš©μ΄ κ°™μœΌλ©΄ μ‹±ν¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

Graceful Shutdown 처리

싱크 μž‘μ—… 쀑 ν”„λ‘œμ„ΈμŠ€ μ’…λ£Œλ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€:
Nodemon μž¬μ‹œμž‘ μ‹œκ·Έλ„μ„ 받아도 싱크가 μ™„λ£Œλ  λ•ŒκΉŒμ§€ 20μ΄ˆκ°„ λŒ€κΈ°ν•©λ‹ˆλ‹€.

HMR ν™œμ„±ν™”

개발 λͺ¨λ“œμ—μ„œ μžλ™μœΌλ‘œ ν™œμ„±ν™”λ©λ‹ˆλ‹€:
ν™˜κ²½λ³€μˆ˜λ‘œ μ œμ–΄ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€:
ν”„λ‘œλ•μ…˜ λΉŒλ“œμ—μ„œλŠ” HMR이 μžλ™μœΌλ‘œ λΉ„ν™œμ„±ν™”λ˜λ©°, 정적 λΉŒλ“œκ°€ μƒμ„±λ©λ‹ˆλ‹€.

개발 팁

HMR μƒνƒœ 확인

λŠλ €μ§„ 것 κ°™λ‹€λ©΄

νŒŒμΌμ„ μˆ˜μ •ν–ˆλŠ”λ° 반영이 λŠλ €μ§„ 것 κ°™λ‹€λ©΄:
μ˜μ‘΄μ„±μ΄ λ„ˆλ¬΄ 많으면 import ꡬ쑰λ₯Ό κ°œμ„ ν•˜μ„Έμš”.

μž¬μ‹œμž‘μ΄ ν•„μš”ν•œ 변경사항

λ‹€μŒ νŒŒμΌλ“€μ„ μˆ˜μ •ν•˜λ©΄ μ„œλ²„λ₯Ό μž¬μ‹œμž‘ν•΄μ•Ό ν•©λ‹ˆλ‹€:
  • sonamu.config.ts - μ„€μ • 파일
  • .env - ν™˜κ²½λ³€μˆ˜
  • package.json - μ˜μ‘΄μ„±
이런 νŒŒμΌλ“€μ€ import.meta.hot?.decline()으둜 ν‘œμ‹œλ˜μ–΄ μžˆμ–΄ λ³€κ²½ μ‹œ μžλ™μœΌλ‘œ μž¬μ‹œμž‘μ„ μš”κ΅¬ν•©λ‹ˆλ‹€.