런타임 CSS-in-JS의 한계를 극복하기 위해 등장한 Vanilla Extract. (2021년 등장, 2022년부터 알려지기 시작)
Vanilla Extract는 CSS-in-JS가 아닌 CSS-in-TS 이다.
- CSS-in-JS는 들어봤는데 TS는 뭐지 ?
특별한건 아니고, 타입스크립트로 스타일을 작성한다 해서 CSS-in-TS라고 한다.
Zero-Runtime인 CSS-in-JS 로는 PandaCSS도 존재한다 ! 🐼
일반 CSS-in-JS는 런타입에 JS로 작성한 코드를 CSS로 변환한다.
JS파일이 실행되며 style을 생성하기 때문에, style의 규모가 커질수록 성능상 문제가 더욱 커진다.
💡 기본적으로 컴포넌트가 렌더링될 때 새로운 스타일을 동적으로 계산해주는 방식으로 동작
→ 이는 근본적으로 성능에 좋지 않다.
styled-components로 작성한 코드
Vanilla Extract로 작성한 코드
동일한 기능을 Vanilla Extract로 작성한 코드이다.
💡 정리해보자면 …
Zero-runtime CSS는 빌드 시점에 CSS를 생성한다.
→ 런타임에는 추가적인 JS 실행 없이 이미 생성된! CSS를 사용하게 된다. 즉, 브라우저가 페이지를 렌더링할 때 CSS를 즉시 사용할 수 있다.
→ 런타임 성능 개선, 더욱 빠른 페이지 로딩
참고) https://techblog.jobkorea.co.kr/vanilla-extract-%EB%8F%84%EC%9E%85%EA%B8%B0-b61c38aaef36
사용된 변수는 locally scoped 되어 다른 .css.ts 파일에서 선언된 변수를 사용하더라도 서로 충돌이 일어나지 않음
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해서 사용한다.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' }]);
// 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
를 사용한다.
런타임에 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
recipe
receipe와 dynamic에 대한 현업자 분의 의견
- 동적 스타일링을 위해서는 vanilla-extract/dynamic 사용 중이긴하지만 vanilla-extract/recipe를 좀 더 적극적으로 사용중입니다. 생각보다 사용자 입력이나 상태에 따라 실시간으로 변해야 하는 유연한 동적 스타일을 생성하는 경우가 많지는 않더라구요.
Vanilla Extract를 사용해보면서 느낀 가장 큰 매력은 CSS에도 타입스크립트를 적용한다는 점이다. camelCase를 사용한다는 점도 굉장히 편안하게 다가왔다.
이 외에도 사실 안써야할 이유를 모를정도로 가장 발전한 CSS-in-TS (JS) 라는 생각이 든다. 성능상 이점이 있긴하지만 사실 엄~청난 차이는 아니라서 체감이 잘되지는 않는다.
최근에 Vanilla Extract를 도입, 마이그레이션 하는 기업들이 많이 있다고 한다.
그만큼 장점이 있다는 뜻이고, 최근 가장 MZ한 스타일링 라이브러리가 아닌가 싶다 !
공식문서가 친절하고 양도 많지 않아서 한 번 쭉 읽어보면 더 이해하기 쉬울듯 하다. 😊
https://vanilla-extract.style/
https://vanilla-extract.style/documentation/getting-started
아티클 잘 읽었습니다! recipe api를 사용하면 variant 기반의 스타일 정의에서도 타입 안정성을 극대화 할 수 있겠네요! 또 보여주신 예시처럼 스타일 재사용이나 미리 정의된 variants 조합을 사용하면 스타일 로직을 한눈에 파악할 수 있어서 가독성과 유지보수에서도 좋아보여요.
이점이 아주 많아보이는데요 정리해보자면:
정도의 장점이 있고, 단점으로는 동적 스타일링의 제약 정도 인것 같네요
저도 프로젝트에서 사용자 입력에 따라 실시간으로 변하는 동적 스타일링이 많이 사용된다고 느낀적이 별로 없어서 그런지,, (아직 플젝 경험이 별로 없기두 ㅎㅎ)
콘페티는 사용자와의 상호작용보다는 티켓 정보 제공을 위주로 하는 플랫폼이기 때문에(아직까지는) 크게 동적 스타일링을 요구하지 않을 것이라고 생각합니다!
그것 제외하고도 오래 프로젝트를 이어갈 것을 생각해보면,, 가독성과 유지보수, 성능면에서 이점이 훨씬 크다고 생각됩니다!
결론: vanila extract 도입 너무 좋은 것 같아요~~! 사용법을 열심히 공부해서 스타일 코드를 깔끔하게 작성해보겠습니다.
Vanilla Extract에 대해서는 이름을 들어보기만 하고 이렇게 구체적으로 뜯어본 건 처음인데, 설명을 읽으며 CSS와 스타일링 도구들에 대해서 조금 더 구체적으로 이해할 수 있었습니다. 특히 CSS-in-JS가 아닌 CSS-in-TS의 사용으로 개발자 경험을 한층 더 개선할 수 있다는 점이 인상적이었습니다.!
우선 Vanilla Extract의 가장 큰 강점인 제로 런타임은 기존의 런타임 스타일링 도구가 가진 한계를 극복하기 위한 좋은 시도인 것 같습니다.
또 Vanilla Extract에서 동적 스타일링을 다루는 방식도 흥미로웠는데, recipe와 dynamic으로 각각의 용도에 맞게 스타일링하는 점은 실제 개발에서 큰 도움이 될 것 같습니다.
마지막으로 스타일 객체를 배열 형태로 재사용할 수 있다는 점이 매우 흥미로웠고 꼭 사용해 보고 싶다는 생각을 했습니다-!