[๐Ÿค–] Turborepo๋กœ Next.js ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์ถ•: ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ ๋ฐ ์ตœ์ ํ™” ์ „๋žต

Turborepo๋ฅผ ํ™œ์šฉํ•˜์—ฌ Next.js ํ”„๋กœ์ ํŠธ๋ฅผ ๋ชจ๋…ธ๋ ˆํฌ๋กœ ๊ตฌ์„ฑํ•˜๊ณ , ๊ณต์œ  ์ปดํฌ๋„ŒํŠธ, ์œ ํ‹ธ๋ฆฌํ‹ฐ, CI/CD ์ตœ์ ํ™” ๋ฐฉ์•ˆ์„ ์‹ค๋ฌด ์˜ˆ์‹œ์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ์„ค๋ช…ํ•ด ๋“œ๋ ค์š”.

24๋ถ„
๋‹จ์–ด: 2,004๊ฐœ
๊ฒŒ์‹œ๊ธ€ ์ธ๋„ค์ผ
์ •๋ณด

๐Ÿค– ์ด ํฌ์ŠคํŒ…์€ Gemini 2.5 Flash AI๊ฐ€ ์ž‘์„ฑํ–ˆ์–ด์š”.
๋‚ด์šฉ์˜ ์ •ํ™•์„ฑ์„ ์œ„ํ•ด ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณค์ง€๋งŒ, ์‹ค๋ฌด ์ ์šฉ ์ „ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”.

์œ ์šฉํ•œ ํŒ

Turborepo๋ฅผ ํ™œ์šฉํ•ด Next.js ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ตฌ์ถ•ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ๊ณ , ๊ณต์œ  ์ฝ”๋“œ๋ฒ ์ด์Šค, ๋นŒ๋“œ ์ตœ์ ํ™”, ๊ทธ๋ฆฌ๊ณ  CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์„ฑ ์ „๋žต์„ ์ตํ˜€๋ณด์„ธ์š”.

์•ˆ๋…•ํ•˜์„ธ์š”, 10๋…„ ์ด์ƒ ๊ฐœ๋ฐœ ํ˜„์žฅ์—์„œ ํ’€์Šคํƒ ๊ฐœ๋ฐœ์ž๋กœ ์ผํ•ด์˜จ ๋ธ”๋ฃจ์˜ˆ์š”. ํ˜„๋Œ€ ์›น ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์€ ์ ์  ๋ณต์žกํ•ด์ง€๊ณ , ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋ŸฐํŠธ์—”๋“œ์™€ ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค, ๊ทธ๋ฆฌ๊ณ  ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์ง€๊ณ  ์žˆ์–ด์š”.
์ด๋Ÿฌํ•œ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž์ถฐ '๋ชจ๋…ธ๋ ˆํฌ(Monorepo)' ์•„ํ‚คํ…์ฒ˜๋Š” ๋งŽ์€ ํŒ€์—์„œ ์„ ํƒํ•˜๋Š” ๋Œ€์•ˆ์ด ๋˜๊ณ  ์žˆ๋‹ต๋‹ˆ๋‹ค. ํŠนํžˆ Next.js ํ”„๋กœ์ ํŠธ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ๋ชจ๋…ธ๋ ˆํฌ์˜ ์žฅ์ ์€ ๋”์šฑ ๋น›์„ ๋ฐœํ•˜์ฃ .
์˜ค๋Š˜์€ Next.js ํ”„๋กœ์ ํŠธ๋ฅผ Turborepo์™€ ํ•จ๊ป˜ ๋ชจ๋…ธ๋ ˆํฌ๋กœ ๊ตฌ์ถ•ํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๋Š” ์‹ค์งˆ์ ์ธ ๋ฐฉ๋ฒ•์„ ์ž์„ธํžˆ ์•Œ๋ ค๋“œ๋ฆด๊ฒŒ์š”.

๐Ÿค” ์™œ ๋ชจ๋…ธ๋ ˆํฌ์™€ Turborepo์ธ๊ฐ€์š”?

0๏ธโƒฃ ๋ชจ๋…ธ๋ ˆํฌ์˜ ๋ถ€์ƒ๊ณผ ์žฅ์ 

๋ชจ๋…ธ๋ ˆํฌ๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ๋ฅผ ํ•˜๋‚˜์˜ Git ์ €์žฅ์†Œ์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ๋งํ•ด์š”. ์ฒ˜์Œ์—๋Š” ๋‹ค์†Œ ์ƒ์†Œํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๋‚˜ ์—ฌ๋Ÿฌ ๊ด€๋ จ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ•๋ ฅํ•œ ์žฅ์ ๋“ค์„ ์ œ๊ณตํ•œ๋‹ต๋‹ˆ๋‹ค.

  • ์ฝ”๋“œ ๊ณต์œ ์˜ ์šฉ์ด์„ฑ: ๊ณตํ†ต UI ์ปดํฌ๋„ŒํŠธ, ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜, ํƒ€์ž… ์ •์˜ ๋“ฑ์„ ์‰ฝ๊ฒŒ ๊ณต์œ ํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ์ค‘๋ณต ์ฝ”๋“œ๋ฅผ ์ค„์ด๊ณ  ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋˜์ฃ .
  • ๋‹จ์ผ ๋ฒ„์ „ ๊ด€๋ฆฌ: ๋ชจ๋“  ํ”„๋กœ์ ํŠธ๊ฐ€ ๋™์ผํ•œ ๋ฒ„์ „์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๊ฐ•์ œํ•˜๊ฑฐ๋‚˜, ์ ์–ด๋„ ์–ด๋–ค ๋ฒ„์ „์ด ์–ด๋””์— ์‚ฌ์šฉ๋˜๋Š”์ง€ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•˜๊ธฐ ์‰ฌ์›Œ์š”. ๋ฒ„์ „ ๋ถˆ์ผ์น˜๋กœ ์ธํ•œ ๋ฌธ์ œ๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.
  • ํ†ตํ•ฉ๋œ ๊ฐœ๋ฐœ ๊ฒฝํ—˜: ๋ชจ๋“  ์ฝ”๋“œ๊ฐ€ ํ•œ๊ณณ์— ์žˆ์–ด ๊ฐœ๋ฐœ์ž๊ฐ€ ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ์˜ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ํƒ์ƒ‰ํ•˜๊ณ  ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋˜ํ•œ, ํ†ต์ผ๋œ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฆฐํŠธ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ ์šฉํ•˜์—ฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์ฃ .
  • ์›์ž์ (Atomic) ๋ณ€๊ฒฝ: ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ์— ๊ฑธ์ณ ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ, ํ•˜๋‚˜์˜ ์ปค๋ฐ‹์œผ๋กœ ๋ชจ๋“  ๊ด€๋ จ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ํ•จ๊ป˜ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๋‹จ์ˆœํ™”ํ•˜๊ณ  ๋ฐฐํฌ ์˜ค๋ฅ˜ ๊ฐ€๋Šฅ์„ฑ์„ ์ค„์—ฌ์ค€๋‹ต๋‹ˆ๋‹ค.

1๏ธโƒฃ ๊ธฐ์กด ๋ฉ€ํ‹ฐ๋ ˆํฌ์˜ ํ•œ๊ณ„

๋ชจ๋…ธ๋ ˆํฌ์˜ ์žฅ์ ์€ ๊ธฐ์กด ๋ฉ€ํ‹ฐ๋ ˆํฌ(Multi-repo) ๋ฐฉ์‹์˜ ํ•œ๊ณ„์™€ ๋น„๊ตํ•˜๋ฉด ๋”์šฑ ๋ช…ํ™•ํ•ด์ ธ์š”. ๋ฉ€ํ‹ฐ๋ ˆํฌ๋Š” ๊ฐ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ณ„๋„์˜ Git ์ €์žฅ์†Œ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ธ๋ฐ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ๋“ค์ด ํ”ํžˆ ๋ฐœ์ƒํ•˜๊ณค ํ–ˆ์–ด์š”.

  • ์ฝ”๋“œ ์ค‘๋ณต: ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ์—์„œ ๋™์ผํ•œ ๊ธฐ๋Šฅ์ด๋‚˜ UI ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ•  ๋•Œ, ์ด๋ฅผ ๋ณต์‚ฌํ•˜๊ฑฐ๋‚˜ ๋ณ„๋„์˜ ํŒจํ‚ค์ง€๋กœ ๋งŒ๋“ค์–ด ๊ด€๋ฆฌํ•ด์•ผ ํ–ˆ์–ด์š”. ํŒจํ‚ค์ง€ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋„ ๋ณต์žกํ•ด์ง€๊ณ ์š”.
  • ๋ฒ„์ „ ๋ถˆ์ผ์น˜: ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ƒˆ ๋ฒ„์ „์ด ๋‚˜์™”์„ ๋•Œ, ๋ชจ๋“  ๊ด€๋ จ ํ”„๋กœ์ ํŠธ์—์„œ ์ˆ˜๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ–ˆ์–ด์š”. ์ด ๊ณผ์ •์—์„œ ๋ฒ„์ „ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ , ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฒ„๊ทธ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ์—ˆ์ฃ .
  • ๋ณต์žกํ•œ CI/CD: ๊ฐ ๋ ˆํฌ๋งˆ๋‹ค CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•ด์•ผ ํ•ด์„œ ๊ด€๋ฆฌ ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•˜๊ณ , ์—ฌ๋Ÿฌ ๋ ˆํฌ์— ๊ฑธ์นœ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์› ์–ด์š”.

2๏ธโƒฃ Turborepo๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๊ฐ€์น˜

๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ๊ตฌ์ถ•ํ•  ๋•Œ, ์ˆ˜๋งŽ์€ ํ”„๋กœ์ ํŠธ์™€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ ค๋ฉด ๊ฐ•๋ ฅํ•œ ๋นŒ๋“œ ์‹œ์Šคํ…œ์ด ํ•„์š”ํ•ด์š”. ์ด๋•Œ Turborepo๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ต๋‹ˆ๋‹ค.
Turborepo๋Š” Vercel์—์„œ ๋งŒ๋“  ๊ณ ์„ฑ๋Šฅ ๋นŒ๋“œ ์‹œ์Šคํ…œ์œผ๋กœ, ๋ชจ๋…ธ๋ ˆํฌ ํ™˜๊ฒฝ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ•ต์‹ฌ ๊ฐ€์น˜๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•ด์ค˜์š”.

  • ์ฆ๋ถ„ ๋นŒ๋“œ(Incremental Builds): ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๋นŒ๋“œํ•˜๊ณ , ์ด์ „์— ๋นŒ๋“œํ–ˆ๋˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์บ์‹ฑํ•˜์—ฌ ์žฌ์‚ฌ์šฉํ•ด์š”. ์ด๋Š” ๋นŒ๋“œ ์‹œ๊ฐ„์„ ํš๊ธฐ์ ์œผ๋กœ ๋‹จ์ถ•์‹œ์ผœ ์ค€๋‹ต๋‹ˆ๋‹ค.
  • ๋ณ‘๋ ฌ ์‹คํ–‰(Parallel Execution): ์˜์กด์„ฑ์ด ์—†๋Š” ์ž‘์—…๋“ค์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜์—ฌ ์ „์ฒด ๋นŒ๋“œ ์‹œ๊ฐ„์„ ์ค„์—ฌ์ค˜์š”. CPU ์ฝ”์–ด๋ฅผ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๋Š” ๊ฑฐ์ฃ .
  • ์›๊ฒฉ ์บ์‹ฑ(Remote Caching): ๋กœ์ปฌ ์บ์‹œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์›๊ฒฉ ์บ์‹œ๋ฅผ ์ง€์›ํ•˜์—ฌ ํŒ€์› ๊ฐ„ ๋˜๋Š” CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ฐ„ ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์–ด์š”. ํ•œ ๋ฒˆ ๋นŒ๋“œ๋œ ๊ฒฐ๊ณผ๋Š” ์–ด๋””์„œ๋“  ์žฌ์‚ฌ์šฉ๋œ๋‹ต๋‹ˆ๋‹ค.
  • ๊ฐ„๊ฒฐํ•œ ์„ค์ •: turbo.json ํŒŒ์ผ ํ•˜๋‚˜๋กœ ๋ชจ๋“  ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ ์–ธ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ์„ค์ •์ด ๋งค์šฐ ๊ฐ„๊ฒฐํ•ด์š”.

๐Ÿš€ Turborepo & Next.js ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์ถ• ๊ฐ€์ด๋“œ

์ด์ œ Turborepo๋ฅผ ํ™œ์šฉํ•˜์—ฌ Next.js ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ์‹ค์ œ๋กœ ๊ตฌ์ถ•ํ•˜๋Š” ๊ณผ์ •์„ ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณผ๊ฒŒ์š”. ์—ฌ๊ธฐ์„œ๋Š” pnpm์„ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ €๋กœ ์‚ฌ์šฉํ•  ๊ฑฐ์˜ˆ์š”. pnpm์€ ์›Œํฌ์ŠคํŽ˜์ด์Šค(workspace) ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋ฉฐ, ๋ชจ๋…ธ๋ ˆํฌ ํ™˜๊ฒฝ์—์„œ ํŠนํžˆ ํšจ์œจ์ ์ธ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฅผ ์ œ๊ณตํ•œ๋‹ต๋‹ˆ๋‹ค.

0๏ธโƒฃ ์ดˆ๊ธฐ ํ”„๋กœ์ ํŠธ ์„ค์ •

๋จผ์ €, ๋ชจ๋…ธ๋ ˆํฌ์˜ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  pnpm ์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์„ค์ •ํ•ด ์ค„๊ฒŒ์š”.

mkdir my-nextjs-monorepo cd my-nextjs-monorepo pnpm init

pnpm init ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๋ฉด package.json ํŒŒ์ผ์ด ์ƒ์„ฑ๋ผ์š”. ์ด์ œ ์ด ํŒŒ์ผ์— workspaces ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ชจ๋…ธ๋ ˆํฌ ๋‚ด์˜ ์•ฑ(apps)๊ณผ ํŒจํ‚ค์ง€(packages)๋“ค์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ค„๊ฒŒ์š”.

// package.json (๋ฃจํŠธ) { "name": "my-nextjs-monorepo", "version": "1.0.0", "private": true, // ์ด ๋ ˆํฌ๋Š” publishํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— private: true๋กœ ์„ค์ •ํ•ด์š”. "scripts": { "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", "test": "turbo run test" }, "workspaces": [ "apps/*", "packages/*" ], "devDependencies": { "turbo": "latest" } }

private: true๋Š” ์ด ๋ฃจํŠธ ํ”„๋กœ์ ํŠธ๊ฐ€ npm์— ๋ฐฐํฌ๋˜์ง€ ์•Š๋„๋ก ํ•˜๋Š” ์„ค์ •์ด์—์š”.
workspaces ํ•„๋“œ๋Š” apps/ ๋””๋ ‰ํ† ๋ฆฌ์™€ packages/ ๋””๋ ‰ํ† ๋ฆฌ ์•„๋ž˜์˜ ๋ชจ๋“  ์„œ๋ธŒ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์›Œํฌ์ŠคํŽ˜์ด์Šค๋กœ ์ธ์‹ํ•˜๋„๋ก ์•Œ๋ ค์ค€๋‹ต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  turbo๋ฅผ ๊ฐœ๋ฐœ ์˜์กด์„ฑ์œผ๋กœ ์„ค์น˜ํ•ด ์ฃผ์„ธ์š”.

pnpm install

์ด์ œ apps์™€ packages ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•ด ์ค„๊ฒŒ์š”.

mkdir apps packages

1๏ธโƒฃ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ

์ผ๋ฐ˜์ ์ธ Turborepo ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์š”.

my-nextjs-monorepo/ โ”œโ”€โ”€ apps/ # ๊ฐœ๋ณ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜(Next.js ์•ฑ, Express API ๋“ฑ)์ด ์œ„์น˜ํ•ด์š”. โ”‚ โ”œโ”€โ”€ web/ # Next.js ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ โ”‚ โ””โ”€โ”€ docs/ # ๋˜ ๋‹ค๋ฅธ Next.js ๋ฌธ์„œ ์‚ฌ์ดํŠธ โ”œโ”€โ”€ packages/ # ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(UI ์ปดํฌ๋„ŒํŠธ, ์œ ํ‹ธ๋ฆฌํ‹ฐ, ์„ค์ • ๋“ฑ)๊ฐ€ ์œ„์น˜ํ•ด์š”. โ”‚ โ”œโ”€โ”€ ui/ # ๊ณต์œ  UI ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ โ”‚ โ”œโ”€โ”€ config/ # ๊ณต์œ  ESLint/TypeScript ์„ค์ • โ”‚ โ””โ”€โ”€ utils/ # ๊ณต์œ  ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜ โ”œโ”€โ”€ package.json # ๋ฃจํŠธ package.json (์›Œํฌ์ŠคํŽ˜์ด์Šค ์„ค์ •) โ”œโ”€โ”€ pnpm-workspace.yaml # pnpm ์›Œํฌ์ŠคํŽ˜์ด์Šค ์„ค์ • ํŒŒ์ผ (์„ ํƒ ์‚ฌํ•ญ, package.json์— ์„ค์ •ํ–ˆ๋‹ค๋ฉด ํ•„์š” ์—†์–ด์š”) โ””โ”€โ”€ turbo.json # Turborepo ์„ค์ • ํŒŒ์ผ

2๏ธโƒฃ ๊ณต์œ  ์ปดํฌ๋„ŒํŠธ/์œ ํ‹ธ๋ฆฌํ‹ฐ ํŒจํ‚ค์ง€ ์ƒ์„ฑ

๋จผ์ €, packages/ui๋ผ๋Š” ๊ณต์œ  UI ์ปดํฌ๋„ŒํŠธ ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”. ๊ฐ„๋‹จํ•œ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ํฌํ•จ์‹œํ‚ฌ ๊ฑฐ์˜ˆ์š”.

mkdir packages/ui cd packages/ui pnpm init

packages/ui/package.json ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด ์ฃผ์„ธ์š”.

// packages/ui/package.json { "name": "@repo/ui", // @repo/ ์ ‘๋‘์‚ฌ๋Š” ๋ชจ๋…ธ๋ ˆํฌ ๋‚ด ํŒจํ‚ค์ง€์ž„์„ ๋‚˜ํƒ€๋‚ด๋Š” ์ผ๋ฐ˜์ ์ธ ์ปจ๋ฒค์…˜์ด์—์š”. "version": "0.0.0", "main": "./src/index.tsx", // ๋ฉ”์ธ ์ง„์ž…์  "types": "./src/index.tsx", // ํƒ€์ž… ์ง„์ž…์  "license": "MIT", "scripts": { "lint": "eslint .", "generate:component": "turbo gen react-component" }, "devDependencies": { "@repo/eslint-config": "workspace:*", // ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋‚ด์˜ ๋‹ค๋ฅธ ํŒจํ‚ค์ง€๋ฅผ ์ฐธ์กฐํ•ด์š”. "@types/react": "^18.2.37", "eslint": "^8.53.0", "react": "^18.2.0", "typescript": "^5.2.2" }, "peerDependencies": { "react": "^18.2.0" } }

@repo/ui๋Š” ์ด ํŒจํ‚ค์ง€์˜ ์ด๋ฆ„์ด์—์š”. workspace:*๋Š” pnpm์—๊ฒŒ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋‚ด์˜ @repo/eslint-config ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ์•Œ๋ ค์ค€๋‹ต๋‹ˆ๋‹ค.
์ด์ œ ๊ฐ„๋‹จํ•œ Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”. packages/ui/src/Button.tsx ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด ์ฃผ์„ธ์š”.

// packages/ui/src/Button.tsx import * as React from "react"; interface ButtonProps { children: React.ReactNode; onClick?: () => void; className?: string; } export function Button({ children, onClick, className }: ButtonProps) { return ( <button onClick={onClick} className={`px-4 py-2 rounded-md font-semibold ${className || ''}`} style={{ backgroundColor: '#0070f3', color: 'white', border: 'none', cursor: 'pointer' }} > {children} </button> ); }

๊ทธ๋ฆฌ๊ณ  packages/ui/src/index.tsx์—์„œ ์ด ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚ด๋ณด๋‚ด๋„๋ก ์„ค์ •ํ•ด ์ฃผ์„ธ์š”.

// packages/ui/src/index.tsx export * from "./Button";

๋งˆ์ง€๋ง‰์œผ๋กœ, ๊ณต์œ  tsconfig.json๊ณผ eslint-config๋ฅผ packages/config์— ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ์š”.

mkdir packages/config cd packages/config pnpm init

packages/config/package.json์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•ด์š”.

// packages/config/package.json { "name": "@repo/config", "version": "0.0.0", "private": true, "main": "index.js", "license": "MIT", "publishConfig": { "access": "public" } }

๊ทธ๋ฆฌ๊ณ  packages/config/eslint-config/library.js ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์Œ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ด ์ฃผ์„ธ์š”. (์‹ค์ œ ESLint ์„ค์ •์€ ํ”„๋กœ์ ํŠธ ์š”๊ตฌ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์–ด์š”.)

// packages/config/eslint-config/library.js module.exports = { extends: [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier" ], plugins: ["@typescript-eslint"], parser: "@typescript-eslint/parser", parserOptions: { ecmaVersion: "latest", sourceType: "module" }, env: { node: true, browser: true }, rules: { // ์—ฌ๊ธฐ์— ํ”„๋กœ์ ํŠธ์— ๋งž๋Š” ๊ทœ์น™์„ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”. } };

3๏ธโƒฃ Next.js ์•ฑ์—์„œ ๊ณต์œ  ํŒจํ‚ค์ง€ ์‚ฌ์šฉํ•˜๊ธฐ

์ด์ œ apps/web์ด๋ผ๋Š” Next.js ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ƒ์„ฑํ•˜๊ณ , ์œ„์—์„œ ๋งŒ๋“  ๊ณต์œ  ui ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ์š”. Turborepo๋Š” Next.js ์•ฑ ํ…œํ”Œ๋ฆฟ์„ ์ œ๊ณตํ•˜๋‹ˆ, ์ด๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ํŽธ๋ฆฌํ•ด์š”.

pnpm dlx create-next-app@latest apps/web --ts --eslint --tailwind --app cd apps/web pnpm install @repo/ui @repo/config

apps/web/package.json์„ ์—ด์–ด๋ณด๋ฉด @repo/ui์™€ @repo/config๊ฐ€ ์˜์กด์„ฑ์œผ๋กœ ์ถ”๊ฐ€๋˜์–ด ์žˆ์„ ๊ฑฐ์˜ˆ์š”. pnpm์ด ์ž๋™์œผ๋กœ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋‚ด์˜ ํŒจํ‚ค์ง€๋ฅผ ๋งํฌํ•ด ์ค€๋‹ต๋‹ˆ๋‹ค.

Next.js ์•ฑ์—์„œ ์›Œํฌ์ŠคํŽ˜์ด์Šค ๋‚ด์˜ ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด next.config.js์— transpilePackages ์„ค์ •์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ๋งŽ์•„์š”. ํŠนํžˆ TypeScript๋‚˜ Tailwind CSS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณต์œ  ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ ํ•„์ˆ˜์ ์ด์—์š”.

// apps/web/next.config.js /** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ["@repo/ui", "@repo/config"], }; module.exports = nextConfig;

์ด์ œ apps/web/app/page.tsx์—์„œ ๊ณต์œ  Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ์š”.

// apps/web/app/page.tsx import { Button } from "@repo/ui"; export default function HomePage() { return ( <main className="flex min-h-screen flex-col items-center justify-between p-24"> <h1>Welcome to Next.js Monorepo!</h1> <Button onClick={() => alert("Hello from shared UI!")}> Click Me (Shared UI) </Button> </main> ); }

์ด์ œ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ๋กœ ๋Œ์•„์™€์„œ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์„ธ์š”.

cd ../.. pnpm run dev

http://localhost:3000์œผ๋กœ ์ ‘์†ํ•˜๋ฉด Next.js ์•ฑ๊ณผ ๊ณต์œ  Button ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ž˜ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.

โš™๏ธ Turborepo ํ•ต์‹ฌ ๊ธฐ๋Šฅ ํ™œ์šฉ ๋ฐ ์ตœ์ ํ™”

Turborepo์˜ ์ง„์ •ํ•œ ํž˜์€ turbo.json ์„ค์ •์„ ํ†ตํ•ด ๋ฐœํœ˜๋œ๋‹ต๋‹ˆ๋‹ค. ์ด ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์„ ์ •์˜ํ•˜๊ณ , ์บ์‹ฑ ์ „๋žต์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”.

0๏ธโƒฃ ์บ์‹ฑ๊ณผ ๋ณ‘๋ ฌ ์‹คํ–‰์œผ๋กœ ๋นŒ๋“œ ์†๋„ ํ–ฅ์ƒ

๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— turbo.json ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•ด ์ฃผ์„ธ์š”.

// turbo.json { "$schema": "https://turbo.build/schema.json", "pipeline": { "build": { "dependsOn": ["^build"], // ์˜์กด์„ฑ ๊ด€๊ณ„์— ์žˆ๋Š” ํŒจํ‚ค์ง€์˜ ๋นŒ๋“œ๊ฐ€ ๋จผ์ € ์™„๋ฃŒ๋˜์–ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ด์š”. "outputs": [".next/**", "dist/**"] }, "lint": { "dependsOn": ["^lint"] }, "dev": { "cache": false, // ๊ฐœ๋ฐœ ์„œ๋ฒ„๋Š” ์บ์‹œํ•˜์ง€ ์•Š์•„์š”. "persistent": true // ํ”„๋กœ์„ธ์Šค๊ฐ€ ๊ณ„์† ์‹คํ–‰๋˜๋„๋ก ํ•ด์š”. }, "test": { "dependsOn": ["^build"], "outputs": ["coverage/**"] } } }
  • pipeline: Turborepo๊ฐ€ ์‹คํ–‰ํ•  ์ž‘์—…(task)๋“ค์„ ์ •์˜ํ•ด์š”.
  • build: ๊ฐ ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ build ์Šคํฌ๋ฆฝํŠธ์— ๋Œ€ํ•œ ์„ค์ •์ด์—์š”.
    • dependsOn: ^build๋Š” ํ˜„์žฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ ์˜์กด์„ฑ(dependency)์— ํ•ด๋‹นํ•˜๋Š” ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ build ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋จผ์ € ์‹คํ–‰๋˜์–ด์•ผ ํ•จ์„ ์˜๋ฏธํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, apps/web์„ ๋นŒ๋“œํ•  ๋•Œ packages/ui๊ฐ€ ๋จผ์ € ๋นŒ๋“œ๋˜๋„๋ก ํ•˜๋Š” ๊ฑฐ์ฃ .
    • outputs: ์ด ์ž‘์—…์˜ ๊ฒฐ๊ณผ๋ฌผ(์บ์‹œํ•  ํŒŒ์ผ)์„ ์ง€์ •ํ•ด์š”. .next/**๋Š” Next.js ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์ด๊ณ , dist/**๋Š” ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์ผ ์ˆ˜ ์žˆ์–ด์š”.
  • dev: ๊ฐœ๋ฐœ ์„œ๋ฒ„๋Š” ์ง€์†์ ์œผ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ cache: false์™€ persistent: true๋กœ ์„ค์ •ํ•ด์š”.

์ด ์„ค์ •์„ ํ†ตํ•ด Turborepo๋Š” ๋ณ€๊ฒฝ๋œ ํŒŒ์ผ๋งŒ ๊ฐ์ง€ํ•˜๊ณ , ์ด์ „์— ๋นŒ๋“œ๋œ ๊ฒฐ๊ณผ๋Š” ์บ์‹œ์—์„œ ๊ฐ€์ ธ์™€ ์žฌ์‚ฌ์šฉํ•ด์š”. ์—ฌ๋Ÿฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๋™์‹œ์— ๋นŒ๋“œํ•  ๋•Œ๋„ ์˜์กด์„ฑ ํŠธ๋ฆฌ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•˜๋ฏ€๋กœ, ๋นŒ๋“œ ์‹œ๊ฐ„์ด ๋†€๋ผ์šธ ์ •๋„๋กœ ๋‹จ์ถ•๋œ๋‹ต๋‹ˆ๋‹ค.

์œ ์šฉํ•œ ํŒ

์›๊ฒฉ ์บ์‹ฑ(Remote Caching) ํ™œ์„ฑํ™”ํ•˜๊ธฐ
Turborepo๋Š” Vercel ๊ณ„์ •๊ณผ ์—ฐ๋™ํ•˜์—ฌ ์›๊ฒฉ ์บ์‹ฑ์„ ์ง€์›ํ•ด์š”. turbo login ๋ช…๋ น์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๊ณ  turbo link๋กœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด, ํŒ€์› ๊ฐ„ ๋˜๋Š” CI/CD ํ™˜๊ฒฝ์—์„œ ๋นŒ๋“œ ์บ์‹œ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ด๋Š” ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ํŒ€์ด๋‚˜ ๋ณต์žกํ•œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ๋นŒ๋“œ ์‹œ๊ฐ„์„ ํš๊ธฐ์ ์œผ๋กœ ์ค„์—ฌ์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด๋ž๋‹ˆ๋‹ค.

1๏ธโƒฃ ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •์œผ๋กœ ์ž‘์—… ์ž๋™ํ™”

turbo.json์˜ pipeline์€ build ์™ธ์—๋„ lint, test, dev ๋“ฑ ๋‹ค์–‘ํ•œ ์ž‘์—…์„ ์ •์˜ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, lint ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ •์˜ํ•˜๋ฉด ๋ชจ๋“  ์›Œํฌ์ŠคํŽ˜์ด์Šค์—์„œ ์ผ๊ด€๋œ ๋ฆฐํŒ…์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

๋ฃจํŠธ package.json์˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‹ค์‹œ ํ•œ๋ฒˆ ํ™•์ธํ•ด๋ณผ๊ฒŒ์š”.

// package.json (๋ฃจํŠธ) { "scripts": { "build": "turbo run build", "dev": "turbo run dev", "lint": "turbo run lint", "test": "turbo run test" }, // ... }

์ด์ œ ๋ฃจํŠธ์—์„œ pnpm run lint๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, Turborepo๋Š” turbo.json์— ์ •์˜๋œ lint ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ชจ๋“  ์›Œํฌ์ŠคํŽ˜์ด์Šค์˜ lint ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์‹คํ–‰ํ•ด์š”. ๋งŒ์•ฝ ํŠน์ • ์›Œํฌ์ŠคํŽ˜์ด์Šค์— lint ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์—†๋‹ค๋ฉด, ํ•ด๋‹น ์›Œํฌ์ŠคํŽ˜์ด์Šค๋Š” ๊ฑด๋„ˆ๋›ฐ๊ฒŒ ๋œ๋‹ต๋‹ˆ๋‹ค.

2๏ธโƒฃ CI/CD ํ™˜๊ฒฝ์—์„œ์˜ Turborepo

CI/CD ํŒŒ์ดํ”„๋ผ์ธ์—์„œ Turborepo๋Š” ๋น›์„ ๋ฐœํ•ด์š”. ์›๊ฒฉ ์บ์‹ฑ๊ณผ ์ฆ๋ถ„ ๋นŒ๋“œ ๋•๋ถ„์— CI/CD ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, GitHub Actions์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Turborepo๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.

# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v3 with: version: 8 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Cache Turborepo build outputs uses: actions/cache@v4 with: path: .turbo key: ${{ runner.os }}-turbo-${{ github.sha }} restore-keys: | ${{ runner.os }}-turbo- - name: Build all projects run: pnpm run build - name: Lint all projects run: pnpm run lint - name: Test all projects run: pnpm run test

์ด ์›Œํฌํ”Œ๋กœ์šฐ๋Š” Turborepo์˜ ์บ์‹ฑ ๋””๋ ‰ํ† ๋ฆฌ(.turbo)๋ฅผ GitHub Actions ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ๋ณต์›ํ•˜์—ฌ, ์ด์ „ ๋นŒ๋“œ์˜ ๊ฒฐ๊ณผ๋ฌผ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ต๋‹ˆ๋‹ค.
์ด๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์€ ํ”„๋กœ์ ํŠธ๋Š” ๋‹ค์‹œ ๋นŒ๋“œ๋˜๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ, CI/CD ์‹คํ–‰ ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์–ด์š”.

๐Ÿ“ ์ •๋ฆฌํ•˜๋ฉฐ

Turborepo์™€ ํ•จ๊ป˜ Next.js ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์€ ์ดˆ๊ธฐ ์„ค์ •์— ์•ฝ๊ฐ„์˜ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•˜์ง€๋งŒ, ์žฅ๊ธฐ์ ์œผ๋กœ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ๊ณผ ์ฝ”๋“œ ๊ด€๋ฆฌ ํšจ์œจ์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œ์ผœ์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ์ „๋žต์ด์—์š”.
๊ณต์œ  ์ฝ”๋“œ๋ฒ ์ด์Šค์˜ ์ด์ , Turborepo์˜ ๊ณ ์„ฑ๋Šฅ ๋นŒ๋“œ ์‹œ์Šคํ…œ, ๊ทธ๋ฆฌ๊ณ  CI/CD ์ตœ์ ํ™” ๊ฐ€๋Šฅ์„ฑ๊นŒ์ง€, ๋ชจ๋…ธ๋ ˆํฌ๋Š” ํ˜„๋Œ€ ์›น ๊ฐœ๋ฐœ ํŒ€์— ํ•„์ˆ˜์ ์ธ ์•„ํ‚คํ…์ฒ˜๋ผ๊ณ  ์ƒ๊ฐํ•ด์š”.

0๏ธโƒฃ ํ•ต์‹ฌ ์š”์•ฝ

  • ๋ชจ๋…ธ๋ ˆํฌ: ์ฝ”๋“œ ๊ณต์œ , ์ผ๊ด€์„ฑ ์œ ์ง€, ๋‹จ์ผ ๋ฒ„์ „ ๊ด€๋ฆฌ, ์›์ž์  ๋ณ€๊ฒฝ์˜ ์ด์ ์„ ์ œ๊ณตํ•ด์š”.
  • Turborepo: ์ฆ๋ถ„ ๋นŒ๋“œ, ๋ณ‘๋ ฌ ์‹คํ–‰, ์›๊ฒฉ ์บ์‹ฑ์„ ํ†ตํ•ด ๋ชจ๋…ธ๋ ˆํฌ์˜ ๋นŒ๋“œ ์†๋„๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ๊ฐœ์„ ํ•ด์š”.
  • ๊ตฌ์ถ•: pnpm ์›Œํฌ์ŠคํŽ˜์ด์Šค์™€ turbo.json ์„ค์ •์„ ํ†ตํ•ด Next.js ์•ฑ๊ณผ ๊ณต์œ  ํŒจํ‚ค์ง€๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • ์ตœ์ ํ™”: turbo.json์˜ pipeline ์„ค์ •๊ณผ CI/CD์—์„œ์˜ ์บ์‹ฑ ํ™œ์šฉ์œผ๋กœ ๊ฐœ๋ฐœ ๋ฐ ๋ฐฐํฌ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค.

1๏ธโƒฃ ๋” ๋‚˜์•„๊ฐ€๊ธฐ

์˜ค๋Š˜ ๋‹ค๋ฃฌ ๋‚ด์šฉ์€ Turborepo ๋ชจ๋…ธ๋ ˆํฌ์˜ ๊ธฐ๋ณธ์ ์ธ ์‹œ์ž‘์ ์ด์—์š”. ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ถ€๋ถ„๋“ค์„ ๋” ๊นŠ์ด ํƒ๊ตฌํ•ด ๋ณผ ์ˆ˜ ์žˆ์–ด์š”.

  • ๋” ๋ณต์žกํ•œ turbo.json ํŒŒ์ดํ”„๋ผ์ธ: ํŠน์ • ์กฐ๊ฑด์— ๋”ฐ๋ผ ์ž‘์—…์„ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, ๋” ์„ธ๋ฐ€ํ•œ ์บ์‹ฑ ์ „๋žต์„ ์ ์šฉํ•ด ๋ณด์„ธ์š”.
  • Storybook ํ†ตํ•ฉ: ๊ณต์œ  UI ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— Storybook์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๊ฐœ๋ฐœ ๋ฐ ๋ฌธ์„œํ™”๋ฅผ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”.
  • Nx ๋˜๋Š” Lerna์™€ ๋น„๊ต: Turborepo ์™ธ์—๋„ Nx, Lerna ๋“ฑ ๋‹ค์–‘ํ•œ ๋ชจ๋…ธ๋ ˆํฌ ๋„๊ตฌ๋“ค์ด ์žˆ์–ด์š”. ๊ฐ ๋„๊ตฌ์˜ ์žฅ๋‹จ์ ์„ ๋น„๊ตํ•˜์—ฌ ํŒ€์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๊ฒƒ์„ ์„ ํƒํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜๋‹ต๋‹ˆ๋‹ค.
  • Docker ์ปจํ…Œ์ด๋„ˆํ™”: ๋ชจ๋…ธ๋ ˆํฌ ๋‚ด์˜ ๊ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ Docker ์ปจํ…Œ์ด๋„ˆ๋กœ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” ์ „๋žต์„ ์„ธ์›Œ๋ณด์„ธ์š”.

๋ชจ๋…ธ๋ ˆํฌ๋Š” ํŒ€์˜ ๊ทœ๋ชจ์™€ ํ”„๋กœ์ ํŠธ์˜ ๋ณต์žก์„ฑ์— ๋”ฐ๋ผ ๋‹ค์–‘ํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ฐœ์ „ํ•  ์ˆ˜ ์žˆ๋Š” ์•„ํ‚คํ…์ฒ˜์˜ˆ์š”. ์ด ๊ธ€์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ฐœ๋ฐœ ์—ฌ์ •์— ์‹ค์งˆ์ ์ธ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ผ์š”.
๊ถ๊ธˆํ•œ ์ ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ์งˆ๋ฌธํ•ด ์ฃผ์„ธ์š”!

๐Ÿ“ฎ ์ฐธ๊ณ 

์—ฐ๊ด€๋œ ํฌ์ŠคํŠธ