Tailwind CSS에서 color 속성 가져오기
Tailwind CSS 설정에서 색상 타입을 추출해 컴포넌트 props에 적용하는 방법
Note
이 글은 Tailwind CSS v3 환경을 기준으로 작성되었습니다.
프로젝트 전반에서 재사용할 아이콘(Icon) 컴포넌트를 설계할 때, props로 색상 속성을 받아 className에 동적으로 추가하고 싶었다.
아이콘은 SVG로 구성되어 있으며, fill과 stroke 값을 하드코딩하는 대신 Tailwind CSS의 유틸리티 클래스와 자동완성을 활용해 직관적으로 색상을 주입하는 것이 목표였다.
원하는 컴포넌트의 사용 형태는 다음과 같았다.
interface IconProps {
fill: string;
stroke: string;
}
const Icon = ({fill, stroke}: IconProps) => {
<svg className={`${fill} ${stroke} `} />
}타입을 string으로 두면 자동완성이 동작하지 않고, fill-bleu-500처럼 오타가 있는 값이 들어와도 TypeScript가 잡아주지 않는다. Tailwind CSS에 정의된 색상 클래스명을 타입으로 추출해야 이 문제를 해결할 수 있다.
Tailwind CSS는 기본 색상 목록을 tailwindcss/colors로 제공한다.
import defaultColors from "tailwindcss/colors";
export type DefaultColors = keyof typeof defaultColors;
const color: DefaultColors = 'blue'; // 자동완성 가능
다만 이 방법으로는 blue처럼 색상 키만 얻을 수 있고, blue-500처럼 명도까지 포함한 클래스명은 만들 수 없다.
resolveConfig를 사용하면 Tailwind CSS 설정 파일을 완전히 해석한 결과를 얻을 수 있다. 이를 통해 각 색상 키에 어떤 명도 값이 정의되어 있는지 확인할 수 있다.
import resolveConfig from "tailwindcss/resolveConfig";
import tailwindConfig from "web/tailwind.config.ts"; // tailwind 설정 파일
const fullConfig = resolveConfig(tailwindConfig);
fullConfig에서 얻은 색상 정보를 바탕으로, 특정 색상 키에 속한 명도 값들을 타입으로 추출한다.
type ColorShade<T extends DefaultColors> = keyof ColorConfig[T] extends
| string
| number
? keyof ColorConfig[T]
: never;
색상 키와 명도를 조합해 blue-500 형태의 클래스명 타입을 만든다. 명도가 없는 색상(예: white, black)은 색상 키만 그대로 사용한다.
type TailwindColorClass = {
[P in DefaultColors]: ColorShade<P> extends never ? P : `${P}-${ColorShade<P>}`;
}[DefaultColors];
추출한 타입을 fill과 stroke props에 적용하면, 유효한 Tailwind CSS 클래스명만 허용하는 타입 안전한 컴포넌트를 만들 수 있다.
interface IconProps {
fill: `fill-${TailwindColorClass}`;
stroke: `stroke-${TailwindColorClass}`;
}실제 서비스 환경에서는 Tailwind의 기본 색상 외에도 브랜드 컬러 등 다양한 커스텀 색상을 tailwind.config.ts에 정의하여 사용한다. 앞선 타입 정의가 커스텀 색상까지 모두 포괄할 수 있도록 구조를 살짝 다듬어 보자.
커스텀 색상들을 하나의 변수로 묶은 뒤, 기본 색상 타입과 합쳐서 새로운 Colors 타입을 만든다.
import defaultColors from "tailwindcss/colors";
const customColors = {
...formalColors,
...keyColors,
...accentColors,
...grayColor,
...theme.colors,
};
export type CustomColors = typeof customColors;
export type DefaultColors = typeof defaultColors;
export type Colors = CustomColors & DefaultColors; // 커스텀 색상과 기본 색상 병합커스텀 색상과 기본 색상을 합친 Colors 타입을 export한다.
type CustomColors = typeof color;
type DefaultColors = typeof defaultColors;
export type Colors = CustomColors & DefaultColors;Colors 타입을 기반으로 TailwindColorClass를 추출하고, IconProps에 적용한다.
import resolveConfig from "tailwindcss/resolveConfig";
import tailwindConfig from "web/tailwind.config.ts";
import type { Colors } from "web/tailwind.config.ts";
const fullConfig = resolveConfig(tailwindConfig);
const colorTheme = fullConfig.theme.colors as Colors;
type ColorConfig = typeof colorTheme;
type ColorKeys = keyof Colors;
type ColorShade<T extends ColorKeys> = keyof ColorConfig[T] extends
| string
| number
? keyof ColorConfig[T]
: never;
type TailwindColorClass = {
[P in ColorKeys]: ColorShade<P> extends never ? P : `${P}-${ColorShade<P>}`;
}[ColorKeys];
interface IconProps {
fill: `fill-${TailwindColorClass}`;
stroke: `stroke-${TailwindColorClass}`;
}이렇게 Tailwind CSS의 resolveConfig와 TypeScript의 유틸리티 타입을 활용하여, 설정 파일에 정의된 색상 토큰을 컴포넌트의 Props 타입으로 완벽하게 추출하고 적용해 보았다.
string 타입으로 열어두었던 색상 props를 Tailwind CSS 설정에서 직접 추출한 타입으로 교체하여 자동완성과 타입 검사를 동시에 챙길 수 있었다.