[React]twin.macro 사용(with typescript)

Inung_92·2024년 2월 22일
1

React

목록 보기
14/15
post-thumbnail

⚡️ twin.macro

📖styled-components와 tailwindcss를 통합하여 사용할 수 있도록 지원하는 라이브러리

사용 비교

twind.macro 미사용

import styled from 'styled-components';

// styled-components 정의
const TestDiv = styled.div`
	color: white;
	width: 100%;
	height: 50%;
	...
`;

// 사용
const TestComponent = () => {
	...
	return (
		...
		// tailwindcss 적용
		<TestDiv className='bg-blue-950'>테스트</TestDiv>
	);
};

twin.macro 사용

import tw from 'twin.macro';
import styled from 'styled-components';

// 통합하여 styled-components 정의
const TestDiv = styled.div`
	${tw`
		text-white
		bg-blue-950
		w-full
	`}
	...css 작성
`;

// 사용
const TestComponent = () => {
	...
	return (
		...
		<TestDiv>테스트</TestDiv>
	);
};

twin.macro의 이점은 styled-componentstailwindcss를 통합하여 가독성이 좋고, 유연성이 뛰어난 컴포넌트를 생성할 수 있으며 조건부 스타일링 적용을 통한 다양한 시도를 할 수 있다는 점이다. 또한, 개발자의 피로도를 줄여주고 스타일링에 소모되는 에너지를 줄이는데 도움을 준다.


⚡️ 의존성 설치

npm install -D styled-components @types/styled-components tailwindcss

// tailwind.config.js 생성
npx tailwindcss init

npm install -D babel-plugin-macros twin.macro

⚡️ 설정

Tailwindcss

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./index.html", "./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

babel

// babel-plugin-macros.config.js
module.exports = {
  twin: {
    preset: "styled-components",
  },
};

package.json

...다른설정
  "babelMacros": {
    "twin": {
      "preset": "styled-components"
    }
  },
...

CSS 파일

// index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

index.css 파일을 제외한 3개의 파일은 root 디렉토리(src 상위)에 위치시키면 된다.


⚡️ 사용

일반적 사용

import tw from 'twin.macro';
import styled from 'styeld-components';

// 일반적 사용
const TestDiv = styled.div`
	${tw`
		bg-blue-200
	`}
`;

조건부 스타일링

import tw from 'twin.macro';
import styled from 'styeld-components';

// 조건부 스타일링
// 1. 백틱
const TestDiv = styled.div`
	${({hasColor}) => hasColor ? tw`bg-red-300` : tw`bg-blue-300`}
	${tw`
		....다른 클래스
	}
`;
// 2. 배열
const TestDiv = styled.div(({hasColor}) => [
	hasColor ? tw`bg-red-300` : tw`bg-blue-300`,
	tw`
		text-white
	`
]);

// typescript 사용
// 1. 백틱
interface DivProps {
	test?: boolean // ?를 사용하지 않을 경우 'test'라는 props을 무조건 전달해야함.
};

const TestDiv = styled.div<DivProps>`
  ${({ test }) => (test ? tw`bg-blue-200` : tw`bg-red-300`)}
`;
// 2. 배열
const TestDiv = styled.div<DivProps>(({ test = false }) => [
  test ? tw`bg-blue-200` : tw`bg-red-300`,
]);

상속

import tw from 'twin.macro';
import styled from 'styeld-components';

const TestDiv = styled.div<DivProps>`
  ${({ test }) => (test ? tw`bg-blue-200` : tw`bg-red-300`)}
`;

const TestBox = styled(TestDiv)`
  ${tw`text-orange-500`}
`;

⚡️ 에러 해결

인자 타입 호환성 에러

Argument of type '({ test }: { test: boolean; }) => TwStyle[]' is not assignable to parameter of type 'Styles<FastOmit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>'. Type '({ test }: { test: boolean; }) => TwStyle[]' is not assignable to type 'StyleFunction<FastOmit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>>'. Types of parameters '__0' and 'executionContext' are incompatible. Property 'test' is missing in type 'ExecutionContext & FastOmit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>' but required in type '{ test: boolean; }'.

위와 같이 에러가 발생하는 경우는 다음과 같이 코드를 작성했을 가능성이 높다.

const TestDiv = styled.div(({test}: {test: boolean}) => [
	...style code
]);

//or

const TestDiv = styled.div(({test}: {test: boolean})`
	...style code
`);

위 코드에서 에러가 발생하는 원인은 다음과 같다.

  • 함수 시그니처 불일치 : styled-components의 스타일 함수가 받아들일 수 있는 인자의 타입과 일치하지 않다.
  • ExecutionContext 타입 불일치 : styled-components 에서 스타일 함수로 전달되는 실행 컨텍스트와 사용자가 정의한 객체 타입 간의 호환성이 없다. styled-components는 props를 포함한 executionContext 객체를 전달해야하는데 위에서 전달한 props는 executionContext에 존재하지 않는다.
    결론적으로 타입스크립트의 타입 체크를 통과할 수 없어서 발생하는 문제이며, 이 문제를 해결하기 위해서는 제네릭 타입으로 정확히 어떤 prop 타입을 넘길 것인지 명시해야한다. 아래 코드를 참고하자.
// props 타입 선언
interface DivProps {
	test?: boolean // ?를 사용하지 않을 경우 'test'라는 props을 무조건 전달해야함.
};

// 백틱
const TestDiv = styled.div<DivProps>`
  ${({ test }) => (test ? tw`bg-blue-200` : tw`bg-red-300`)}
`;
// 배열
const TestDiv = styled.div<DivProps>(({ test = false }) => [
  test ? tw`bg-blue-200` : tw`bg-red-300`,
]);
profile
서핑하는 개발자🏄🏽

0개의 댓글