๋ณธ๋ฌธ์œผ๋กœ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Currency ์•ฑ์— ์ƒˆ๋กœ ๋‚˜์˜จ Tailwind v4๋ฅผ ์ ์šฉํ•ด๋ณด๊ณ  ์‹ถ์—ˆ๋‹ค. ๊ธฐ์กด ์•ฑ๋“ค์€ v3๋ฅผ ์“ฐ๊ณ  ์žˆ์—ˆ๋Š”๋ฐ, "๊ฐ๊ฐ ๋”ฐ๋กœ ๋นŒ๋“œํ•˜๋‹ˆ๊นŒ ์ƒ๊ด€์—†๊ฒ ์ง€"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋˜ ๊ฒŒ ์ฐฉ๊ฐ์ด์—ˆ๋‹ค. PostCSS ์„ค์ •์ด ์ถฉ๋Œํ•˜๋ฉด์„œ ๋นŒ๋“œ๊ฐ€ ๋ง๊ฐ€์ง€๊ธฐ ์‹œ์ž‘ํ–ˆ๊ณ , ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋‹ค ๋ณด๋‹ˆ ๊ฒฐ๊ตญ ์ „์ฒด monorepo ๊ตฌ์กฐ๋ฅผ ๋œฏ์–ด๊ณ ์น˜๊ฒŒ ๋๋‹ค.

 

 

์ผ๋‹จ Tailwind v4 ์ ์šฉํ–ˆ๋˜ ๊ฒƒ

Tailwind v4์˜ ์ƒˆ๋กœ์šด CSS ์—”์ง„์ด ๊ถ๊ธˆํ•ด์„œ Currency ์•ฑ์— ์ ์šฉํ•œ ์ฝ”๋“œ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๋ณด๋‹ˆ ์„ค์ • ๋ฐฉ๋ฒ•์ด ๊ฝค ๋‹ฌ๋ผ์ ธ ์žˆ์—ˆ๋‹ค.

// apps/currency/vite.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    tailwindcss(), // v4๋Š” Vite ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ๋ชจ๋“  ๊ฑธ ์ฒ˜๋ฆฌ
  ]
})

๊ธฐ์กด v3์ฒ˜๋Ÿผ ๋ณ„๋„์˜ PostCSS ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํ•„์š” ์—†๋‹ค๋Š” ๊ฒŒ ์‹ ๊ธฐํ–ˆ๋‹ค. ์ผ๋‹จ ์ ์šฉํ•ด๋ณด๋‹ˆ ์ž˜ ๋™์ž‘ํ–ˆ๋‹ค. "์ด๊ฑฐ ์‰ฝ๋„ค?"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ ํ„ฐ๋ณด ๋นŒ๋“œ๋ฅผ ๋Œ๋ ค๋ณด๋‹ˆ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ๋‹ค.

 

 

์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ: PostCSS ์„ค์ • ์ถฉ๋Œ

ํ„ฐ๋ณด๊ฐ€ ์ „์ฒด ์•ฑ๋“ค์„ ๋นŒ๋“œํ•  ๋•Œ PostCSS ์„ค์ •์ด ๋’ค์„ž์ด๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. Currency ์•ฑ์˜ CSS๋Š” ์ œ๋Œ€๋กœ ์ƒ์„ฑ๋˜๋Š”๋ฐ, ๋‹ค๋ฅธ ์•ฑ๋“ค์˜ CSS๊ฐ€ ์ด์ƒํ•ด์กŒ๋‹ค. ๋กœ๊ทธ๋ฅผ ๋ณด๋‹ˆ Tailwind v3์™€ v4์˜ PostCSS ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด ์„œ๋กœ ๊ฐ„์„ญํ•˜๊ณ  ์žˆ์—ˆ๋‹ค. Currency ์•ฑ์— ๋ณ„๋„์˜ PostCSS ์„ค์ •์„ ๋งŒ๋“ค์–ด์„œ ๊ฐ„๋‹จํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์„ ์ค„ ์•Œ์•˜๋‹ค.

// apps/currency/postcss.config.js
export default {
  plugins: [] // Tailwind v4๋Š” Vite์—์„œ ์ฒ˜๋ฆฌํ•˜๋‹ˆ๊นŒ ๋น„์›Œ๋‘์ž
}

ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๋‹ค. ํ„ฐ๋ณด๊ฐ€ ๋นŒ๋“œํ•  ๋•Œ ๊ฐ ์•ฑ์˜ node_modules๊ฐ€ ์„œ๋กœ ์˜ํ–ฅ์„ ์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์•˜๋‹ค. LLM์— ๋ฌผ์–ด๋ณด๋‹ˆ "phantom dependencies" ๋ฌธ์ œ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋‹ค๊ณ  ํ–ˆ๋‹ค.

 

 

๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ: ํŒจํ‚ค์ง€ ๊ฒฉ๋ฆฌ ๋ถ€์กฑ

๋ฌธ์ œ๋ฅผ ๋” ํŒŒ๋ณด๋‹ˆ npm์˜ hoisting ๋•Œ๋ฌธ์ด์—ˆ๋‹ค. ๊ฐ ์•ฑ์ด ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌ๋˜์–ด์•ผ ํ•˜๋Š”๋ฐ, ์˜์กด์„ฑ๋“ค์ด ์„ž์ด๋ฉด์„œ PostCSS ํ”Œ๋Ÿฌ๊ทธ์ธ๋“ค์ด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฐฉ์‹์œผ๋กœ ๋กœ๋“œ๋˜๊ณ  ์žˆ์—ˆ๋‹ค. ๊ฒฐ๊ตญ ํŒจํ‚ค์ง€ ๊ฒฉ๋ฆฌ๊ฐ€ ๋” ์—„๊ฒฉํ•œ pnpm์œผ๋กœ ๊ฐˆ์•„ํƒ€๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋‹ค. ์ผ๋‹จ ์„ค์ •๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด๋ดค๋‹ค.

# pnpm-workspace.yaml
packages:
  - 'apps/*'
# .pnpmrc
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=false  # ์ด๊ฒŒ ํ•ต์‹ฌ์ด์—ˆ๋‹ค
store-dir=.pnpm-store

shamefully-hoist=false๊ฐ€ ์ค‘์š”ํ–ˆ๋‹ค. ์ด ์„ค์ •์œผ๋กœ ๊ฐ ์•ฑ์˜ ์˜์กด์„ฑ์ด ์™„์ „ํžˆ ๊ฒฉ๋ฆฌ๋๋‹ค.

 

 

์„ธ ๋ฒˆ์งธ ๋ฌธ์ œ: Turborepo ์„ค์ •์˜ ๋ฏธ์Šคํ„ฐ๋ฆฌ

pnpm์œผ๋กœ ๋ฐ”๊พธ๊ณ  ๋‚˜๋‹ˆ ์ƒˆ๋กœ์šด ์˜๋ฌธ์ด ์ƒ๊ฒผ๋‹ค. ๊ณต์‹ ๋ฌธ์„œ์— ๋‚˜์˜จ best pracice๋Œ€๋กœ turbo.json ํŒŒ์ผ์„ ์ž‘์„ฑํ•˜๋‹ค ๋ณด๋‹ˆ ์ด์ƒํ–ˆ๋‹ค.

{
  "tasks": {
    "build": {
      "inputs": [
        "src/**",
        "public/**", 
        "*.json",
        "postcss.config.*",
        "vite.config.*"
      ]
    }
  }
}

๋ถ„๋ช…ํžˆ ๋‚ด ํ”„๋กœ์ ํŠธ์—๋Š” apps/currency, apps/chat, apps/mbti ๊ฐ™์€ ํด๋”๋“ค์ด ์žˆ๋Š”๋ฐ, ์™œ ๊ตฌ์ฒด์ ์ธ ๊ฒฝ๋กœ๊ฐ€ ์—†์„๊นŒ? ์ฒ˜์Œ์—๋Š” ์ด๋ ‡๊ฒŒ ์จ์•ผ ํ•˜๋Š” ์ค„ ์•Œ์•˜๋‹ค:

"inputs": ["apps/currency/**", "apps/chat/**", "apps/mbti/**"]

ํ„ฐ๋ณด๊ฐ€ ์–ด๋–ป๊ฒŒ ๋‚ด ์•ฑ๋“ค์„ ์ฐพ๋Š”์ง€ ๊ถ๊ธˆํ•ด์„œ ์—ฌ๋Ÿฌ ์‹คํ—˜์„ ํ•ด๋ดค๋‹ค.

 

 

์‹คํ—˜ 1: ํ„ฐ๋ณด๊ฐ€ ๋ญ˜ ๋ณด๊ณ  ์žˆ๋‚˜?

npx turbo ls

์ด ๋ช…๋ น์–ด๋กœ ํ„ฐ๋ณด๊ฐ€ ์ธ์‹ํ•˜๋Š” ํŒจํ‚ค์ง€๋“ค์„ ํ™•์ธํ•ด๋ดค๋‹ค. ์‹ ๊ธฐํ•˜๊ฒŒ๋„ apps ํด๋” ์•ˆ์˜ ๊ฒƒ๋“ค๋งŒ ๋‚˜ํƒ€๋‚ฌ๋‹ค. ๊ทธ๋Ÿผ ํ„ฐ๋ณด๊ฐ€ ์–ด๋–ป๊ฒŒ ์ด๊ฑธ ์•Œ์ง€?

LLM์— ๋ฌผ์–ด๋ณด๋‹ˆ "์›Œํฌ์ŠคํŽ˜์ด์Šค ์„ค์ •์„ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•œ๋‹ค"๊ณ  ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋” ์‹คํ—˜ํ•ด๋ดค๋‹ค.

 

 

์‹คํ—˜ 2: ์›Œํฌ์ŠคํŽ˜์ด์Šค ํŒจํ„ด ํ…Œ์ŠคํŠธ

packages ํด๋”๋ฅผ ๋งŒ๋“ค๊ณ  ๊ทธ ์•ˆ์— ํ…Œ์ŠคํŠธ ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด๋ดค๋‹ค. npx turbo ls๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด๋„ ์ƒˆ ํŒจํ‚ค์ง€๋Š” ๋ชฉ๋ก์— ์—†์—ˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ pnpm-workspace.yaml์— packages/*๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋‹ˆ ์ƒˆ ํŒจํ‚ค์ง€๊ฐ€ ๋‚˜ํƒ€๋‚ฌ๋‹ค! ์ด์ œ ์ดํ•ด๋๋‹ค. ํ„ฐ๋ณด๋Š” ์›Œํฌ์ŠคํŽ˜์ด์Šค ์„ค์ •์„ ์ฝ๊ณ  ๊ทธ์— ๋”ฐ๋ผ ํŒจํ‚ค์ง€๋“ค์„ ์ฐพ๋Š”๋‹ค.

์ฆ‰, turbo.json์˜ postcss.config.* ํŒจํ„ด์€ ๊ฐ ์•ฑ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ƒ๋Œ€์ ์œผ๋กœ ์ ์šฉ๋œ๋‹ค๋Š” ๋œป์ด์—ˆ๋‹ค. apps/currency/postcss.config.js, apps/chat/postcss.config.js ์ด๋Ÿฐ ์‹์œผ๋กœ.

 

 

๋ฌธ์ œ ํ•ด๊ฒฐ์˜ ์ „์ฒด์ ์ธ ๊ทธ๋ฆผ

๋ชจ๋“  ํผ์ฆ์ด ๋งž์ถฐ์ง€๋‹ˆ Currency ์•ฑ์˜ Tailwind v4 ์ ์šฉ์ด ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ–ˆ๋‹ค.

  1. pnpm์˜ ์—„๊ฒฉํ•œ ๊ฒฉ๋ฆฌ: shamefully-hoist=false๋กœ ๊ฐ ์•ฑ์˜ PostCSS ๊ด€๋ จ ํŒจํ‚ค์ง€๋“ค์ด ์™„์ „ํžˆ ๋ถ„๋ฆฌ๋จ
  2. ๋…๋ฆฝ์ ์ธ PostCSS ์„ค์ •: Currency ์•ฑ์€ ๋นˆ PostCSS ์„ค์ •, ๋‹ค๋ฅธ ์•ฑ๋“ค์€ ๊ธฐ์กด v3 ์„ค์ • ์œ ์ง€
  3. ํ„ฐ๋ณด์˜ ๊ฐœ๋ณ„ ๋นŒ๋“œ: ๊ฐ ์•ฑ์ด ์ž์‹ ์˜ ์„ค์ •์— ๋”ฐ๋ผ ๋…๋ฆฝ์ ์œผ๋กœ ๋นŒ๋“œ๋จ

์ตœ์ข… ๋นŒ๋“œ ํ”Œ๋กœ์šฐ๋Š” ์ด๋ ‡๊ฒŒ ๋๋‹ค:

{
  "scripts": {
    "build-apps": "turbo run build",
    "build": "pnpm run build-apps && next build",
    "clean": "rm -rf public/static && rm -rf apps/*/node_modules && rm -rf apps/*/.next",
    "dev": "pnpm run build-apps && next dev --turbopack"
  }
}

๋จผ์ € ํ„ฐ๋ณด๊ฐ€ ๊ฐ ์•ฑ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋นŒ๋“œํ•˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฌผ์„ ๋ฉ”์ธ Next.js ํ”„๋กœ์ ํŠธ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ๊ตฌ์กฐ๊ฐ€ ๋๋‹ค.

 

 

์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ถ€๊ฐ€ ํšจ๊ณผ๋“ค

Currency ์•ฑ์˜ Tailwind v4 ์ ์šฉ์„ ์œ„ํ•ด ์‹œ์ž‘ํ•œ ์ž‘์—…์ด์—ˆ๋Š”๋ฐ, ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ „์ฒด monorepo๊ฐ€ ํ›จ์”ฌ ๊ฒฌ๊ณ ํ•ด์กŒ๋‹ค.

๋นŒ๋“œ ์บ์‹œ ๊ฐœ์„ : ๊ฐ ์•ฑ์˜ ์„ค์ • ํŒŒ์ผ ๋ณ€๊ฒฝ์ด ํ•ด๋‹น ์•ฑ์—๋งŒ ์˜ํ–ฅ์„ ์ฃผ๊ฒŒ ๋๋‹ค. Currency ์•ฑ์˜ Vite ์„ค์ •์„ ๋ฐ”๊ฟ”๋„ ๋‹ค๋ฅธ ์•ฑ๋“ค์€ ์บ์‹œ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

์˜์กด์„ฑ ์ถฉ๋Œ ํ•ด๊ฒฐ: phantom dependencies ๋ฌธ์ œ๊ฐ€ ์™„์ „ํžˆ ์‚ฌ๋ผ์กŒ๋‹ค. ๊ฐ ์•ฑ์ด ์ •๋ง๋กœ ๋…๋ฆฝ์ ์ธ ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰๋œ๋‹ค.

๊ฐœ๋ฐœ ๊ฒฝํ—˜ ํ–ฅ์ƒ: ์ƒˆ๋กœ์šด ์•ฑ์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋ณ„๋„ ์„ค์ •์ด ํ•„์š” ์—†์–ด์กŒ๋‹ค. apps ํด๋”์— ๋””๋ ‰ํ† ๋ฆฌ ๋งŒ๋“ค๊ณ  package.json ์ถ”๊ฐ€ํ•˜๋ฉด ํ„ฐ๋ณด๊ฐ€ ์ž๋™์œผ๋กœ ์ธ์‹ํ•œ๋‹ค.

 

 

์‚ฝ์งˆํ•˜๋ฉด์„œ ๋ฐฐ์šด ๊ฒƒ๋“ค

์ด๋ฒˆ ๊ฒฝํ—˜์—์„œ ๊ฐ€์žฅ ํฐ ๊ตํ›ˆ์€ "์ž๋™ํ™”๋œ ๋„๊ตฌ์˜ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ"์˜ ์ค‘์š”์„ฑ์ด์—ˆ๋‹ค.

ํ„ฐ๋ณด๊ฐ€ "์›Œํฌ์ŠคํŽ˜์ด์Šค๋ฅผ ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•œ๋‹ค"๋Š” ๋ฌธ์„œ๋งŒ ๋ดค์„ ๋•Œ๋Š” ๊ทธ๋ƒฅ '์˜ค ํŽธํ•˜๋„ค' ๋ผ๊ณ ๋งŒ ์ƒ๊ฐํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ pnpm-workspace.yaml ํŒŒ์ผ์„ ์ฝ๊ณ , ๊ธ€๋กœ๋ธŒ ํŒจํ„ด์„ ํ™•์žฅํ•˜๊ณ , ๊ฐ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ package.json์„ ์ฐพ๋Š” ๊ตฌ์ฒด์ ์ธ ๊ณผ์ •์ด ์žˆ์—ˆ๋‹ค.

์ด๋Ÿฐ ๊ณผ์ •์„ ์ดํ•ดํ•˜๊ณ  ๋‚˜๋‹ˆ Currency ์•ฑ์˜ ํŠน๋ณ„ํ•œ PostCSS ์„ค์ •์ด ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ ์•ฑ๋“ค๊ณผ ๊ฒฉ๋ฆฌ๋˜๋Š”์ง€๋„ ๋ช…ํ™•ํ•ด์กŒ๋‹ค. ๊ฐ ์•ฑ์˜ ๋นŒ๋“œ ํ™˜๊ฒฝ์ด ์ง„์งœ๋กœ ๋…๋ฆฝ์ ์ด๋ผ๋Š” ํ™•์‹ ์ด ์ƒ๊ฒผ๋‹ค.

 

 

๋งˆ์น˜๋ฉฐ

Tailwind v4 ํ•˜๋‚˜ ์ ์šฉํ•˜๋ ค๋‹ค๊ฐ€ monorepo ์ „์ฒด๋ฅผ ๋œฏ์–ด๊ณ ์น˜๊ฒŒ ๋์ง€๋งŒ, ๊ฒฐ๊ณผ์ ์œผ๋กœ๋Š” ํ›จ์”ฌ ์ข‹์€ ๊ตฌ์กฐ๋ฅผ ์–ป์—ˆ๋‹ค. Currency ์•ฑ์€ v4์˜ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋“ค์„ ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋๊ณ , ๋‹ค๋ฅธ ์•ฑ๋“ค๋„ ์•ˆ์ •์ ์œผ๋กœ v3๋ฅผ ๊ณ„์† ์“ธ ์ˆ˜ ์žˆ๋‹ค.

๋ฌด์—‡๋ณด๋‹ค ์ด์ œ๋Š” ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ์‹คํ—˜ํ•  ๋•Œ ๋‹ค๋ฅธ ์•ฑ๋“ค์—๊ฒŒ ์˜ํ–ฅ์„ ์ค„ ๊ฑฑ์ •์ด ์—†์–ด์กŒ๋‹ค. ๊ฐ ์•ฑ์ด ์™„์ „ํžˆ ๋…๋ฆฝ์ ์ธ ํ™˜๊ฒฝ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋‹ˆ๊นŒ.

์ฒ˜์Œ์—๋Š” ๋‹จ์ˆœํ•œ CSS ์ถฉ๋Œ ๋ฌธ์ œ์˜€๋Š”๋ฐ, ํ•ด๊ฒฐ ๊ณผ์ •์—์„œ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์™€ ๋นŒ๋“œ ์‹œ์Šคํ…œ๊นŒ์ง€ ์ œ๋Œ€๋กœ ์ดํ•ดํ•˜๊ฒŒ ๋๋‹ค. ์ด๋Ÿฐ ์‹์œผ๋กœ ํ•˜๋‚˜์˜ ๋ฌธ์ œ๋ฅผ ๊นŠ์ด ํŒŒ๋‹ค ๋ณด๋ฉด ๊ด€๋ จ๋œ ์‹œ์Šคํ…œ ์ „์ฒด๋ฅผ ๋ฐฐ์šฐ๊ฒŒ ๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค. ์‹œ๊ฐ„์€ ์ข€ ๊ฑธ๋ ธ์ง€๋งŒ ๊ฐ’์ง„ ๊ฒฝํ—˜์ด์—ˆ๋‹ค.

 

 

https://junha.kr/currency

 

currency

 

junha.kr

 

๋ฐ˜์‘ํ˜•