본문 바로가기

프로젝트/dowith

twin.macro 도입 및 Tailwind CSS + styled-components

- dowith 프로젝트를 시작하면서 프론트엔드 동료와 스타일링을 위한 도구로 Tailwind CSS를 선택했다.

- Tailwind CSS를 선택하게 된 가장 큰 이유는 "편리함"이었다.

- Tailwind CSS는 다양한 스타일 속성을 작은 유틸리티 클래스 형태로 미리 정의해 제공하기 때문에 유틸리티 클래스들을 조합하여 원하는 디자인을 즉시 적용할 수 있기 때문에 편리하다.

- 하지만, Tailwind CSS를 사용하면서도 불편한 점이 있었고 회의 끝에 새로운 방식을 도입하였고 그 내용에 대해 설명해보겠다.

HTML 태그만 보고 이 태그가 어떤 역할을 하는 요소인지 직관적으로 파악하기 어렵다는 점입니

문제 정의

- Tailwind CSS를 사용하면서 우리 팀이 느낀 불편한 점은 바로 "가독성"이었다.

- Tailwind CSS는 미리 정의된 유틸리티 클래스를 조합해 사용하므로 간편하다는 장점이 있지만, 컴포넌트가 조금만 복잡해져도 HTML 태그만 보고 해당 태그가 어떤 역할을 하는 요소인지 직관적으로 파악하기 어려웠다. 즉, "가독성"이 현저히 떨어진다는 불편함이 있었다.

- 아래 예시를 한번 보자

export const HomePage = () => {
  return (
    <div className="flex w-full flex-col items-start">
      <div className="flex items-center justify-between">header</div>
      <div>홈</div>
      <div className="flex w-full md:flex-col">
        <div className="flex grow flex-col items-start gap-5 p-5 md:gap-3 lg:max-w-[500px] xl:max-w-[500px]">
          <div className="flex w-full flex-col items-start gap-2">
            <div className="flex w-full items-center justify-between">
              <div className="flex items-center gap-2">
                <span className="pt-1 text-B20 text-title md:text-B16">
                  참여중인 스페이스
                </span>
              </div>
              <button className="relative flex h-10 w-10 items-center justify-center rounded-md border border-line p-2 md:h-8 md:w-8">
                <div className="absolute left-7 flex h-5 w-5 items-center justify-center rounded-full border border-line bg-white p-1 top-[-10px] md:left-5 md:h-4 md:w-4 md:top-[-8px]">
                  <span className="text-B12 text-red md:text-M10">1</span>
                </div>
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

- 위 코드는 우리 프로젝트의 HomePage인데, 간단한 레이아웃 작업만 진행된 상태인데도 수많은 div 태그 속에서 각 div 태그들이 어떤 역할을 하는지 직관적으로 파악하기 어렵다고 느껴지는데, 혼자하는 프로젝트가 아닌 이상 팀 동료가 내 코드를 보고 컴포넌트를 파악하기는 더더욱 어려울 것이라고 판단된다.

해결 방안

- 우리 팀에서 회의 끝에 생각해낸 해결 방안은 twin.macro 라이브러리를 사용해서 Tailwind CSS와 styled-components를 결합해서 사용하는 것이었다.

- twin.macro 라이브러리란 Tailwind CSSstyled-components 또는 emotion 같은 CSS-in-JS 라이브러리를 함께 사용할 수 있게 해주는 도구로써, 유틸리티 클래스 기반 스타일링(Tailwind CSS)과 CSS-in-JS의 장점을 결합하여 사용할 수 있다.

- 즉, 우리 팀이 겪고 있는 가독성 저하 문제를 styled-components로 해결하면서, 동시에 Tailwind CSS의 간편한 스타일링 방식도 함께 활용할 수 있게 되었다.

 

- twin.macro를 적용하여 만든 간단한 예시는 다음과 같다.

import tw from 'twin.macro';

const Container = tw.div`flex h-screen items-center justify-center bg-gray-100`;

const Box = tw.div`rounded-lg p-4 text-white shadow-lg`;

const Test = () => (
  <Container>
    <Box>
      <p>Hello, twin.macro!</p>
    </Box>
  </Container>
);

- styled-components와 같이 HTML 태그에 변수명을 정의할 수 있어 가독성을 향상시킬 수 있고 Tailwind CSS의 간편한 스타일링 또한 적용할 수 있다.

- 간단한 사용법 외에도 깃헙 공식문서를 보면 다양한 사용법이 있다.

- 우리 팀은 아직 간단한 레이아웃 단계에서만 적용했기 때문에 추가적인 사용방법은 추후에 추가 기록할 예정이다.

실제 적용

import tw from 'twin.macro';

export const HomePage = () => {
  return (
    <div className="flex w-full flex-col items-start">
      <div className="flex items-center justify-between">header</div>
      <div>홈</div>
      <ContentWrapper>
        <JoinedSpaceSection>
          <JoinedSpaceSectionHeader>
            <TitleAndWaitButtonWrapper>
              <JoinedSpaceTitleWrapper>
                <JoinedSpaceTitle>참여중인 스페이스</JoinedSpaceTitle>
              </JoinedSpaceTitleWrapper>
              <WaitingSpaceDropdownButton>
                <WaitingSpaceCountWrapper>
                  <WaitingSpaceCount>1</WaitingSpaceCount>
                </WaitingSpaceCountWrapper>
              </WaitingSpaceDropdownButton>
            </TitleAndWaitButtonWrapper>
          </JoinedSpaceSectionHeader>
        </JoinedSpaceSection>
      </ContentWrapper>
    </div>
  );
};

const ContentWrapper = tw.div`flex w-full md:flex-col`;

const JoinedSpaceSection = tw.div`flex grow flex-col items-start gap-5 p-5 md:gap-3 lg:max-w-[500px] xl:max-w-[500px]`;
const JoinedSpaceSectionHeader = tw.div`flex w-full flex-col items-start gap-2`;
const TitleAndWaitButtonWrapper = tw.div`flex w-full items-center justify-between`;
const JoinedSpaceTitleWrapper = tw.div`flex items-center gap-2`;
const JoinedSpaceTitle = tw.span`pt-1 text-B20 text-title md:text-B16`;

const WaitingSpaceDropdownButton = tw.button`relative flex h-10 w-10 items-center justify-center rounded-md border border-line p-2 md:(h-8 w-8)`;
const WaitingSpaceCountWrapper = tw.div`absolute left-7 flex h-5 w-5 items-center justify-center rounded-full border border-line bg-white p-1 top-[-10px] md:(left-5 h-4 w-4 top-[-8px])`;
const WaitingSpaceCount = tw.span`text-B12 text-red md:text-M10`;

 

- 이전에 Tailwind CSS만을 사용했을 때의 코드와 비교했을 때, 각 HTML 태그가 하는 역할이 직관적으로 파악되고 "가독성"이 향상되어  전보다 다른 사람도 코드를 이해하기에 수월하다.

Trouble Shooting

- twin.macro를 적용하면서 발생한 문제는 기존에 Tailwind CSS 사용을 위해 설치했던 eslint-plugin-tailwindcss와 twin.macro의 자동 코드 포맷팅클래스 정렬의 일관성을 유지하기 위해 설치한 prettier-plugin-twin.macro 충돌 문제였다.

- 이는 eslint-plugin-tailwindcss에서의 순서 규칙과 prettier-plugin-twin.macro에서의 순서 규칙이 다르기 때문으로 판단되는데, 팀원과 회의를 통해 twin.macro를 사용하기 때문에 eslint-plugin-tailwindcss를 제외하고 prettier-plugin-twin.macro를 적용하는 것으로 결정하였다.


학습 단계로 잘못된 정보가 있을 수 있습니다. 잘못된 부분에 대해 알려주시면 정정하도록 하겠습니다

참고 : https://github.com/ben-rogerson/twin.macro

https://fe-developers.kakaoent.com/2022/221013-tailwind-and-design-system/

https://velog.io/@jun_53/Tailwind-CSS-twin.macro-%EC%84%A4%EC%B9%98-%ED%95%98%EA%B8%B0-with-react-next

https://velog.io/@94applekoo/Tailwind-CSS-2.-Tailwind-Styledcomponent-%EC%B4%9D%EC%A0%95%EB%A6%AC