TailwindCss vs StyledComponent

박채윤·2024년 3월 15일
2

들어가면서...

이번 과제에서는 동일한 컴포넌트를 두가지 라이브러리를 사용해서 생성했다.

공용컴포넌트를 생성하는 과제였기 때문에 props를 통해서 재사용가능 하도록 설계했다.

🙌설계

  • 1.purpose (input의 사용목적)
    • input의 사용목적에따라 넓이를 네가지로 설정.
  • 2.hasButton (검색이필요한 input이라면)
    • 클릭할 버튼이 필요할 때를 위한 button의 유무.
  • 3.hasIcon (버튼에 icon이필요한경우)
    • 버튼에 text대신 icon이 필요한 경우

간단하게 목적에 따라 세가지 정도의 dseign을 결정하는 props를 전달받아
형태를 결정 할 수 있게 설계했다.

😱그 전에 간단하게 두가지 라이브러리에대해 알아보자.

StyledComponents

😘장점?

가장 큰 장점은 별도의 CSS 파일을 만들지 않고 하나의 파일 안에 스타일을 관리 할 수 있다는 점이다.

 ex) const Wrapper = styled.div`
 	 display: flex;
 	 justify-content: center;
 	 align-items: center;
`

위와같이 style은 따로정의 하기 때문에 코드의 가독성을 해치지 않고 한파일에서 관리 할 수 있다는 것이 가장 큰 장점으로 보인다.

😂단점?

아쉬운 점은 스타일을 가진 tag를 만들때마다 이름을 지어주어야한다...
프로젝트를 진행하게되면 수 많은 컴포넌트를 생성하는데 매번 이름을 지어주어야하는 고민에 빠지게된다.
또한 팀원들과 정확한 convetion을 정하지 않는다면 같은 역할을 하는 컴포넌트의 이름이 제각각이 될 수 있다.

 	 display: flex;
 	 justify-content: center;
 	 align-items: center;
`

위의 예시처럼 자식요소들을 가운데로 모아주는 tag를 생성할때
CenterWrapper, CenterContainer, CenterWrap, CenterBox ....등등
작성자마다 다른 이름을 붙히는 경우가 발생한다.

TailwindCss

😘장점?

장점은 빠른 코드작성이 가장 큰 장점이라 생각된다.

이전 작업들에선 styled-components를 사용해서 매번 tag이 이름을 선언해주고 또한 해당 태그가 어떤 style이적용 되었는지 확인하기 위해서는 다시 선언부에 가서 확인,수정을 해줘야했다.
하지만 tag선언과 동시에 style을 작성하는 tailwind-css덕분에 간편하게 작성할 수 있엇다.

또한 tailwind css intellisense 은 자동완성을 지원해주는 extension이다.
이를 통해 생산성이 올라가는 장점이 있었다.

😂단점?

모두가 꼽는 아쉬운 점은 바로 가독성 이다. 다양한 옵션, 기능이 존재하는 컴포넌트 일수록
점점 옆으로 길어지는 style속성에 코드가 눈에 들어오지 않는 점이다.

또한 제공된 utility class를 사용하기 때문에 모르는 속성들을 직접 찾아가며 작성해야 하는 점이다.

😎지금까지 두 라이브러리의 장단점을 간단하게 살펴봤는데 실제 같은 컴포넌트를 만들땐 어떤 차이를 보일까? 다음 코드를 보며 알아보자.

Input(with styled-componet)

앞서 설계했던 것 처럼 공용컴포넌트 input을 생성했다.

const MBInput = ({
  purpose,
  hasButton = false,
  placeHolder,
  buttonName = "버튼",
  hasIcon = false,
  ...props
}) => {
  return (
    <Wrapper>
      <CustomInput
        purpose={purpose}
        placeholder={placeHolder || "palceHolder"}
        {...props}
      ></CustomInput>
      {hasButton && (
        <ButtonBase hasIcon={hasIcon}>
          {hasIcon ? (
            <IconBase src="https://svgsilh.com/svg/1093183.svg" />
          ) : (
            buttonName
          )}
        </ButtonBase>
      )}
    </Wrapper>
  )
}
export default MBInput

const Wrapper = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
`
const CustomInput = styled.input<InputCssType>`
  height: ${FONT_SIZE.small};
  padding: 1rem;
  cursor: pointer;
  border: 1px solid ${COLOR.grayScale[500]};
  border-radius: 1.5rem;
  ${({ purpose }) => InputCss[purpose as PURPOSE_TYPE]}
  &:hover {
    background-color: ${COLOR.theme.main.light};
  }
`
const ButtonBase = styled.button<ButtonCssType>`
  min-width: 2rem;
  min-height: 2rem;
  position: relative;
  right: 3.5rem;
  border: none;
  background-color: transparent;
  &:hover {
    transform: scale(1.2);
    background-color: transparent;
  }
  ${({ hasIcon }) =>
    !hasIcon &&
    css`
      border-radius: 8px;
      border: 1px solid ${COLOR.grayScale[600]};
      &:hover {
        transform: scale(1.2);
        background-color: ${COLOR.theme.mainOppsite.light};
      }
    `}
  cursor: pointer;
`
const IconBase = styled.img`
  width: 2rem;
  height: 2rem;
`

위와 같이 코드를 작성하여 input컴포넌트를 생성했다. 위의 코드를 보면 분기가 생성됬기 때문에 너무 코드가 길어지고 props를 넘겨주기위해 매번 ${({})=> } 의 형태로 작성해줘야 하는 번거로움이 보인다.
또한 현재는 style만 포함된 코드이지만 해당 input태그의 기능을 하는 logic이 포함된다면?
너무 긴 코드가 작성될 것이고 그것이 불편해 따로 css파일을 분리해서 관리한다면
styled-components의 장점이 사라지는 것 아닐까? 라는 생각이 든다.

그렇다면 동일한 컴포넌트를 tailwind-css를 사용해 만들어 보자.

Input(with tailwind-css)

const MBInput = ({
  pusrpose,
  shape = "rectangle",
  iconUrl,
  hasButton = false,
  hasIcon = false,
  buttonText = "버튼",
}) => {
  return (
    <div className={WrapperVarinats({ pusrpose, shape })}>
      <input className="w-[80%] outline-none bg-transparent text-ellipsis" />
      {hasButton &&
        (hasIcon ? (
          <img
            className="w-8 h-8 cursor-pointer  hover:scale-125"
            src={iconUrl || `https://svgsilh.com/svg/1093183.svg`}
          />
        ) : (
          <button className="w-[8rem] h-fit  rounded-[5rem] border-[1px] border-solid border-grayScale-600 hover:bg-theme-mainOppsite-light">
            {buttonText}
          </button>
        ))}
    </div>
  )
}

export default MBInput

const WrapperVarinats = cva(
  "h-[5rem] pl-[1rem] pr-[1rem] flex items-center justify-between border-solid border-[1px] border-grayScale-600 focus-within:bg-theme-main-light ",
  {
    variants: {
      pusrpose: {
        search: "w-[30rem] ",
        resgisterSmall: "w-[20rem]",
        resgisterNormal: "w-[50rem]",
        resgisterLarge: "w-[100rem]",
      },
      shape: {
        round: "rounded-[5rem]",
        rectangle: "",
      },
    },
  }
)

위의 코드는 tailwind-css로 작성한 input컴포넌트이다.앞서 봤던 styled-components의 코드와 비교를 해보자.

우선 가장 눈에띄는 점은 수가 짧아진 점이다. tag의 이름을 선언해주는 부분이 사라지면서 코드가 간결해진 느낌을 받는다.

이전 코드에서는 분기처리를 하기위해 InputCss = {key:css``} 의 형태를 사용해서 분기처리를 한반면 현재 코드에서는 cva(class-variance-authority)를 통해 분기처리를 해준것을 확인할 수 있다.

cva란?
간단하게 설명하자면 props에 의해 분기가 생성되면 해당 case에 맞는 css를 적용해준다.
cva("",{}) 형태로 작성하는데 첫번째 parameter에는 공통된 css를 작성해주고
두번째 parameter에는 {variants:{}} variants아래 객체형태로 분기에 맞는 css를 작성해주면된다.

여기까지 두 라이브러리의 간단한 장단점, 실제 코드를 보며 비교를 해봤는데 과연 어떤 라이브러리가 더 사용하기 좋은 것일까?

🎉승자는?

무조건 좋은 라이브러리는 없다고 생각한다. 개발자의 환경, 역량에 맞는 라이브러리를 선택하게 될탠데
개인적으로는 tailwind-css 에 한표를 주고싶다.
utility class를 알고 있어야만 작성할 수 있고, 코드의 가독성이 떨어지는 단점이 있는 반면에
보다 간결한 분기처리, 자동완성과 제공된 키워드를 활용한 style적용, 또한 tag의 이름을 고민할 필요 없이 작성할 수 있다는 장점이 나에게는 크게 다가왔다.

이번 과제에서 처음 사용해 보는 라이브러리임에도 더 좋다고 느끼게 만드는 장점이 분명히있는 라이브러리라고 생각이된다.

글을 마치며...

다양한 css라이브러리중 많이 사용되는 두가지의 라이브러리만 비교해봤지만 내가 추구하는 이상적인 개발자가 되기 위해서는 더 많은 경험을 해봐야 할것이다.
지금까지 styled-components가 최고의 라이브러리인줄 알았지만 tailwind-css를 사용하며 더 좋은 것이 있다는 생각을 했다.
다양한 경험을 통해 최선의 결정을 내릴 수 있는 개발자가 되면 좋겠다.

👏bonus

위에서 다루지않은 내용중에 clsx와 merge에 관한내용이 있다.
tailwind-css를 보다 잘 활요하게 해주는 것들인데 링크를 남겨두었으니 관심이있다면 참고해서 성장하는데 도움이 되었으면 좋겠다.

작성자 걍걍규's velog

profile
왕이될 상인가

0개의 댓글