lil.dev
Published on

๐ŸŽ‰ ์ปฌ๋Ÿฌ๋ฆฌ์ŠคํŠธ ํ”„๋กœ์ ํŠธ #17 button ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ - ๋””์ž์ธ ์‹œ์Šคํ…œ

๊ธ€์“ด์ด

    ๐Ÿ“Œ

    Welcome

    โœจ ์ปฌ๋Ÿฌ๋ฆฌ์ŠคํŠธ ์‚ฌ์ดํŠธ(์•„์ง ์—†์Œ

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

    ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ

    ํ…Œ์ŠคํŠธ ํผ์˜ ํ•˜๋‹จ ์ €์žฅํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ๊ธฐ์ค€์œผ๋กœ ์‚ผ์•„์„œ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถ„๋ฆฌํ•œ๋‹ค.

    ๋ถ„๋ฆฌํ•˜๋Š” ์ด์œ ๋Š” ๋””์ž์ธ ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ์™€ ๊ฐ™์€๋ฐ,
    ์ค‘๋ณต๋˜๋Š” ๋ฒ„ํŠผ๋“ค์„ ํ•œ๋ฒˆ์— ์ˆ˜์ •ํ•˜๊ธฐ๊ฐ€ ์‰ฌ์›Œ์ง€๊ณ , ์ผ๊ด€์„ฑ ์žˆ๋Š” ํ™”๋ฉด์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

    1๋‹จ๊ณ„ ๊ธฐ์กด ํ™”๋ฉด์—์„œ ํŠน์ •ํ•œ ๋ถ€๋ถ„์„ ์ž˜๋ผ์„œ, Button.tsx ๋กœ ๋ถ„๋ฆฌํ–ˆ๋‹ค

    import React from 'react'
    
    function Button() {
      return (
        <button type="submit" className="bg-yellow-300 p-4 rounded-xl">
          ์ €์žฅํ•˜๊ธฐ
        </button>
      )
    }
    
    export default Button
    

    2๋‹จ๊ณ„ props๋กœ ๋ฐ”๋€Œ๋Š” ๋ถ€๋ถ„์„ ๋ฐ›๊ธฐ - ์ถ”์ถœํ•  ๋•Œ๋Š” ๋ณ€ํ•˜๋Š” ๋ถ€๋ถ„๊ณผ ๋ณ€ํ•˜์ง€ ์•Š์€ ๋ถ€๋ถ„์„ ๊ตฌ๋ถ„ํ•ด์•ผ

    import React from 'react'
    
    // "string" => string literal type => ์›์†Œ๊ฐ€ 1๊ฐœ์ธ ์ง‘ํ•ฉ
    // string => ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด๋“ค์˜ ํƒ€์ž… => ์›์†Œ๊ฐ€ ๋งค์šฐ ๋งŽ์€ ์ง‘ํ•ฉ!
    
    function Button({ type, children }: { type: 'submit' | 'button'; children: string }) {
      return (
        <button type={type} className="bg-yellow-300 p-4 rounded-xl">
          {children}
        </button>
      )
    }
    
    export default Button
    

    3๋‹จ๊ณ„ type์„ ๊ฐ„์†Œํ™”ํ•˜๊ณ , className์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค๊ธฐ

    React component๋กœ์จ button์ด ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  props๋ฅผ ๋„˜๊ฒจ์คŒ์œผ๋กœ์จ
    type์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ชจ๋“  props๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” button์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.


    className์„ props์— ๋„ฃ์–ด์ฃผ์–ด ๊ฐ ํŽ˜์ด์ง€์—์„œ ์ถ”๊ฐ€๋กœ ํ•„์š”ํ•œ className(tailwind๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— className์„ ์ด์šฉํ•ด css๋ฅผ ์ˆ˜์ •ํ•œ๋‹ค.)์„ ๋„ฃ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

    import React from 'react'
    
    type ButtonProps = React.ComponentProps<'button'> // <- ์ค‘์š”!
    
    function Button({ type, children, className }: ButtonProps) {
      return (
        <button type={type} className={'bg-yellow-300 p-4 rounded-xl ' + className}>
          {children}
        </button>
      )
    }
    
    export default Button
    

    4๋‹จ๊ณ„ polymorphic component (๋‹คํ˜•์ ์ธ ์ปดํฌ๋„ŒํŠธ)

    ๊ฐ™์€ ๋””์ž์ธ ์‹œ์Šคํ…œ ์ปดํฌ๋„ŒํŠธ๋ฅผ... ์„œ๋กœ ๋‹ค๋ฅธ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ!

    "๋” ๋งŽ์€ ์‚ฌ์šฉ๋ก€๋ฅผ ์ง€์›ํ•˜๋ ค๋ฉด ๋” ๋ณต์žกํ•˜๊ฑฐ๋‚˜, ๋‹จ์ˆœํ•˜๋”๋ผ๋„ ๋” ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ๊ฐœ๋…์ด ํ•„์š”ํ•˜๋‹ค"

    ์ œ๋„ค๋ฆญ์€... ํƒ€์ž…์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. C๋กœ ๋ญ˜ ๋ฐ›๋Š๋ƒ์— ๋”ฐ๋ผ์„œ "button"๋„ ์ด์ œ ๊ฐ€๋Šฅํ•˜๊ณ  "a"๋„ ๊ฐ€๋Šฅ!

    • ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž๋กœ ๊ตฌ์กฐ๋ถ„ํ•ดํ• ๋‹น๋œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›๊ธฐ

    • ์Šคํ”„๋ ˆ๋“œ ์—ฐ์‚ฐ์ž๋กœ react component์— props๋ฅผ ๋„˜๊ธฐ๊ธฐ

    import React from 'react'
    
    type ButtonProps<C extends React.ElementType = 'button'> = React.ComponentProps<C> & {
      as?: C
      className: string
      children: React.ReactElement
    }
    
    function Button<C extends React.ElementType>({
      as,
      className,
      children,
      ...props // as, className, children์„ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ props๋Š” props ๊ฐ์ฒด๋กœ ๋ฐ›๋Š”๋‹ค
    }: ButtonProps<C>) {
      const Component = as || 'button'
    
      return (
        <Component
          className={
            'bg-yellow-300 hover:bg-yellow-400 transition-colors duration-200 p-4 rounded-xl ' +
            className
          }
          {...props} // props ๊ฐ์ฒด์— ์žˆ๋Š” ๋ชจ๋“  prop์„ Component์— ๋„˜๊ธด๋‹ค
        >
          {children}
        </Component>
      )
    }
    
    export default Button
    

    ๋””์ž์ธ ์‹œ์Šคํ…œ

    ๋งŒ๋“œ๋Š” ์ด์œ 

    ๋””์ž์ธ์˜ ํ†ต์ผ์„ฑ!, ๊ณตํ†ต๋˜๋Š” ๋กœ์ง์„ ์žฌ์‚ฌ์šฉํ•ด์„œ ์ƒ์‚ฐ์„ฑ๋„ ๋†’์ด๊ณ , ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋„ ๋ฐ˜์˜ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ฌ(์œ ์ง€๋ณด์ˆ˜์„ฑ)

    ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

    ์ƒํ–ฅ์‹ => ๊ทธ๋ƒฅ ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ๋น„์Šทํ•ด ๋ณด์ด๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์„ ๋ฌถ์–ด์„œ ์ถ”์ถœํ•ด๋‚˜๊ฐ„๋‹ค (์ดˆ๊ธฐ!)
    ํ•˜ํ–ฅ์‹ => ํšŒ์‚ฌ ๊ทœ๋ชจ๋„ ํฌ๊ณ , ์•„๋‹ˆ๋ฉด ์ฒ˜์Œ๋ถ€ํ„ฐ ์ฒด๊ณ„๋ฅผ ๊ฐ–์ถ”๊ณ  ์‹ถ๊ณ , ๊ธฐ์กด์˜ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋‚˜ ์ดํ•ด๋„๊ฐ€ ๋†’์„ ๋•Œ!

    ์ง€๊ธˆ ๋‚˜์™€ ๊ฐ™์€ ๊ฒฝ์šฐ๋Š”, ํ† ์ดํ”„๋กœ์ ํŠธ๋กœ ์•„์ฃผ ์ž‘์€ ๊ทœ๋ชจ์ด๋ฏ€๋กœ ์ƒํ–ฅ์‹์œผ๋กœ ๋งŒ๋“ ๋‹ค.

    css reset์„ ํ•˜๋Š” ์ด์œ 

    1. ๋ธŒ๋ผ์šฐ์ €๋งˆ๋‹ค ๊ธฐ๋ณธ CSS๊ฐ€ ๋‹ค๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์•„์˜ˆ CSS๋ฅผ ์ดˆ๊ธฐํ™”์‹œ์ผœ๋†“๊ณ  ์ฒ˜์Œ๋ถ€ํ„ฐ ๋ชจ๋‘ ๋˜‘๊ฐ™์€ ํ™”๋ฉด์„ ๋ณผ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ ๋‹ค.

    2. semantic๊ณผ ์‹œ๊ฐ์  ํ˜•ํƒœ๋Š” ๋ถ„๋ฆฌ๋˜์–ด์•ผ ํ•œ๋‹ค

    variant

    ๊ฐ™์€ ์šฉ๋„์—ฌ๋„ ์‹œ๊ฐ์  ์ฐจ์ด๊ฐ€ ์žˆ์„๋•Œ๋Š” variant๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
    ํ•จ์ˆ˜์—์„œ์˜ variant์™€ ํ—ท๊ฐˆ๋ฆฌ์ง€ ๋ง ๊ฒƒ.

    ๋ฐ˜์‘ํ˜• UI์™€ tailwind

    tailwind์—์„œ ์‚ฌ์šฉํ•˜๋Š” class๋ฅผ "utility class"๋ผ๊ณ  ํ•œ๋‹ค.

    css์™€ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค! className์„ ์“ฐ๋Š” ๊ฒŒ ๋‹ค๋ฅผ ๋ฟ~

    clsx => className์„ ์—ฌ๋Ÿฌ ๊ฐœ๋กœ ์ชผ๊ฐœ์„œ ๊ตฌ๋ถ„ํ•ด์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ