Zero Runtime CSS-in-JS์ ๋ํด ์์๋ณด์
์ต๊ทผ์ ์งํํ ํ๋ก์ ํธ์์ ์์ฆ์ ์ฃผ๋ชฉ๋ฐ๊ณ ์๋ Zero Runtime CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ, Vanilla Extract๋ฅผ ์ฌ์ฉํด ๋ณด์์ต๋๋ค. Vanilla Extract๋ ์ปดํ์ผ ํ์์ CSS ํ์ผ์ ์์ฑํ์ฌ ๊ธฐ์กด ๋ฐํ์์ ์์ฑ๋๋ CSS-in-JS๋ณด๋ค ์ฑ๋ฅ์์ ์ด์ ์ ์ ๊ณตํ๋ ๊ฒ์ผ๋ก ์๋ ค์ ธ ์์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ๊ธฐ์กด์ CSS-in-JS์๋ ์ด๋ค ์ฑ๋ฅ์ ์ธ ๋ฌธ์ ๊ฐ ์์๊ธฐ ๋๋ฌธ์ Zero Runtime CSS-in-JS ๊ธฐ์ ์ด ๋ ์ค๋ฅด๊ฒ ๋์์๊น์?
์ด ํฌ์คํ
์์๋ ๋ฐํ์ CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํน์ง๊ณผ ๋ฌธ์ ๋ ๋ฌด์์ธ์ง, ๊ทธ๋ฆฌ๊ณ Zero Runtime ์ ๊ทผ ๋ฐฉ์์ด ๊ทธ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง์ ๋ํด ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
CSS ์ ์ฉ ๊ณผ์
CSS๊ฐ ์น ํ์ด์ง์ ์ ์ฉ๋๋ ๊ณผ์ ๋ถํฐ ์์๋ด
์๋ค.
- ๋คํธ์ํฌ์์ HTML์ ๋ฐ์ ๋ธ๋ผ์ฐ์ ๋ก ๋ก๋ฉํ๋ค.
- HTML์ DOM(Document Object Model)๋ก ๋ณํํ๋ค.
- ๋ธ๋ผ์ฐ์ ๋ HTML ๋ฌธ์์ ๋ฆฌ์์ค๋ค์ ๊ฐ์ ธ์จ๋ค. ์ด๋ฏธ์ง, ๋์์, CSS ๋ฑ์ด ํด๋น๋๋ค.
- ๋ธ๋ผ์ฐ์ ๋ ๊ฐ์ ธ์จ CSS๋ฅผ ๋ณํํ์ฌ CSSOM(CSS Object Model)์ ์์ฑํ๋ค. 2์์ ๋ง๋ค์ด์ง DOM Tree์ CSSOM Tree๋ฅผ ๊ฒฐํฉํ์ฌ ์คํ์ผ์ DOM ๋ ธ๋์ ์ ์ฉํ๋ค. ์ด๋, CSS Cascading ๊ท์น์ ๋ฐ๋ผ ์คํ์ผ์ด ์ ์ฉ๋๋ค. ์ด ๊ณผ์ ์ Render Tree๋ผ๊ณ ํ๋ค.
- ํ์ด์ง๋ฅผ ํ๋ฉด์ ๊ทธ๋ฆฐ๋ค. ์ด ๋จ๊ณ๋ฅผ Painting์ด๋ผ๊ณ ํ๋ค.
์ ๊ณผ์ ์ ๊ทธ๋ฆผ์ผ๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.

CSS์ ๋ ๋๋ง ๋ฌธ์
์์์ ์ค๋ช
ํ ๊ฒ์ฒ๋ผ ๋ธ๋ผ์ฐ์ ๋ HTML์ ๋จผ์ ๋ฐ์์จ ๋ค์ CSSOM์ ์์ฑํ์ฌ HTML์ ์คํ์ผ์ ์ ์ฉ์ํฌ ์ค๋น๋ฅผ ํฉ๋๋ค.
์ฌ๊ธฐ์ CSS๋ก ์ธํด ๋ฐ์ํ๋ ๋ ๋๋ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
ํ ํ์ด์ง์ ์ ์ฉ๋ CSS์ ์๋ง์ ์คํ์ผ๊ณผ ์ ํ์๊ฐ ํฌํจ๋์ด ์๋ค๋ฉด ์ด๋จ๊น์? CSS ํ์ผ์ ํฌ๊ธฐ๊ฐ ํด์๋ก ๋ธ๋ผ์ฐ์ ๊ฐ CSSOM์ ๋ง๋๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ด ๊ธธ์ด์ง๋๋ค.
์ด ๊ณผ์ ์์ ๋ ๋๋ง ๋ธ๋กํน์ด ๋ฐ์ํด ์์ฒญ๋ ํ์ด์ง์ ์ฒซ Painting์ด ์ง์ฐ๋ ์ ์์ต๋๋ค.
์ฒซ Painting์ ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒญ๋ ํ์ด์ง์ ์ฒซ ๋ฒ์งธ ํฝ์
์ ๋ ๋๋งํ๋ ์ด๋ฒคํธ์
๋๋ค.
์ฒซ Painting์ด 0.5์ด ์ด์ ์ง์ฐ๋๋ค๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ์ ํ๋์ด ๋ถ์ ์ ์ธ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
ํด๋ผ์ด์ธํธ์๊ฒ CSS๋ฅผ ๋ ๋นจ๋ฆฌ ์ ๋ฌํ๊ณ ์ฒซ Painting ์๊ฐ์ ์ต์ ํํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํด์ผ ํฉ๋๋ค.
โ๏ธ CSS-in-JS: ๋ ๋๋ง ๋ธ๋กํน ํด๊ฒฐ๊ณผ ํด๋์ค ์ค๋ณต ๋ฐฉ์ง
๋ ๋๋ง ๋ธ๋กํน์ ํด๊ฒฐํ๋ ค๋ฉด CSS ํ์ผ์ ์ปดํฌ๋ํธํํ์ฌ chunk(์์ ๋ฉ์ด๋ฆฌ)๋ก ๋ถํ ํ๋ฉด ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ ์ด ๋ฐฉ๋ฒ์ ๋์ผํ ํด๋์ค ์ด๋ฆ์ ์ฌ์ฉํ ๊ฒฝ์ฐ ์ ์ญ์ ์ผ๋ก ์คํ์ผ์ด ์ค๋ณต๋์ด ๋์์ธ ์์ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
CSS-in-JS๊ฐ ์ด๋ฌํ ํด๋์ค ์ค๋ณต๊ณผ ๋ฒ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ์ค๋๋ค.
CSS-in-JS์ ์ฅ์
CSS-in-JS๊ฐ ํด๊ฒฐํ๋ ๊ฐ์ฅ ํฐ ๋ฌธ์ ๋ ์์์ ์งง๊ฒ ์ค๋ช
ํ ๊ธ๋ก๋ฒ ์ค์ฝํ ๋ฌธ์ ์ธ๋ฐ,
์ด์ธ์๋ CSS-in-JS๊ฐ ๊ฐ์ง๊ณ ์๋ ์ฅ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
1๏ธโฃ CSS ํ์ผ์ JavaScript์ ํจ๊ป ๊ด๋ฆฌ
๋์ผํ ํ์ผ์ JavaScript ์ฝ๋์ ์คํ์ผ ์ฝ๋๋ฅผ ํจ๊ป ์์ฑํด ์ปดํฌ๋ํธ์ ์ ์ง๋ณด์๊ฐ ๋ณด๋ค ๋ ์ฉ์ดํฉ๋๋ค.
2๏ธโฃ ์ค์ฝํ ๋ฐ ์ ํ์ ๊ด๋ จ
CSS-in-JS๋ JavaScript ํ์ผ๋ง๋ค ์คํ์ผ์ ์์ฑํ๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์คํ์ผ๊ณผ ์ถฉ๋ํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
์คํ์ผ ์ถฉ๋์ ํผํ๊ธฐ ์ํด ์ด๋ค CSS ๋ฐฉ๋ฒ๋ก ์ ์ฌ์ฉํ ์ง ๊ฑฑ์ ํ ํ์๋ ์์ต๋๋ค.
๋ณดํต ์คํ์ผ์ ์์ฑํ ๋ ์ ํ์๋ฅผ ์์ ๋ถ์ด์ง ์๊ณ ํ๋์ ์ปดํฌ๋ํธ์ ๋ํด์๋ง ์์ฑํ๋ฏ๋ก, specificity ๋ฌธ์ ๋ ๊ฑฐ์ ๋ฐ์ํ์ง ์์ต๋๋ค.
3๏ธโฃ ๋์ ์คํ์ผ๋ง
CSS-in-JS๋ ๋ค์๊ณผ ๊ฐ์ด ์กฐ๊ฑด๋ถ CSS๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
import styled from 'styled-components';
const Title = styled.h1<{ highlight?: boolean }>`
font-size: 1.5em;
text-align: center;
color: ${(props) => (props.highlight ? 'black' : '#BF4F74')};
`;
function App() {
return (
<>
<Title>Hello World</Title>
<Title highlight>Hello World</Title>
</>
);
}
export default App;
CSS-in-JS์ ๋จ์
CSS-in-JS๊ฐ ์ปดํฌ๋ํธ ๋จ์๋ก CSS๋ฅผ ๋๋์ด ์์ฑํ ์๋ ์์ง๋ง, ์์์ ์ค๋ช
ํ๋ ๋ ๋๋ง ๋ธ๋กํน์ ์์ ํ ํด๊ฒฐํ์ง๋ ๋ชปํฉ๋๋ค.
์ด์ธ์๋ ๋ช ๊ฐ์ง ๋จ์ ์ด ์์ต๋๋ค.
1๏ธโฃ ๋ ๋๋ง ๋ธ๋กํน
CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ *์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง๋ ๋ ์๋ก์ด ์คํ์ผ์ ๋์ ์ผ๋ก ๊ณ์ฐํด ์ถ๊ฐํ๋ ๋ฐฉ์์ผ๋ก ๋์ํ๋ฉฐ, ์ด๋ ๊ทผ๋ณธ์ ์ผ๋ก ์ฑ๋ฅ์ ์ข์ง ์์ต๋๋ค.
์ปดํฌ๋ํธ๊ฐ ๋ง์์๋ก ์ฒซ Painting ์๊ฐ์ด ๊ธธ์ด์ ธ ๋ ๋๋ง ๋ธ๋กํน์ด ์๊ธฐ๊ณ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ํดํฉ๋๋ค.
*์์
import { useState } from 'react';
import styled from 'styled-components';
const Title = styled.h1<{ $primary?: boolean }>`
font-size: 1.5em;
text-align: center;
color: ${(props) => (props.$primary ? '#000000' : '#BF4F74')};
`;
function App() {
const [primary, setPrimary] = useState(false);
const changeColorStyledComponent = () => {
setPrimary((p) => !p);
};
return (
<>
<Title $primary={primary}>Hello World</Title>
<button onClick={changeColorStyledComponent}>change color</button>
</>
);
}
export default App;
์ฌ์ฉ์๊ฐ ๋ฒํผ์ ๋๋ ์ ๋ ๋์ ์ผ๋ก ์คํ์ผ์ด ์ถ๊ฐ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
2๏ธโฃ SSR๊ณผ์ ํธํ์ฑ
CSS-in-JS๋ ๋ฐํ์์ ์คํ์ผ์ ์์ฑํ๊ณ , ์๋ฒ์์ ํ์ด์ง์ ์ด๊ธฐ HTML์ ์์ฑํ๋ SSR ๋ฐฉ์์ ์๋ก ์์ถฉ๋๋ ์๊ตฌ์ฌํญ์ผ๋ก ์ธํด ์๊ธฐ๋ ๋ฌธ์ ๊ฐ ๋ง์ต๋๋ค.
์๋ฒ์์ ์์ฑ๋ HTML์ด ์ฌ์ฉ์์ ๋ธ๋ผ์ฐ์ ๋ก ์ ๋ฌ๋๊ธฐ ์ ๊น์ง CSS-in-JS๋ก ์์ฑํ ์คํ์ผ์ด ์ ์ฉ๋์ง ์์ต๋๋ค.
์ด๋ ์ด๊ธฐ ๋ก๋ฉ ์ ์คํ์ผ์ด ์ ์ฉ๋์ง ์์ ์ฝํ
์ธ ๊ฐ ๊น๋นก์ด๋ฉฐ ๋ณด์ด๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
Next.js์ ๊ณต์ ๋ฌธ์์๋ ๋ฐํ์ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ํ์ํ CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ์ฌ ์๋ฒ ์ปดํฌ๋ํธ์์ ์ง์๋์ง ์๋๋ค๋ ์ฃผ์ ๋ฌธ๊ตฌ๊ฐ ์จ ์๊ณ ,
Emotion GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์๋ ์ด์ ๊ด๋ จ๋ ์๋ง์ ๋ฌธ์ ๊ฐ ์ ์๋ฉ๋๋ค.
Zero Runtime CSS-in-JS
์ด๋ฌํ CSS-in-JS์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด, Zero Runtime CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ง๋ค์ด์ง๊ฒ ๋ฉ๋๋ค.
Zero Runtime ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์๋ CSS-in-JS ์ฝ๋๋ฅผ CSS ํ์ผ๋ก ๋จผ์ ๋ณํํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ๋ธ๋ผ์ฐ์ ๊ฐ ํด๋น ์คํ์ผ์ ์ฝ๊ณ ์น ํ์ด์ง์ ์ ์ฉํฉ๋๋ค.
์ด ๋ฐฉ์์ผ๋ก ๋ฐํ์์ ์คํ์ผ ํ๊ทธ๋ฅผ ์์ฑํจ์ผ๋ก์จ ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
์์์ ๋ณด์ฌ ๋๋ฆฐ ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ๋๋ ์ ๋ ํ
์คํธ์ ์์์ด ๋ณ๊ฒฝ๋๋ ๋์ผํ ๊ธฐ๋ฅ์ Vanilla Extract๋ก ์์ฑํ ์์ ์
๋๋ค.
import { useState } from 'react';
import { title } from './test.css';
function App() {
const [primary, setPrimary] = useState(false);
const changeColor = () => {
setPrimary((p) => !p);
};
return (
<>
<h1 className={`${title} ${primary ? 'primary' : ''}`}>Hello World</h1>
<button onClick={changeColor}>change color</button>
</>
);
}
export default App;
// test.css.ts
import { style } from '@vanilla-extract/css';
export const title = style({
fontSize: '1.5em',
textAlign: 'center',
color: '#BF4F74',
selectors: {
[`&.primary`]: {
color: 'black',
},
},
});
๋น๋ ํ์์ ๋ฒํผ์ ๋๋ ์ ๋์ ์คํ์ผ๊น์ง ๋ชจ๋ ๊ณ์ฐ๋์ด HTML๊ณผ CSS์ ์ด๊ธฐ ๋ก๋ฉ์ด ์๋ฃ๋๋ฉด style ํ๊ทธ์์ ํ์ธํ ์ ์์ต๋๋ค.
Vanilla Extract ์ธ์๋ Linaria, Astroturf, Reshadow, Panda CSS ๋ฑ ๋ค์ํ Zero Runtime CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ต๋๋ค.
Vanilla Extract
Vanilla Extract๋ GitHub์์ 2024๋
1์ ๊ธฐ์ค 9์ฒ ๊ฐ ์ด์์ ์คํ๋ฅผ ๋ฐ์ ์ธ๊ธฐ ์๋ Zero Runtime CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
ํ๋ ์์ํฌ์ ๊ตฌ์ ๋ฐ์ง ์์ ๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ ๋ชจ๋ ํ๋ก ํธ์๋ ํ๋ ์์ํฌ์์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
Vanilla Extract๋ฅผ ์ฌ์ฉํ๋ฉด ๋ก์ปฌ ๋ฒ์์ ์คํ์ผ๊ณผ ๋ณ์๋ฅผ JavaScript ๋๋ TypeScript๋ก ์์ฑํ๊ณ ๋น๋ ์์ ์ CSS ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค. ๋ํ ์ ํ๋ฆฌ์ผ์ด์
์ ํ
๋ง๋ฅผ ๋์ ์ผ๋ก ์ง์ ํ๊ธฐ ์ํ API๋ ์ ๊ณตํฉ๋๋ค.
Vanilla Extract์ ๋ค๋ฅธ ํน์ง์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ํ์ ์์ : ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ง์ํ์ฌ ๊ฐ๋ฐ์ ๊ฒฝํ์ ๊ฐ์ ํฉ๋๋ค.
- ๋ฒ๋ค๋ฌ ์ง์: ์นํฉ, Vite, Parcel, Babel๊ณผ ๊ฐ์ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ํ๋ก ํธ์๋ React ํ๋ ์์ํฌ ๋ฐ ๋๊ตฌ์ ํตํฉ๋์ด ์์ต๋๋ค.
- ์ ๋ฌธ์ํ๋จ
- ์ฌ์ด ์ค์
- ์ฌ๋ฌ ํจํค์ง์ ํจ๊ป ์ฌ์ฉํ ์ ์์ (sprinkles, recipe, dynamic)
- sprinkles๋ฅผ ํ์ฉํด tailwind์ฒ๋ผ Atomic CSS๋ฅผ ๊ตฌ์ฑํ ์ ์์
- recipe๋ฅผ ํ์ฉํ์ฌ variant ๊ธฐ๋ฐ ์คํ์ผ๋ง์ ๊ตฌ์ฑํ ์ ์์
- dynamic์ ํ์ฉํด ๋์ ์ผ๋ก ๊ฐ์ ์ ๋ฐ์ดํธํ ์ ์์
๊ธ์ ๋ง์น๋ฉฐ
์ด ํฌ์คํ
์์, Vanilla Extract์ ๊ฐ์ Zero Runtime CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๊ธฐ์กด CSS-in-JS ๋ฐฉ์์ ํ๊ณ๋ฅผ ์ด๋ป๊ฒ ๊ทน๋ณตํ๋์ง ์์๋ณด์์ต๋๋ค. Zero Runtime CSS-in-JS๋ ์ปดํ์ผ ํ์์ CSS ํ์ผ์ ์์ฑํจ์ผ๋ก์จ ๋ฐํ์ ๋ ๋๋ง ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ณ , ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค. ์ด๋ฅผ ํตํด ์น ์๋น์ค์์ ์ค์ํ ์ฒซ ํ๋ฉด ๋ก๋ฉ ์๊ฐ์ ๋จ์ถํ์ฌ ์ข์ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋ค ์ ์์ต๋๋ค.