Vanilla Extract 알아보기

Minha Kang·2024년 12월 22일
0

Web

목록 보기
2/2
post-thumbnail

런타임 CSS-in-JS의 한계를 극복하기 위해 등장한 Vanilla Extract. (2021년 등장, 2022년부터 알려지기 시작)
Vanilla Extract는 CSS-in-JS가 아닌 CSS-in-TS 이다.

  • CSS-in-JS는 들어봤는데 TS는 뭐지 ?

    특별한건 아니고, 타입스크립트로 스타일을 작성한다 해서 CSS-in-TS라고 한다.

1️⃣ 핵심, Zero-Runtime CSS tool


  • 런타임이 아닌 빌드 타임에 CSS로 변환.
  • 스타일드컴포넌트, 이모션 → 런타임에 JS로 작성한 코드를 CSS변환
  • Vanilla Extract → 제로런타임 CSS 툴 , 빌드 타임에 CSS로 변환시켜준다.
    • 또한 타입스크립트로 작성하기에 오타, 미리 선언한 변수명을 잘못 작성할 경우 정적 타이핑으로 오타들을 다 잡아준다 → DX에서 장점을 가진다.

Zero-Runtime인 CSS-in-JS 로는 PandaCSS도 존재한다 ! 🐼

https://panda-css.com/

2️⃣ 런타임 vs 제로 런타임의 차이


런타임 CSS

일반 CSS-in-JS는 런타입에 JS로 작성한 코드를 CSS로 변환한다.
JS파일이 실행되며 style을 생성하기 때문에, style의 규모가 커질수록 성능상 문제가 더욱 커진다.

💡 기본적으로 컴포넌트가 렌더링될 때 새로운 스타일을 동적으로 계산해주는 방식으로 동작
→ 이는 근본적으로 성능에 좋지 않다.

styled-components로 작성한 코드

  • 버튼을 눌렀을 때 동적으로 스타일링이 추가된 것을 확인할 수 있다.

제로 런타임 CSS

Vanilla Extract로 작성한 코드

동일한 기능을 Vanilla Extract로 작성한 코드이다.

  • 빌드타임에 모든 스타일이 계산되는 것을 확인할 수 있다. (버튼을 눌렀을 때의 스타일을 미리 계산)
    - HTML과 CSS의 초기 로딩이 완료되면 style 태그에서 확인 가능하다.

💡 정리해보자면 …

Zero-runtime CSS빌드 시점에 CSS를 생성한다.
→ 런타임에는 추가적인 JS 실행 없이 이미 생성된! CSS를 사용하게 된다. 즉, 브라우저가 페이지를 렌더링할 때 CSS를 즉시 사용할 수 있다.
런타임 성능 개선, 더욱 빠른 페이지 로딩

3️⃣ 또 다른 장점, 근데 이제 개발자 친화적인


  • Vanilla Extract는 타입스크립트를 전처리기로 이용.
  • CSS 코드를 styles.css.ts 와 같은 ts 확장자를 가진 파일로 작성
    • → 모든 스타일에 대해 타입 추론이 가능. (컴파일 타임 → 오타 발생시 체크 가능)

  • CSS 오타와 같은 휴먼 에러가 컴파일 단계에서 잡힘 → 개발 생산성 증가 (DX 📈)

참고) https://techblog.jobkorea.co.kr/vanilla-extract-%EB%8F%84%EC%9E%85%EA%B8%B0-b61c38aaef36

4️⃣ 특징


  • 지역범위 적용

사용된 변수는 locally scoped 되어 다른 .css.ts 파일에서 선언된 변수를 사용하더라도 서로 충돌이 일어나지 않음

  • css속성에 kebab-case 대신 camelCase적용

  • 일부 속성에 단위 생략 가능 (생략시 px로 변환)

5️⃣ 기본문법


import { globalStyle } from '@vanilla-extract/css'

globalStyle("*", {
	boxSizing: "border-box",
	padding: 0,
	margin: 0,
})

globalStyle('html', {
    '@media': {
        '(prefers-color-scheme: dark)': {
            colorScheme: 'dark',
        },
    },
});
  • globalStyle(selector, attributes) 선택자와 CSS 속성들을 전달하여 글로벌 스타일로 적용 가능하다.
import { style, globalStyle } from '@vanilla-extract/css'

export const myStyle = style({
  display: 'flex',
  paddingTop: '3px',
})

globalStyle('body', {
  margin: 0,
})
  • style은 변수에 할당하여 export한 뒤, 원하는 컴포넌트에서 import해서 사용한다.
    • 별도의 css파일이 필수적.

6️⃣ 스타일 재사용


import { style } from '@vanilla-extract/css';

const base = style({
  padding: '10px',
  border: 'none',
  borderRadius: '4px',
  fontSize: '1rem',
});

const primaryButton = style([base, { backgroundColor: 'blue', color: 'white' }]);
const secondaryButton = style([base, { backgroundColor: 'grey', color: 'white' }]);
  • 스타일 객체를 배열 형태로 제공하여 위와 같은 방식으로 스타일 중복을 줄일 수 있다.

7️⃣ 동적 스타일링


recipe

// style.css.ts
import { recipe } from '@vanilla-extract/recipes';

export const button = recipe({
  base: {
    padding: '12px 24px',
    borderRadius: '8px',
  },
  variants: {
    color: {
      primary: { backgroundColor: 'blue' },
      secondary: { backgroundColor: 'gray' },
    },
    size: {
      small: { fontSize: '12px' },
      large: { fontSize: '18px' },
    },
  },
  defaultVariants: {
    color: 'primary',
    size: 'large',
  },
});
// .tsx

import { button } from './styles/style.css';

export const Button = () => (
  <button className={button({ color: 'secondary', size: 'small' })}>
    Custom Button
  </button>
);

동적 스타일링을 적용할 때는 Vanilla Extract에서 제공하는 api인 recipe를 사용한다.

  • 미리 정의된 variants 조합을 사용해 컴파일 타임에 CSS 클래스를 생성

dynamic

런타임에 CSS속성을 직접 조작하려면 dynamic이라는 별도의 api를 사용한다.

💡 재사용 가능한 컴포넌트들을 만들고자하면 CSS props를 이용해 동적 스타일링을 적용해야 한다.
하지만, Vanilla-Extract에서는 불가능하다 …

  • CSS Props 방식은 런타임에 JS를 사용하여 스타일을 계산하고, 적용하는 방식
    • → zero-runtime에서는 불가능 (빌드시점에 모든 CSS 생성하기 때문)
    • → 별도의 api인 dynamic을 사용해야함.
// styles.css.ts

import { createVar, style } from '@vanilla-extract/css';

// createVar는 고유한 cssVariable이름을 만들어준다.
export const brandColor = createVar();
export const textColor = createVar();

export const container = style({
  background: brandColor,
  color: textColor
});
// .tsx

import { assignInlineVars } from '@vanilla-extract/dynamic';
import {
  container,
  brandColor,
  textColor
} from './styles.css.ts';

const MyComponent = ({ tone }: { tone?: critical }) => (
  <section
    className={container}
    style={assignInlineVars({
      [brandColor]: 'pink',
      [textColor]: tone === 'critical' ? 'red' : null
    })}
  >
    ...
  </section>
);
  • 실시간 동적 스타일링이 필요하다면 → dynamic
    • 테마 전환, 사용자 입력에 따른 폰트 크기 조절 등
    • props에 따라 다양한 변형이 필요한 경우

  • 미리 정의된 변형이 있는 컴포넌트라면 → recipe
    • 버튼, 카드 등 variant에 따라 바뀌는 컴포넌트
    • 고정된 옵션 내에서 동적으로 스타일을 조합해야 할 경우

receipe와 dynamic에 대한 현업자 분의 의견

  • 동적 스타일링을 위해서는 vanilla-extract/dynamic 사용 중이긴하지만 vanilla-extract/recipe를 좀 더 적극적으로 사용중입니다. 생각보다 사용자 입력이나 상태에 따라 실시간으로 변해야 하는 유연한 동적 스타일을 생성하는 경우가 많지는 않더라구요.

8️⃣ 마지막으로 ...


Vanilla Extract를 사용해보면서 느낀 가장 큰 매력은 CSS에도 타입스크립트를 적용한다는 점이다. camelCase를 사용한다는 점도 굉장히 편안하게 다가왔다.

이 외에도 사실 안써야할 이유를 모를정도로 가장 발전한 CSS-in-TS (JS) 라는 생각이 든다. 성능상 이점이 있긴하지만 사실 엄~청난 차이는 아니라서 체감이 잘되지는 않는다.

  • 토스증권, 카카오헤어샵, 캐치테이블, ...

최근에 Vanilla Extract를 도입, 마이그레이션 하는 기업들이 많이 있다고 한다.
그만큼 장점이 있다는 뜻이고, 최근 가장 MZ한 스타일링 라이브러리가 아닌가 싶다 !

공식문서가 친절하고 양도 많지 않아서 한 번 쭉 읽어보면 더 이해하기 쉬울듯 하다. 😊
https://vanilla-extract.style/
https://vanilla-extract.style/documentation/getting-started

2개의 댓글

comment-user-thumbnail
2024년 12월 29일

Vanilla Extract에 대해서는 이름을 들어보기만 하고 이렇게 구체적으로 뜯어본 건 처음인데, 설명을 읽으며 CSS와 스타일링 도구들에 대해서 조금 더 구체적으로 이해할 수 있었습니다. 특히 CSS-in-JS가 아닌 CSS-in-TS의 사용으로 개발자 경험을 한층 더 개선할 수 있다는 점이 인상적이었습니다.!
우선 Vanilla Extract의 가장 큰 강점인 제로 런타임은 기존의 런타임 스타일링 도구가 가진 한계를 극복하기 위한 좋은 시도인 것 같습니다.
또 Vanilla Extract에서 동적 스타일링을 다루는 방식도 흥미로웠는데, recipe와 dynamic으로 각각의 용도에 맞게 스타일링하는 점은 실제 개발에서 큰 도움이 될 것 같습니다.
마지막으로 스타일 객체를 배열 형태로 재사용할 수 있다는 점이 매우 흥미로웠고 꼭 사용해 보고 싶다는 생각을 했습니다-!

답글 달기
comment-user-thumbnail
2024년 12월 29일

아티클 잘 읽었습니다! recipe api를 사용하면 variant 기반의 스타일 정의에서도 타입 안정성을 극대화 할 수 있겠네요! 또 보여주신 예시처럼 스타일 재사용이나 미리 정의된 variants 조합을 사용하면 스타일 로직을 한눈에 파악할 수 있어서 가독성과 유지보수에서도 좋아보여요.
이점이 아주 많아보이는데요 정리해보자면:

  • 제로 런타임으로 빌드 단계에서 css 생성 -> 성능적 이점
  • 타입스크립트 친화적임 -> 오타나 잘못된 속성 사용 방지
  • 가독성

정도의 장점이 있고, 단점으로는 동적 스타일링의 제약 정도 인것 같네요
저도 프로젝트에서 사용자 입력에 따라 실시간으로 변하는 동적 스타일링이 많이 사용된다고 느낀적이 별로 없어서 그런지,, (아직 플젝 경험이 별로 없기두 ㅎㅎ)
콘페티는 사용자와의 상호작용보다는 티켓 정보 제공을 위주로 하는 플랫폼이기 때문에(아직까지는) 크게 동적 스타일링을 요구하지 않을 것이라고 생각합니다!
그것 제외하고도 오래 프로젝트를 이어갈 것을 생각해보면,, 가독성과 유지보수, 성능면에서 이점이 훨씬 크다고 생각됩니다!

결론: vanila extract 도입 너무 좋은 것 같아요~~! 사용법을 열심히 공부해서 스타일 코드를 깔끔하게 작성해보겠습니다.

답글 달기

관련 채용 정보