디자인 시스템 개발일지 3 ( 스타일 관련 유틸 함수 정리 )

스타일 관련 유틸 함수

스타일 관련 유틸함수가 필요한 이유

  • tailwind-variant로 생성해서 지정한 props와, className을 통해 받는 props를 다 가능하게 할 경우 스타일들이 겹치는 문제가 발생한다.
  • 디자인 시스템을 구성하는 경우 default 스타일링과 사용자가 추가로 className을 입력하여 override되는 스타일이 있다.
  • 이 때, 스타일들을 적용함에 있어서 우선순위가 중요한데, 어떤 방식으로 합칠지 명시를 해주는 것이 스타일 병합 유틸의 역할이다.

<스타일 병합 유틸을 쓰지 않는 경우>

export const Component = ({className, ...props}: Props) => {
	
	const componentStyles = getComponentStyles(props)
	
	return (
	<div className = {`${componentStyles} ${className}`}>
		text 
	</div>)
}
  • 이것도 마찬가지로 동작하긴 한다.
  • props를 통해 조합된 componentStyles 기반으로
  • 하지만 단순 className의 선언 순서로 우선순위가 결정되기 때문에 안정성이 부족하고, 가독성은 좋지 않다.

clsx & twMerge

clsx

  • 조건부로 클래스 이름을 결합하고 관리하는데 사용되는 함수
  • 여러 개의 클래스를 조건에 따라 동적으로 쉽게 결합
  • 불필요한 빈 문자열이나 null, undefined 같은 값들을 자동으로 걸러준다.
    • 이전에 나왔던 className을 문자열 그대로 넣는 방식에서는 undefined 처리가 자동으로 되지 않는다.
  • ClassValue[] 타입을 input으로 받음.

<예제>

clsx(
  'btn',
  isActive && 'btn-active',
  isDisabled && 'btn-disabled'
);

twMerge

  • className 충돌 막아주면서, 겹치는 className의 경우 마지막에 선언된 className 기준 오버라이드 처리됨.
  • tailwind 유틸리티 클래스로 등록되어있지 않은 className은 자동으로 삭제해준다.
import { twMerge } from 'tailwind-merge'

twMerge('px-2 py-1 bg-red-600 p-3 bg-grey-800')
// => p-3 bg-grey-800

cn ( 개인 프로젝트에서는 mergeStyles )

  • 저 둘을 통합한 방식이다.
  • clsx로 클래스네임들을 관리하고, twMerge로 겹치는 className까지 제거
import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';

export const cn = (...inputs: ClassValue[]) => {
  return twMerge(clsx(inputs));
};