[Daily morning study] CSS-in-JS vs CSS Modules ๋น๊ต
#daily morning study
CSS ์คํ์ผ๋ง ๋ฐฉ์์ ๋ฐ์
์ ํต์ ์ผ๋ก CSS๋ ๋ณ๋์ .css ํ์ผ๋ก ์์ฑํ๊ณ HTML์ ๋งํฌํ๋ ๋ฐฉ์์ด์๋ค. React ๊ฐ์ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ํ๋ ์์ํฌ๊ฐ ๋ฑ์ฅํ๋ฉด์ ์คํ์ผ๋ ์ปดํฌ๋ํธ ๋จ์๋ก ๊ด๋ฆฌํ๋ ค๋ ํ๋ฆ์ด ์๊ฒผ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฌผ๋ก CSS Modules์ CSS-in-JS๊ฐ ๋ฑ์ฅํ๋ค.
CSS Modules
CSS ํ์ผ์ ๋ชจ๋์ฒ๋ผ importํด์ ํด๋์ค ์ด๋ฆ์ ์๋์ผ๋ก ๊ณ ์ ํ๊ฒ ๋ง๋๋ ๋ฐฉ์์ด๋ค.
๋์ ์๋ฆฌ
๋น๋ ํ์์ ํด๋์ค ์ด๋ฆ์ [filename]__[classname]__[hash] ํํ๋ก ๋ณํํด์ ์ ์ญ ์ถฉ๋์ ๋ฐฉ์งํ๋ค.
/* Button.module.css */
.button {
padding: 8px 16px;
background-color: #007bff;
color: white;
border-radius: 4px;
}
.primary {
background-color: #0056b3;
}
import styles from './Button.module.css';
function Button({ children, variant }) {
return (
<button className={`${styles.button} ${variant === 'primary' ? styles.primary : ''}`}>
{children}
</button>
);
}
๋น๋ ํ ์ค์ HTML์๋ Button__button__a1b2c ๊ฐ์ ๊ณ ์ ํ ํด๋์ค ์ด๋ฆ์ด ์ ์ฉ๋๋ค.
์ฅ์
- ๋ณ๋์ ๋ฐํ์ ์ค๋ฒํค๋ ์์ (๋น๋ ํ์์ ์ฒ๋ฆฌ)
- ๊ธฐ์กด CSS ๋ฌธ๋ฒ ๊ทธ๋๋ก ์ฌ์ฉ ๊ฐ๋ฅ
- ๋ฒ๋ค ํฌ๊ธฐ๊ฐ ์์
- ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR)์์๋ ๋ฌธ์ ์์
๋จ์
- ๋์ ์คํ์ผ๋ง์ด ๋ฒ๊ฑฐ๋ก์ (์ธ๋ผ์ธ ์คํ์ผ์ด๋ ํด๋์ค ์กฐ๊ฑด๋ถ ์ถ๊ฐ๋ก ํด๊ฒฐ)
- JS ๋ณ์๋ฅผ CSS์ ์ง์ ์ฌ์ฉํ๊ธฐ ์ด๋ ค์
- ์ปดํฌ๋ํธ์ ์คํ์ผ ํ์ผ์ ๋ฐ๋ก ๊ด๋ฆฌํด์ผ ํจ
CSS-in-JS
JavaScript ์ฝ๋ ์์์ CSS๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ด๋ค. ๋ํ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก styled-components์ Emotion์ด ์๋ค.
styled-components ์์
import styled from 'styled-components';
const Button = styled.button`
padding: 8px 16px;
background-color: ${props => props.primary ? '#0056b3' : '#007bff'};
color: white;
border-radius: 4px;
&:hover {
opacity: 0.9;
}
`;
function App() {
return (
<>
<Button>๊ธฐ๋ณธ ๋ฒํผ</Button>
<Button primary>์ฃผ์ ๋ฒํผ</Button>
</>
);
}
๋์ ์๋ฆฌ
- ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ ๋๋ง๋ ๋ JS๊ฐ CSS ๋ฌธ์์ด์ ํ์ฑ
- ๊ณ ์ ํ ํด๋์ค ์ด๋ฆ ์์ฑ (์:
sc-aBcDeF) <style>ํ๊ทธ์ CSS ์ฝ์- props ๋ณํ์ ๋ฐ๋ผ ๋์ ์ผ๋ก CSS ์ ๋ฐ์ดํธ
์ฅ์
- props ๊ธฐ๋ฐ ๋์ ์คํ์ผ๋ง์ด ์์ฐ์ค๋ฌ์
- JS ๋ณ์, ํจ์, ํ ๋ง๋ฅผ CSS์ ์ง์ ํ์ฉ ๊ฐ๋ฅ
- ์ปดํฌ๋ํธ์ ์คํ์ผ์ ํ ํ์ผ์์ ๊ด๋ฆฌ
- ์๋ vendor prefixing
๋จ์
- ๋ฐํ์ ์ค๋ฒํค๋: JS์์ CSS๋ฅผ ์ฒ๋ฆฌํ๋ฏ๋ก ์ฑ๋ฅ์ ์ํฅ
- ๋ฒ๋ค ํฌ๊ธฐ ์ฆ๊ฐ (styled-components ๊ธฐ์ค ์ฝ 15KB gzip)
- SSR ์ค์ ์ด ๋ณต์กํจ (์คํ์ผ ์ง๋ ฌํ ํ์)
- ํด๋์ค ์ด๋ฆ์ด ๋์ ์ผ๋ก ์์ฑ๋์ด ๋๋ฒ๊น ์ด ์ด๋ ค์
์ฑ๋ฅ ๋น๊ต
| ํญ๋ชฉ | CSS Modules | CSS-in-JS (๋ฐํ์) |
|---|---|---|
| ๋ฒ๋ค ํฌ๊ธฐ | ์์ | ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ๋จ |
| ์ด๊ธฐ ๋ ๋๋ง | ๋น ๋ฆ | ์ฝ๊ฐ ๋๋ฆผ (JS ํ์ฑ) |
| ๋์ ์คํ์ผ | ํด๋์ค ์กฐํฉ์ผ๋ก ์ฒ๋ฆฌ | props๋ก ์ง๊ด์ ์ฒ๋ฆฌ |
| SSR | ๊ฐ๋จ | ๋ณ๋ ์ค์ ํ์ |
| CSS ํ์ฑ ์ฃผ์ฒด | ๋น๋ํ์ | ๋ฐํ์ |
Zero-runtime CSS-in-JS์ ๋ฑ์ฅ
๊ธฐ์กด CSS-in-JS์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋น๋ ํ์์ CSS๋ฅผ ์ถ์ถํ๋ zero-runtime ๋ฐฉ์์ด ๋ฑ์ฅํ๋ค.
- Linaria: ๋ฐํ์ ์์ด static CSS ์ถ์ถ, CSS Modules์ฒ๋ผ ๋์
- vanilla-extract: TypeScript ๊ธฐ๋ฐ type-safe CSS, ๋น๋ํ์ ์ฒ๋ฆฌ
- Panda CSS: utility-first + ๋น๋ํ์ ์ถ์ถ
// vanilla-extract ์์
import { style } from '@vanilla-extract/css';
export const button = style({
padding: '8px 16px',
backgroundColor: '#007bff',
color: 'white',
borderRadius: '4px',
});
๋์ ์คํ์ผ์ CSS custom properties(๋ณ์)๋ฅผ ํ์ฉํด์ ์ฒ๋ฆฌํ๋ค.
์ ํ ๊ธฐ์ค
| ์ํฉ | ์ถ์ฒ ๋ฐฉ์ |
|---|---|
| ์ฑ๋ฅ ์ค์, SSR ํ๊ฒฝ | CSS Modules ๋๋ zero-runtime CSS-in-JS |
| ๋์ ์คํ์ผ์ด ๋ง์ UI | CSS-in-JS (styled-components, Emotion) |
| ํ์ ์์ ์ฑ ์ค์ | vanilla-extract |
| Next.js App Router | CSS Modules (์๋ฒ ์ปดํฌ๋ํธ ํธํ) |
| ์๊ท๋ชจ ํ๋ก์ ํธ | CSS Modules๋ก ์์์ด ๋ฌด๋ |
Next.js 13+ App Router์์๋ ์๋ฒ ์ปดํฌ๋ํธ๊ฐ ๊ธฐ๋ณธ์ด๊ธฐ ๋๋ฌธ์, ํด๋ผ์ด์ธํธ ๋ฐํ์์ด ํ์ํ styled-components ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ 'use client' ์ง์์ด๊ฐ ์๋ ์ปดํฌ๋ํธ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ค. ๊ทธ๋์ Next.js ๊ณต์ ๋ฌธ์์์๋ CSS Modules๋ฅผ ๊ธฐ๋ณธ ์คํ์ผ๋ง ๋ฐฉ๋ฒ์ผ๋ก ๊ถ์ฅํ๋ค.