본문 바로가기
react

Tailwind CSS 알아보기

by HomieKim 2023. 7. 21.

예전에 스타일링 방식에 관에서 글을 쓴적이 있는데 

2022.09.02 - [react] - CSS-in-JS vs CSS-module vs Post CSS 차이 구분하기

 

CSS-in-JS vs CSS-module vs Post CSS 차이 구분하기

리액트 기반의 프론트엔드를 공부하다 보면 다양한 스타일링 방식을 알게됩니다. 프로젝트를 진행하며 다양한 방식의 스타일링을 사용해보려 했고 CSS-in-JS 방식과 CSS-Module 방식을 주로 사용하

hoime.tistory.com

utility first 방식의 스타일링에 관심이 생겨서 공부해 보려고 한다.

최근 진행 했던 프로젝트들을 대부분 CSS-in-JS 방식을 사용했고 개인적으로 개발 경험이 굉장히 좋다고 생각한다.

특히 컴포넌트를 만들다 보면 props에 따라서 동적으로 스타일을 줄 때가 많은데 물론 classnamse 라던가.. 꼭 CSS-in-JS 방식이 아니여도 구현이 가능하지만

스타일드 컴포넌트 내에서 props로 핸들링하는 것이 코드 가독성 면에서나 개발 생산성 측면에서 개인적으로는 더 좋다고 생각한다

 

다만, 최근에 next 13 버전 사용하면서 서버컴포넌트 내에서 css 로드하는데 이슈가 좀 있는 것 같다. 

20230721 기준 공식문서

물론, 최근에는 styled-components 및 다른 CSS-in-JS 라이브러리를 계속해서 지원하고 있지만 

emotion은 아직 지원이 제대로 안되고 issue에 나와있는데로 registry 파일 만들어서 사용해도 로드시에 css가 늦게 로드되는 현상이 있었다.

사실 css-in-js 방식이 편하긴 하지만 런타임에 로드 되기때문에 next 13 은 물론 앞으로 리액트가 버전업 하면서 RSC 방식이 본격적으로 도입되면 CSS-Module 이나 Tailwind 처럼 런타임 이전에 css를 생성해줄 수 있는 방식이 조금 더 적합하지 않는가? 생각이 들어 공부해 보려 한다.

 


✔️ Tailwind 란?

생각 보다 공식문서에 설명이 빈약하다.. 

Tailwind CSS works by scanning all of your HTML files, JavaScript components, and any other templates for class names, generating the corresponding styles and then writing them to a static CSS file.

It's fast, flexible, and reliable — with zero-runtime.

핵심은 utility-first 라는 컨셉, 흔히 사용되는 bootstrap 생각하면 된다. 

개발자가 직접 클래스 네임을 지정하고 css 파일을 만들어서 적용 하는 것이 아니라 미리 만들어둔 css 집합들을 지정된 클래스 네임을 사용해서 불러 오는 것이라고 생각하면 된다! 예시를 보는게 가장 빠른데

<div class="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4">
  <div class="shrink-0">
    <img class="h-12 w-12" src="/img/logo.svg" alt="ChitChat Logo">
  </div>
  <div>
    <div class="text-xl font-medium text-black">ChitChat</div>
    <p class="text-slate-500">You have a new message!</p>
  </div>
</div>

이런식으로 미리 정의된 클래스 네임을 사용하면 해당 클래스 네임에 맞는 css가 적용되는 것

 

🧐 어떻게 동작하는 건지?

사실 CRA든 next 든 프로젝트 init할 때 해당 프레임워크에서 제공하는 템플릿 사용하면post-css나 tailwind 알아서 다 설치해주고,
위 예제 처럼 공식문서 나 cheat shteet 를 보고 필요한 클래스를 적용하면 끝입니다~

 

 물론 그냥 사용하면 좋지만 이상하지 않나요? css 파일을 작성해서 import 한 것도 아니고.. style 태그를 작성한 것도 아니고 그냥 className 만 적어주면 css가 알아서 생성된다고..? 마법인가..? 

사실 tailwind 패키지를 설치한 후 별도의 빌드 프로세스를 거쳐 해당 클래스에 해당하는 css 파일을 생성해줍니다!
빌드된 결과물에 보면 생성된 css 파일에 tailwind 에서 사용되는 클래스가 포함된 css 파일을 볼 수 있습니다.

next로 tailwind 사용하는 프로젝트 하나 만들어서 빌드해보니까 layout.css 파일이 생기고
내부에 tailwind에서 사용되는 클래스 네임들이 있는걸 볼 수 있음!

 

🧐 커스텀 하기 어려운거 아니야?

사실 개발하다 보면 디자이너가 제공해준 피그마 파일을 보고 해당 디자인에 맞춰서 개발하는 경우가 보통일 것이다. 

그러면 디자인 요구사항에 맞는 커스텀한 컴포넌트가 필요할 텐데, 

커스텀하기 어려운거 아니야? 라는 생각을 할 것 이다. (나만 그런 걸 수도 있다.)

별도의 디자인 요구사항이 있는게 아니라면 MUI나 antd 같은 디자인 프레임워크를 쓰는게 빠르고,
내가 개발하려는 UI에 최적화된 컴포넌트를 디자인하기위해 DX 가 더 좋은 CSS-in-JS를 선택하곤 했다.

막상 공부해보니 커스터마이징에 대한 가이드가 상세하게 나와 있습니다.

 

📌 Theme 커스텀

예를 들어 font-size 적용 시 tailwind에서는  text-{size}  형식의 className을 사용합니다.

ex) `text-sm`, `text-lg` ...

미리적용 되어 있는 css말고 내가 원하는 폰트 사이즈를 적용시키고 싶다면 어떻게 할까요?

대괄호 내에 직접 값을 입력할 수 있는데 이를 tailwind 에서는 Arbitrary values 라고 합니다.

<>
      <p className="text-sm ">The quick brown fox ...</p>
      <p className="text-lg">The quick brown fox ...</p>
      <p className="text-xl">The quick brown fox ...</p>
      <p className="text-2xl">The quick brown fox ...</p>
      {/* arbitrary values */}
      <p className="text-[36px]">The quick brown fox ...</p>
</>

아마 여기서 저와같은 생각을 한 사람이 많을 겁니다.

tailwind -> onDemand 라며? -> 미리 만들어둔 css를 className으로 적용 -> 근데 저렇게 동적으로 value넣으면?? -> CSS-in-JS처럼 런타임에 실행됨? -> 아니면 빌드할 때 저것만 또 만듬? -> ???

 

찾아 보니 JIT 모드(Just-In-Time) 라고 말그대로 빌드 프로세스에 저런 임의의 값을 포함에서 프로젝트 내에 사용하고 있는 css만 포함해서 번들에 추가한다고 합니다. 굿...

 

value를 커스텀 할 수 있는 다른 방법이 또 있는데요, CSS-in-JS에서 Theme Provider를 사용 하듯이, 사용자 지정 테마를 미리 지정하여 사용 하는 방법이 있습니다. 

// tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      fontSize: {
        test: "30px",
      },
      
    },
  },
  plugins: [],
};

// JSX 내에서 사용 시 30px 적용 됨
  <p className="text-test">The quick brown fox ...</p>

※ 단, 사용시 주의 사항이 있다면 theme 프로퍼티 안에 extends 로 설정을 해야 확장 되며,

theme 하위에 바로 속성을 적용할 경우 기본 tailwind 규칙이 무시 됩니다. 

 

문서 내에 커스텀 가이드를 봤을 때 이외에도 plugin이나 @layer 등 사용하는 방법도 있으나 이건 tailwind 기본 값을 수정하는 게 아니라 확장하세 사용하는 방법 인 것 같습니다. 

정리하자면, 기본 값이 아닌 내가 원하는 값으로 수정 하는 방법에는 크게 두가지

  1. Arbitrary value
  2. Theme config

방법을 사용하면 됩니다.

 

🧐 가독성이 너무 안좋은거 아니야?

가독성이 진짜 마법의 단어 입니다. 개인적으로 가독성을 굉장히 중요하게 생각합니다만,
이게 또, 주관적인 것이 기도 하고.. 가독성 안좋은 대신에 생산성이 더 좋으면 된거 아니야? 하면 또 할말 없고.. 

참 애매모호 합니다. 실제로 이렇게 className에 길게 스타일을 늘여놓는게 가독성을 해친다고 개인적으로 생각했었습니다.

 

이러한 문제를 해결할 수 있는 방법을 찾아보다가 

여러 utilities 들을 그룹화 하거나 css를 적용할 수 있는 방법을 찾았습니다.

📌 Using CSS and @layer

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .card {
    padding: 8px 12px;
    border-radius: 8px;
    @apply text-2xl m-5 text-gray-50 bg-blue-300
  }
}

// jsx 에서 사용할 때
return(
	<div className='card'>Card Component</div>
)

이런식으로 특정 클래스네임을 만들어서 css를 직접 지정할 수 도 있고 @apply로 유틸리티 클래스도 적용하여

컴포넌트화 할 수 있습니다.

이렇게 사용 하면 재사용성도 높아지고 클래스 네임이 길어지는 걸 막아 코드 가독성도 더욱 좋아질 수 있습니다!

 

이렇게 해결? tailwind로 프로젝트를 해본적 없긴한데 개인적인 생각으로 저렇게 많이 쓰진 않을 것 같다.

저 코드를 다르게 사용하면

// Card 컴포넌트 직접 생성
const Card = (props) => {
	return (
    <div clssName='py-3 px-2 text-2xl m-5 text-gray-50 bg-blue-300 rounded-lg'>
    	{props.children}
    </div>
    )
}

// styled-components 였다면 대충 요론 느낌 이겠지
export const Card = styled.div`
    padding :8px 12px;
    border-radius: 8px;
    margin:1.25rem;
    color: {color}
    background-color: {color}
`

만약에 내가 개발한다면 이런 방식으로 컴포넌트를 생성해서 재사용할 것 같다 그냥.


이쯤에서 정리

  • 그래도 색상 디폴트 값이라 폰트 사이즈 rem 적용 되어서 쓰는 거는 tailwind가 확실히 생산성이 좋을 것 같다.
  • 그래도 컴포넌트 단위로 재사용할 수 있다는게 CSS-in-JS가 좋은 것 같다.

사실 이런 장단점 때문에 요즘엔 twin.macro 이거 많이 사용하는 것 같다.

글이 너무 길어져서 twin.macro는 공부 조금 더 해보고 한번 써보고 따로 글 작성 해야 할 듯

 

 

댓글