tailwind-merge

Janeยท2024๋…„ 2์›” 11์ผ
3
post-thumbnail
post-custom-banner

๐Ÿค” ๋ฌด์—‡์„ ์œ„ํ•œ ๊ฒƒ์ผ๊นŒ?

  • ๊ฐ„๋‹จํžˆ ์†Œ๊ฐœํ•˜์ž๋ฉด, ์Šคํƒ€์ผ ์ถฉ๋Œ ์—†์ด JS์—์„œ Tailwind CSS ํด๋ž˜์Šค๋“ค์„ ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์œ ํ‹ธ ํ•จ์ˆ˜์ด๋‹ค.

  • Tailwind CSS๋ฅผ React๋‚˜ Vue ๊ฐ™์€ ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์ด ํ•˜๋‚˜์˜ ํŠน์ • ์ผ€์ด์Šค์—์„œ๋งŒ ์ปดํฌ๋„ŒํŠธ์˜ ๋ช‡๋ช‡ ์Šคํƒ€์ผ์„ ๋ฐ”๊พธ๊ณ  ์‹ถ์€ ์ƒํ™ฉ์„ ๋งˆ์ฃผ์นœ ๊ฒฝํ—˜์ด ๋งŽ๋‹ค.

function CommonInput(props) {
    const className = `border rounded px-2 py-1 ${props.className || ''}`
    return <input {...props} className={className} />
}

function ExceptionalInput(props) {
    return (
        <MyGenericInput
            {...props}
            className="p-3" // โ† Only want to change some padding
        />
    )
}
  • ExceptionalInput์ด ๋ Œ๋”๋ง ๋  ๋•Œ, border rounded px-2 py-1 p-3์ด๋ผ๋Š” className์„ ๊ฐ–๋Š” input์ด ์ƒ์„ฑ๋œ๋‹ค.
  • ํ•˜์ง€๋งŒ CSS ์บ์Šค์บ์ด๋”ฉ์˜ ์ž‘๋™ ๋•Œ๋ฌธ์— p-3์€ ๋ฌด์‹œ๋  ๊ฒƒ์ด๋‹ค.
    • CSS ์บ์Šค์บ์ด๋”ฉ(cascading)
  • className์—์„œ ํด๋ž˜์Šค๋ช…์˜ ์ˆœ์„œ๋Š” ์˜๋ฏธ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์— p-3์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” px-2์™€ py-1์„ ์‚ญ์ œํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
๐Ÿค  ๊ทธ๋ž˜์„œ tailwind-merge๊ฐ€ ๋“ฑ์žฅํ–ˆ์Šต๋‹ˆ๋‹ค!
function MyGenericInput(props) {
    // ์ด์ œ `props.className`๊ฐ€ ์ถฉ๋Œํ•˜๋Š” ํด๋ž˜์Šค ๋ช…์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    const className = twMerge('border rounded px-2 py-1', props.className)
    return <input {...props} className={className} />
}
  • tainwind-merge๋Š” ์ถฉ๋Œํ•˜๋Š” ํด๋ž˜์Šค ๋ช…๋งŒ ๋ฎ์–ด์“ฐ๊ณ  ๋‹ค๋ฅธ ๊ฒƒ๋“ค์€ ๊ทธ๋Œ€๋กœ ๋‚จ๊ฒจ๋‘”๋‹ค.
    • ์œ„์˜ ์˜ˆ์‹œ์—์„œ ExceptionalInput์€ border rounded p-3์ด๋ผ๋Š” ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ ๋ Œ๋”๋ง๋  ๊ฒƒ์ด๋‹ค.

๐Ÿ“‚ API reference

โœจ twMerge

function twMerge(
    ...classLists: Array<string | undefined | null | false | 0 | typeof classLists>
): string
  • ๊ธฐ๋ณธ Tailwind config๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ฑฐ๋‚˜ ๊ธฐ๋ณธ๊ฐ’๊ณผ ๊ทผ์‚ฌํ•œ config ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์„ ๊ฒฝ์šฐ ์‚ฌ์šฉํ•˜๋Š” ๊ธฐ๋ณธ ํ•จ์ˆ˜์ด๋‹ค.

  • twMerge๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด extendTailwindMerge๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปค์Šคํ…€ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

โœจ twJoin

function twJoin(
    ...classLists: Array<string | undefined | null | false | 0 | typeof classLists>
): string

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

twJoin(
    'border border-red-500',
    hasBackground && 'bg-red-100',
    hasLargeText && 'text-lg',
    hasLargeSpacing && ['p-2', hasLargeText ? 'leading-8' : 'leading-7'],
)

๐ŸŒŸ ํŠน์ง•

Merging behavior

  • tailwind-merge๋Š” ์ง๊ด€์ ์œผ๋กœ ๋งŒ๋“ค์–ด์กŒ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์˜ ๊ทœ์น™๋“ค์„ ํ†ตํ•ด ์ถฉ๋Œ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ์–ด๋–ค ํด๋ž˜์Šค๊ฐ€ ์ด๊ธฐ๋Š”์ง€๋ฅผ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐ŸŸข ์žฅ์ 

๋‹ค์–‘ํ•œ ์ธต์œ„์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์‰ฝ๋‹ค.

tailwind-merge๋Š” ๋””์ž์ธ ์‹œ์Šคํ…œ์ด๋‚˜ UI ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ฒ˜๋Ÿผ HOC์— ์ ํ•ฉํ•˜๋‹ค.

  • HOC(๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ): ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ ธ์™€ ์ƒˆ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

๋น ๋ฅธ ๊ฐœ๋ฐœ๊ณผ ๋ฐ˜๋ณต ์†๋„๋ฅผ ๊ฐ€๋Šฅ์ผ€ ํ•œ๋‹ค.

tailwind-merge๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ ๋‚ด์—์„œ ๊ฐ๊ฐ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•˜์ง€ ์•Š๊ณ ๋„ ๋‹ค์–‘ํ•œ ๋ฒ”์œ„์˜ ์Šคํƒ€์ผ๋ง์„ ์ง€์›ํ•  ์ˆ˜ ์žˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์˜ ๋„ˆ๋น„, ๊ธ€์ž ์ƒ‰์ƒ์ด๋‚˜ ์œ„์น˜๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ •์˜ํ•˜์ง€ ์•Š๋”๋ผ๋„ ํ•˜๋‚˜์˜ className์˜ prop์œผ๋กœ ์ปค์Šคํ…€ํ•œ width์™€ ๊ธ€์ž ์ƒ‰์ƒ, ์œ„์น˜๋ฅผ ๋ณ€๊ฒฝ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

์„ฑ๊ธ‰ํ•œ ์ถ”์ƒํ™”๋ฅผ ๋ฐฉ์ง€ํ•œ๋‹ค.

  • ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š” ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•ด ํŠน์ • ์ƒํ™ฉ์—์„œ๋งŒ error๋ฅผ ๋‚˜ํƒ€๋‚ด๋„๋ก ์Šคํƒ€์ผ์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ์— ํŠน์ • ์†์„ฑ๋งŒ ์ „๋‹ฌํ•˜์—ฌ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.
    ์ดํ›„ ๋” ๋งŽ์€ ๊ณณ์—์„œ ํ•ด๋‹น ์Šคํƒ€์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ ๋‚ด๋ถ€๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

๐Ÿ”ด ๋‹จ์ 

๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค.

tailwind-merge์˜ ๊ฒฝ์šฐ ์–ด๋–ค ํด๋ž˜์Šค๋“ค์ด ์ถฉ๋Œํ•˜๋Š”์ง€๋ฅผ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฉ๋Œ€ํ•œ ํฌ๊ธฐ์˜ config์— ์˜์กดํ•œ๋‹ค.
ํƒ€์ดํŠธํ•œ ๋ฒˆ๋“ค ์‚ฌ์ด์ฆˆ ์ œ์•ฝ ์กฐ๊ฑด์„ ๊ฐ€์งˆ ๊ฒฝ์šฐ ์ œํ•œ์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋„ˆ๋ฌด ๋งŽ์€ ์ž์œจ์„ฑ์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

๋Œ€๊ทœ๋ชจ ํŒ€์ด๋‚˜ ๊ณต๊ฐœ์ ์œผ๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ง„ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ์ž๊ฐ€ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ—ˆ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ปดํฌ๋„ŒํŠธ์˜ API๋ฅผ ์‚ฌ์šฉํ• ์ˆ˜๋„, ์‚ฌ์šฉํ•˜์ง€ ์•Š์„์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ tailwind-merge๋Š” ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋„ˆ๋ฌด ๋งŽ์€ ์ž์œ ๋ฅผ ๋ถ€์—ฌํ•˜์—ฌ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์œ ์ง€ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฐœ์ „์‹œํ‚ค๊ธฐ๊ฐ€ ๋” ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ๋‹ค. tailwind-merge๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ปดํฌ๋„ŒํŠธ ์Šคํƒ€์ผ๋ง์˜ ์™„์ „ํ•œ ํ†ต์ œ๋Š” ํฌ๊ธฐํ•ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์€ ์ปดํฌ๋„ŒํŠธ ๋ฆฌํŒฉํ† ๋ง์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ๋‹ค.

์ปดํฌ๋„ŒํŠธ์— ์ž„์˜์˜ ํด๋ž˜์Šค๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ์˜ ๋‚ด๋ถ€ ์Šคํƒ€์ผ์„ ๋ฆฌํŒฉํ„ฐ๋งํ•  ๋•Œ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ์ž์˜ ์Šคํƒ€์ผ์ด ์†์ƒ๋  ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ ๋‹น์‹ ์ด ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์„ ์ž์ฃผ ๋ฆฌํŒฉํ† ๋งํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค๋ฉด ์ด๋Ÿฌํ•œ ์Šคํƒ€์ผ์€ ์ปดํฌ๋„ŒํŠธ์˜ ์šฉ๋„ ๋˜ํ•œ ๋ฆฌํŒฉํ† ๋งํ•  ์˜ˆ์ •์ด ์•„๋‹Œ ํ•œ props๋กœ ์ „๋‹ฌ๋˜๋Š” ์Šคํƒ€์ผ๊ณผ ๋ณ‘ํ•ฉ๋˜์ง€ ๋ง์•„์•ผ ํ•œ๋‹ค.

Tailwind CSS๋‚˜ ์ปดํฌ๋„ŒํŠธ ํ•ฉ์„ฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ๋ฌด์šฉํ•˜๋‹ค.

tailwind-merge๋Š” TailwindCSS๊ณผ ์ปดํฌ๋„ŒํŠธ ํ•ฉ์„ฑ์„ ํŠน์ •ํ•œ ํ˜•ํƒœ์—์„œ ํ•จ๊ผ ์‚ฌ์šฉํ•  ๋•Œ์—๋งŒ ์œ ์šฉํ•˜๋‹ค.

  • ์ปดํฌ๋„ŒํŠธ ํ•ฉ์„ฑ: ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž‘์€ ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌํ•˜๊ณ  ์žฌ์กฐํ•ฉํ•˜์—ฌ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ

๐Ÿ”Ž References

profile
An investment in knowledge pays the best interest๐Ÿ™ƒ
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€