StyleX 써보기

StyleX 는 무엇인가?
2023.12 메타에서 스타일 라이브러리를 공개했다. CSS-in-JS 솔루션이다.
CSS-in-JS 를 선호하는 나에겐 매우 흥미로운 소식이다. styled-components 와 비슷하면서도 서로 다른 장단점을 갖고 있다길래 더 공부하고 싶어졌다.
StyleX 는 단순한 CSS-in-JS 라이브러리가 아닌 대규모 애플리케이션과 재사용 컴포넌트 라이브러리, 정적 타입 코드베이스의 요구사항을 충족시키기 위해 설계되었다고 한다.
장단점
장점
- 빠른 속도 바벨 변환으로 인해 속도가 빠름
- 성능 최적화 컴파일 단계에서 스타일 코드가 CSS 로 변환되므로, 런타임에 스타일을 주입하는 다른 CSS-in-JS에 비해서 장점이라 볼 수 있다.
- 확장성 StyleX는 스타일을 ‘아토믹(atomic)’ CSS 클래스 이름으로 변환, 구성 및 최적화한다. 유틸리티 클래스 이름의 별도 라이브러리를 학습하거나 관리할 필요가 없다. 파일과 컴포넌트의 경계를 넘어 스타일 병합이 가능
- 예측 가능성 스타일 충돌 우려 없이 클래스 이름을 명확하게 사용할 수 있다.
- TypeScript와 함께 사용 시 스타일 정의, 테마 등에 높은 타입 안정성
- 서버 컴포넌트에서 스타일 작성이 가능하다. ( styled-components 의 경우, 클라이언트 컴포넌트에서만 작성이 가능했지만 stylex는 컴파일단계에서 변환되므로 서버 컴포넌트에서 작성이 가능하다. )
단점
- 스타일을 적용할 때,동적 스타일링에 제한적인 문제가 있다. StyleX는 컴파일과정에서 스타일을 최적화하여 클래스 이름을 자동으로 생성하는데, 이 과정에서 클래스 이름이 충돌할 가능성이 있다. props에 따라 동적으로 스타일을 생성할 때도 마찬가지이다. 그래서 StyleX는 고유한 클래스 이름 생성 알고리즘을 사용하지만, 완벽하게 충돌을 방지할 수는 없다고 한다.
- StyleX는 컴파일 과정을 통해 스타일을 최적화하기 때문에, 동적 스타일링을 사용할 때 컴파일 과정이 반복될(?) 수 있다.
설치
StyleX 런타임 패키지 설치
npm install --save @stylexjs/stylex yarn add --dev @stylexjs/stylex
StyleX 설치 후에 스타일이 컴파일 타임에 처리되도록 Babel 플러그인을 추가로 설정해줘야 한다. 공식 문서에서 이런 빌드 타임 컴파일러 방식을 권장하고 있다.
npm install --save-dev @stylexjs/babel-plugin yarn add --dev @stylexjs/nextjs-plugin
설치를 완료하고 .babelrc.js,next.config.js 파일을 만들어서 코드를 추가하자
// .babelrc.js
const path = require("path");
module.exports = {
presets: ["next/babel"],
plugins: [
[
"@stylexjs/babel-plugin",
{
dev: process.env.NODE_ENV === "development",
genConditionalClasses: true,
treeshakeCompensation: true,
aliases: {
"@/*": [path.join(__dirname, "*")],
},
unstable_moduleResolution: {
type: "commonJS",
rootDir: path.join(__dirname, "../.."),
},
},
],
],
};
// next.config.js
/** @type {import('next').NextConfig} */
const stylexPlugin = require("@stylexjs/nextjs-plugin");
const nextConfig = {
reactStrictMode: true,
};
module.exports = stylexPlugin({
rootDir: __dirname,
})(nextConfig);
module.exports = nextConfig;
여기서 만약 create-next-app 을 한 후에 StyleX를 설치한 상태라면 layout.tsx에서 에러가 날 것이다.
그 이유는 layout.tsx 에서 next/font를 사용하고 있기 때문인데, Babel을 사용하는 프로젝트에서는 next/font가 지원되지 않기 때문에 문제가 발생한다.
layout.tsx 에서 next/font를 싹 다 지워주고 다시 실행하면 해결된다.
튜토리얼
공식 깃헙 코드에 Next.js 에서 적용한 예시가 있다. 이 자료를 보고 쉽게 사용할 수 있다.

사용 방식
import * as stylex from "stylex";
const styles = stylex.create({
red: { color: "red" },
});
let a = stylex.props(styles.red);
가장 많이 사용되는 문법인 create 와 props 이다.
create 에 스타일 객체를 넣으면 키를 클래스 이름으로 매핑하여 컴파일된 스타일을 반환한다. 반환한 스타일은 props 를 사용해서 스타일을 주입한다.
props 에 반환된 스타일을 넣으면 props 는 className을 반환한다.
그래서 실제로 props 가 반환한 값을 콘솔로 찍어보면
{
className: 'page__style.MainContainer x78zum5 xdt5ytf x1ap80js xh8yej3 x19bbpc0'
}
이런식으로 출력된다. Tailwind css 처럼 사용된는 것 같다.
스타일 병합
StyleX 의 장점 중 하나인 스타일 병합은 말 그대로 create로 생성된 스타일을 여러개 병합해서 사용할 수 있는 방식이다.
import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({
base: {
fontSize: 16,
lineHeight: 1.5,
color: "grey",
},
highlighted: {
color: "rebeccapurple",
},
});
<div {...stylex.props(styles.base, styles.highlighted)} />;
base, highlighted 두개를 위처럼 함께 병합해서 사용할 수 있다.
조건부 스타일링
import * as stylex from "stylex";
const styles = stylex.create({
RouterButton: (params) => ({
backgroundColor: {
default: "rgba(0, 0, 0, 0)",
":hover": "var(--hv-cr);",
},
color: params === "true" ? "var(--text--color)" : "grey",
}),
});
let a = stylex.props(style.RouterButton(params === "/app" ? "true" : "false"));
css in js 답게 조건부 스타일링도 가능하다.
버그
왜 인지는 모르겠는데, 자꾸 스타일이 안먹힌다. 예를 들어서 에디터에 분명 flex-Direction: column 을 적용해 놨는데 개발 환경에서 먹히지 않는다. 그래서 row 로 바꿨다가 다시 column 으로 바꿨더니 다시 된다.
그리고 저번에 한 컴포넌트를 스타일 적용해놓고 다시 이어서 작업할려고 켰더니 스타일이 다 망가져있다. 그래서 다시 위와 동일하게 다른 스타일을 먹였다가 다시 돌아와서 저장했더니 돌아왔다. 알 수 없는 오류인데, 이게 지금 내 컴터에서만 이러는건지 (vscode) 아니면 다 이러는건지 모르겠다.
평가
기대치 만큼 만족스럽지는 않은 기술인 것 같다. 메타에서 운영하는 서비스는 대부분 StyleX 로 스타일링 하고 있다길래 많은 기대가 있었던 것 같다. 역시나 역시..공개된지 얼마 안된 기술이다보니 문제가 많다. 깃헙이슈에 일주일에 2~3개 꼴로 문제가 올라오고 있다.
외에도 지인도 사용해본 결과 Tailwind css 와 충돌하는 문제가 있다고 한다. 아마도 props 스타일 자체를 className으로 반환하는 방식이 Tailwind 와 문제를 발생시키는 것 같다.
그리고 스타일 작성시, 죄다 따옴표를 붙여줘야함 예를들어
display: 'flex'
이게 별건 아니지만 은근히 거슬림 다른 스타일 방식은 이렇지 않아서 작성하는데 조금 거슬렸다.
일단 쓰던 styled-components 이어서 계속 써야겠다.