Vite Monorepo μμ Next + Turborepo λ₯Ό μ μ©νκ² λλ©΄μ μΆκ°νκ±°λ μμ λ λΆλΆ, λ³κ²½ν λΆλΆμ μ μ΄λ³΄κ³ μ νλ€.
κΈ°μ‘΄ Vite λ₯Ό μ¬μ©ν λλ Blog, Portfolio ν¨ν€μ§μ core κ³΅μ© ν¨ν€μ§λ‘ λλμ΄μ Έ μμλλ°, NextJS λ‘ λ§μ΄κ·Έλ μ΄μ νλ©΄μ Blog, Portfolio μ apps μ components, hooks μ κ³΅μ© ν¨ν€μ§λ‘ λ¬λΌμ§κ² λμμ΅λλ€.
sh
// λ³κ²½ μ packages βββ core βββ blog βββ portfolio // λ³κ²½ ν monorepo β apps βΒ Β βββ blog βΒ Β βββ portfolio βββ packages Β Β βββ components Β Β βββ hooks
λν κΈ°μ‘΄μ yarn workspaces λ₯Ό μ¬μ©νλ€λ©΄ μ΄λ²μλ pnpm κ³Ό Turborepo λ₯Ό μ¬μ©ν΄μ monorepo λ₯Ό ꡬννμ΅λλ€.
pnpm κ³Ό Turborepo λ₯Ό μ¬μ©νκ² λ μ΄μ λ μμ£Ό κ°λ¨νκ³ μμ°μ€λ¬μ λλ°, NextJS λ₯Ό μ¬μ©νκ² λλ©΄μ κ°μ νμ¬μμ κ°λ°νλ Turborepo κ° μΆμ²λμ΄μ μ±ννκ² λμμ΅λλ€. λν Turborepo 곡μλ¬Έμμμλ μμ°μ€λ½κ² pnpm μ μΆμ²νκ³ μμ΄μ μ±ννκ² λμμ΅λλ€.
κΈ°μ‘΄ Vite Monorepo λ₯Ό μ¬μ©ν λλ κ°κ° κ°λ³μ μΈ ν¨ν€μ§λ€μ yarn workspaces λ‘ λ¨μν λ¬Άμ΄λμ λλμ λ°μμ΅λλ€. λ¬Όλ‘ core λ₯Ό κ°μ Έμμ μ¬μ©νλ λ±μ μ₯μ μ λ§μ΄ μμμ§λ§, λ€λ₯Έ ν¨ν€μ§ λκ°λ₯Ό ν λ ν¬ μμ λ£μ΄λκ³ , yarn workspaces μ λͺ λ Ήμ΄ λ±μ μ¬μ©ν΄ μ κ·Όνμ§λ§ κ°κ° λΉλ/μ€ννκ² λμ΄μ λλμ΄μ Έ μλ€λ λλμ λ°μμ΅λλ€.
λ³κ²½λ Turborepo λ₯Ό μ¬μ©νκ² λλ©΄μ λ³΄λ€ Monorepo λΌλ μλ―Έμ κ°κΉμμ‘λ€λ μκ°μ λ§μ΄ νκ² λμμ΅λλ€. ν¨ν€μ§κ° λΆλ¦¬λμ΄μκ³ , κ³΅μ© ν¨ν€μ§λ₯Ό κ°μ Έμμ μ¬μ©νλ κ²μ λμΌνμ§λ§ λ³λ ¬ μμ μ΄ μ€νλλ λΆλΆμ΄ μΈμμ μ΄μμ΅λλ€. μλ₯Ό λ€λ©΄ λΉλλ₯Ό μ€νν λ, κΈ°μ‘΄μλ κ°κ° νλμ© μ νν΄μ μ§νν΄μΌ νμ§λ§ Turborepo λ ν λ²μ λͺ¨λ ν¨ν€μ§ λΉλ μ€νμ΄ κ°λ₯ν΄μ λΉλ μκ°μ΄ λ§μ΄ μ€μ΄λ€μμ΅λλ€.
λν κΈ°μ‘΄ yarn berry pnp λ₯Ό μ¬μ©νμ λλ μμ‘΄μ±μμ μ¬λ¬ μ΄λ €μμ κ²ͺμλλ°, pnpm μ κ²½μ° monorepo λ₯Ό κ°λ ₯νκ² μ§μνλ€λ³΄λ ν΄λΉ λΆλΆμ ν¨μ¨μ μΌλ‘ μ²λ¦¬ν΄μ£Όμ΄ ν° μ΄λ €μμ΄ μμμ΅λλ€.
κΈ°μ‘΄ Viteλ₯Ό μ¬μ©ν λμ λ³κ²½λ Next λ₯Ό μ¬μ©ν λ κ°κ° λΌμ°ν νλ λ°©λ²μ λ§μ μ°¨μ΄κ° μμμ΅λλ€.
κΈ°μ‘΄ Vite μμλ createBrowserRouter λ₯Ό μ¬μ©ν΄μ μλμ²λΌ κ²½λ‘λ₯Ό μ€μ νμ΅λλ€.
ts
// createBrowserRouter μμ
export const router = createBrowserRouter([
{
Component: MainLayout,
children: [
{
path: "",
Component: lazy(() => import("./pages/Main")),
},
{
path: "/",
children: [
{
path: "blog",
Component: lazy(() => import("./pages/Blog")),
},
{
path: "blog/:postKey",
Component: lazy(() => import("./pages/BlogPost")),
},
],
},
],
},
]);
κ°κ° νμ΄μ§μ κ²½λ‘λ₯Ό μ§μ ν΄μ λͺ
μνκ³ , Component λ‘ κ°κ°μ Layout μ μ€μ ν΄ μ€ μ μμμ΅λλ€. λν loader λ±μ μ¬μ©ν΄μ λ‘λ© λ±μ μΆκ°λ‘ μ€μ ν μ μμ΅λλ€.
μ κ° λλ μ λ°©λ²μ μ₯μ μ κ²½λ‘κ° νλ²μ λͺ μλμ΄ μμ΄μ νμ΄μ§μ μ 체μ μΈ κ²½λ‘λ₯Ό νμΈν λ κ°λ μ±μ΄ μ’μ΅λλ€. λν λΌμ°ν μ΄ ν κ³³μ λͺ¨μμ Έ μμ΄μ μ€μ μ μΆκ°νκ±°λ μμ ν λ μ©μ΄ν©λλ€.
Next 13 λ²μ λΆν°λ App Router λ₯Ό μ¬μ©ν΄μ λΌμ°ν μ΄ κ°λ₯ν΄μ‘μ΅λλ€. App Router μ κΈ°λ³Έμ μΈ κ΅¬μ‘°λ app λλ ν 리 λ΄λΆμ ν΄λ κ΅¬μ‘°κ° μλμΌλ‘ URL κ²½λ‘κ° λλ ꡬ쑰μ λλ€.
sh
// λ³κ²½λ ν΄λ ꡬ쑰 μμ blog βββ src Β Β βββ app Β Β Β Β βββ page.tsx // 1. / ( main νμ΄μ§ ) Β Β Β Β βββ layout.tsx Β Β Β Β βββ blog Β Β βββ page.tsx // 2 /blog ( blog κ²μκΈ λͺ©λ‘ νμ΄μ§ ) Β Β βββ layout.tsx βββ [postKey] βββ page.tsx // 3 /blog/:postKey ( blog κ²μκΈ νμ΄μ§ ) βββ layout.tsx
μ μ²λΌ μμ±λλ ꡬ쑰μΈλ°, κ°κ° ν΄λ λ΄λΆμ page.tsx κ° νμ΄μ§λ₯Ό νμν©λλ€. layout.tsx μ κ²½μ° ν΄λΉ νμ΄μ§ νμ κ²½λ‘μ λͺ¨λ μ μ©λ©λλ€. μ μ νμ΄μ§μ κ²½μ° λ§λ€κ³ μΆμ κ²½λ‘μ μ΄λ¦μ μμ ν΄λ κ΅¬μ‘°λ‘ μ μ©μν¬ μ μμ΅λλ€. λμ νμ΄μ§μ κ²½μ°, [] λ΄λΆμ μ μ©νκ³ μΆμ κ°μ λ£μ΄ ν΄λΉ κ°μ key λ‘ μ¬μ©ν΄μ μ§μ ν μ μμ΅λλ€. λν createBrowserRouter μ²λΌ loading.tsx error.tsx λ₯Ό μ¬μ©ν΄μ λ‘λ©/μλ¬ uiλ ν¨κ» ꡬνν μ μμ΅λλ€.
μ ꡬ쑰μ μ₯μ μ ν΄λ κ΅¬μ‘°λ‘ λμ΄μμ΄μ λ΄κ° μμ±νκ³ μΆμ νμ΄μ§μ λ°λ‘λ°λ‘ μμ νκ³ , μ μ©ν μ μμ΅λλ€. ν΄λΉ νμ΄μ§μ λ‘λ©/μλ¬ λ±λ λ°λ‘λ°λ‘ μ μ©ν μ μλ€λ μ₯μ μ΄ μμ΅λλ€. λν SSR μ κΈ°λ³Έμ μΌλ‘ μ§μνκΈ°μ metadata μ€μ λ±μ΄ νΈλ¦¬νκ³ , SEO μ€μ μ μ΅μ νλμ΄μλ€λ μ₯μ μ΄ μμ΅λλ€. λ€λ§ λ¨μ μΌλ‘λ λΌμ°ν
μ λ³κ²½ν΄μΌ ν λ ν΄λ μμ²΄κ° μμ λμ΄μΌ νλ―λ‘ λ³΅μ‘νλ€λ λ¨μ μ΄ μμ΅λλ€. λν ν΄λ λΌμ°ν
μ΄ κΉμ΄μ§μλ‘ κ°λ
μ±μ΄ μ΄μ§ λ¨μ΄μ§λ λΆλΆμ΄ μμ΅λλ€.
App Router λ₯Ό μ²μ μ μ©νλ©΄μ μ΄μ§ λ¬λ컀λΈκ° μμ΄μ μ΄λ €μμ΄ μμμ§λ§, μ μ©νλ©΄μ λ§μ΄ μ΅μν΄μ‘μ΅λλ€. λΌμ°ν μ΄ λ무 κΉμ§ μμμμΈμ§ κ°λ μ± λΆλΆμμλ ν° μ΄λ €μμ μμμ§λ§ κΉμ΄μ§λ©΄ μ΄λ €μΈ κ² κ°λ€λ λλμ λ°μμ΅λλ€. νμ¬λ λ‘λ©/μλ¬ μ²λ¦¬λ λ°λ‘ λμ΄μμ§λ μμ§λ§ μΆνμ μΆκ° μμ μ λλ€.
κΈ°μ‘΄ Vite μ κ²½μ°, λͺ¨λ CSR μ΄μκΈ° λλ¬Έμ λ°λ‘ μ»΄ν¬λνΈλ₯Ό ꡬλΆν νμκ° ν¬κ² μμμ΅λλ€. νμ§λ§ Next μ κ²½μ° κΈ°λ³Έμ μΌλ‘ SSR κ³Ό μλ² μ»΄ν¬λνΈλ₯Ό μ§μνκ³ , μΆκ°μ μΈ μ€μ μΌλ‘ ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈλ₯Ό μ§μνκΈ° λλ¬Έμ μ΄λ₯Ό λΆλ¦¬ν νμμ±μ΄ μμ΅λλ€.
ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈλ κΈ°μ‘΄μ μ»΄ν¬λνΈλ€κ³Ό κ°μ₯ μ μ¬νλ€κ³ λ³Ό μ μμ΅λλ€. μ¬μ©μ μ΄λ²€νΈμ μλ΅νκ³ , μνλ₯Ό κ΄λ¦¬νκ³ React hooks λ₯Ό μ¬μ©ν μ μμ΅λλ€. λν λΈλΌμ°μ API ( window, document ) λ±μ μ κ·Όνκ³ μ¬μ©ν μ μμ΅λλ€. μ΄ μ»΄ν¬λνΈλ€μ JS λ²λ€μ ν¬ν¨λμ΄μ λΈλΌμ°μ μμ λ€μ΄λ‘λλ ν μ¬μ©λ©λλ€. μ»΄ν¬λνΈ μ΅μλ¨μ use client μ§μμ΄λ₯Ό μΆκ°νλ©΄ ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈλ‘ μΈμλ©λλ€.
μλ² μ»΄ν¬λνΈλ JS λ²λ€λ‘ λ€μ΄λ‘λ λμ§ μκ³ , μλ²μμ λ λλ§λμ΄ μ λ¬λλ μ»΄ν¬λνΈμ λλ€. λ°μ΄ν°λ₯Ό κ°μ Έμ€κ³ api λ₯Ό νΈμΆνκ±°λ μ μ μΈ μ»¨ν μΈ λ€μ μλ²μμ λ΄λΉν©λλ€. React Hook, μνκ΄λ¦¬ λ±μ μ¬μ©ν μ μμΌλ©°, Next λ λͺ¨λ μ»΄ν¬λνΈλ₯Ό κΈ°λ³Έμ μΌλ‘ μλ² μ»΄ν¬λνΈλ‘ μΈμν©λλ€. μ΄κΈ°μ λΉ λ₯΄κ² 보μ¬μ€μΌ νλ νμ΄μ§μ λλΆλΆμ μλ² μ»΄ν¬λνΈλ‘ μμ±νκ² λ©λλ€.
κ²°κ³Όμ μΌλ‘ Nextμ κ°μ₯ ν° μ₯μ μ ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈμ μλ² μ»΄ν¬λνΈλ₯Ό μ μ ν μ‘°ν©ν΄μ μ¬μ©ν μ μλ€λ μ μ λλ€. κΈ°λ³Έμ μΌλ‘ SSR μ μ§μνλ μλ² μ»΄ν¬λνΈλ₯Ό μ¬μ©νλ©΄μλ, μνκ΄λ¦¬λ μ΄λ²€νΈ κ΄λ¦¬λ ν΄λΌμ΄μΈνΈ μ»΄ν¬λνΈλ₯Ό μ¬μ©ν΄μ μ μ ν μ‘°μ ν μ μμ΅λλ€.
Next λ₯Ό μ²μ μ μ©νλ©΄μ λͺ¨λ₯΄λ λΆλΆλ λ§μκ³ μλ‘ μ μ©ν λΆλΆλ λ§μλλ° μ μ©νκ³ λλ λ§μ΄ μ΅μν΄μ§ κ² κ°μ΅λλ€. 무μλ³΄λ€ SSR μ μ²μ μ μ©ν΄ λ³Έ κ²μ΄λΌ λΏλ―νκ³ , μμ§μ λ 곡λΆν΄μΌκ² μ§λ§ λ€μμλ μλ‘μ΄ κ²λ λ μλν΄λ³΄κ³ μΆμ΅λλ€.