CSS-in-JS 라이브러리는 JS와 상태를 공유할 수 있어서 동적으로 스타일링을 할 수 있는 장점이 있습니다. 컴포넌트 기반 개발과 잘 맞아떨어지고 스타일 충돌 문제를 방지할 수 있어 개발자들에게 많은 사랑을 받고 있습니다(저 포함)✨
CSS-in-JS 는 동작 방식이 런타임에 스타일을 생성하는 방식과 빌드 타임에 스타일을 생성하는 방식으로 나누어집니다.
자바스크립트가 실행되는 시점(컴포넌트가 렌더링될 때) 스타일을 생성합니다. 동적으로 <style>
태그를 생성하여 <head>
태그에 추가됩니다.
대표적인 라이브러리로는 styled-components, emotion, JSS 등이 있습니다.
const StyledButton = styled.button`
background-color: ${props => props.primary ? 'blue' : 'gray'};
color: white;
`;
// 렌더링 시점에:
<StyledButton primary>Click Me</StyledButton>
브라우저의 <head>
섹션이나 특정 <style>
태그 안에 동적으로 생성된 스타일이 삽입됩니다.
<style>
.sc-AxjAm { background-color: blue; color: white; }
</style>
개발 모드에서 주로 사용되며 <style>
태그를 생성하여 스타일을 삽입합니다.
단점 - 런타임 오버헤드가 발생할 수 있습니다. 스타일을 계산하고 적용하는 데 시간이 걸리므로 초기 로딩 속도가 느려질 수 있습니다. SSR 설정이 별도로 필요할 수 있습니다.
zero-runtime이란 런타임에 스타일을 생성하거나 적용하는 작업이 없는 것을 의미
스타일이 빌드 과정에서 정적으로 생성되어 CSS 파일로 출력됩니다.
초기 로딩 성능이 개선되며, 스타일 적용이 빠르고 브라우저의 스타일 계산 부담이 줄어듭니다.
대표적으로 styled-components / emotion, linaria, stitches, @vanilla-extract 등이 있습니다.
styled-components & emotion 의 경우 개발 모드와 프로덕션 모드에 차이가 있는데, 프로덕션 모드에선 빌드 타임에 스타일을 미리 생성해서 최적화합니다.
(개발 모드에서의 <style>
태그와 프로덕션 모드에서 <style>
태그를 비교해 보면 차이가 납니다)
내부 Babel 플러그인들(babel-plugin-styled-components, @emotion/babel-plugin)이 제공하는 최적화는 런타임 오버헤드를 완전히 제거하지는 않습니다! 이 플러그인들은 스타일의 생성과 적용 과정을 최적화하고 일부 작업을 빌드 타임으로 이전하지만, 몇 가지 런타임 작업은 여전히 필요하기 때문에 완전한 zero-runtime 으로 볼 수는 없습니다. 컴포넌트가 렌더링될 때 스타일을 적용하는 런타임 작업이 여전히 필요합니다..!
빌드 타임에 CSS를 생성하여 성능을 최적화하는 CSS-in-JS 라이브러리입니다.
스타일을 빌드 타임에 추출하지만, 실제로 런타임에 <style>
태그를 동적으로 삽입하므로 완전한 zero-runtime이라고 할 수 없습니다. 런타임에서 스타일을 적용하는 과정이 포함됩니다.
import { styled } from '@stitches/react';
const Button = styled('button', {
backgroundColor: 'gray',
borderRadius: '4px',
border: 'none',
color: 'white',
padding: '10px 20px',
cursor: 'pointer',
'&:hover': {
backgroundColor: 'darkgray',
},
});
//...
<Button>버튼</Button>
빌드 타임에 Stitches가 생성하는 CSS 객체
const styles = {
h1: {
fontSize: '24px',
color: '#333',
},
button: {
backgroundColor: 'gray',
borderRadius: '4px',
border: 'none',
color: 'white',
padding: '10px 20px',
cursor: 'pointer',
'&:hover': {
backgroundColor: 'darkgray',
},
},
};
HTML에 삽입된 <style>
태그
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Stitches Example</title>
<style>
/* Injected by Stitches at runtime */
.title_hash {
font-size: 24px;
color: #333;
}
.button_hash {
background-color: gray;
border-radius: 4px;
border: none;
color: white;
padding: 10px 20px;
cursor: pointer;
}
.button_hash:hover {
background-color: darkgray;
}
</style>
</head>
<body>
<div id="root">
<button class="button_hash">버튼</button>
</div>
</body>
</html>
빌드 타임에 CSS를 생성하여 런타임 오버헤드가 없는 zero-runtime CSS-in-JS 라이브러리입니다.
Linaria의 문법은 Styled Components와 유사합니다!
import { styled } from '@linaria/react';
const Button = styled.button`
background: gray;
border-radius: 4px;
border: none;
color: white;
padding: 10px 20px;
cursor: pointer;
&:hover {
background: darkgray;
}
`;
//...
<Button>버튼</Button>
빌드 타임에 Linaria가 생성하는 CSS 파일 (예: styles.css)
.button_classname {
background: gray;
border-radius: 4px;
border: none;
color: white;
padding: 10px 20px;
cursor: pointer;
}
.button_classname:hover {
background: darkgray;
}
HTML에 삽입된 <style>
태그
Linaria는 빌드된 HTML 파일에 <style>
태그를 포함시켜 스타일을 적용
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Linaria Example</title>
<style>
.button_classname {
background: gray;
border-radius: 4px;
border: none;
color: white;
padding: 10px 20px;
cursor: pointer;
}
.button_classname:hover {
background: darkgray;
}
</style>
</head>
<body>
<div id="root">
<button class="button_classname">버튼</button>
</div>
</body>
</html>
개발 모드와 프로덕션 모드에서 빌드 타임 스타일 생성을 지향하는 zero-runtime CSS-in-JS 라이브러리입니다.
Vanilla Extract의 컴파일 타임 CSS 파일 생성
// styles.css.ts
import { style } from '@vanilla-extract/css';
export const button = style({
backgroundColor: 'blue',
color: 'white',
});
// Component.tsx
import { button } from './styles.css';
function Component() {
return <button className={button}>버튼</button>;
}
컴파일 타임에 styles.css
파일이 생성되고, HTML 파일에 포함됩니다.
<link rel="stylesheet" href="styles.css">
styles.css 내용
.button { background-color: blue; color: white; }
프로젝트 규모와 복잡도, SSR인지 CSR인지, 스타일링 방식 등 기준을 고려하여 요구사항에 맞는 CSS-in-JS 라이브러리를 선택하는 것이 중요하다고 봅니다✨