답은 NO.
그 이유에 대해서 차근 차근 설명해보고자 합니다.
CSS는 전역 스코프를 갖는다.
CSS는 기본적으로 전역적인 스코프를 가집니다. 순수한 css를 다시 떠올려보면 쉽게 그 이유를 알 수 있습니다.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="main.css">
<link rel="stylesheet" href="buttons.css">
<style data-styled="true">
.sc-bdVaJa { background-color: blue; color: white; }
</style>
</head>
<body>
<div class="container">
<button class="btn">Click me</button>
</div>
<script src="app.js"></script>
</body>
</html>
순수한 css는, html의 <link>태그로 css 파일들을 불러오거나, <style>태그 안의 텍스트 엘리먼트에 직접 적어 넣습니다.
이렇게 가져온 css의 클래스네임을 인식하여, 필요한 요소에 스타일링을 위해 클래스네임을 집어 넣는 구조죠.
그래서 캐스케이딩 규칙(우선순위 법칙)에 따라, 동일한 클래스명을 갖는 경우에는 우선 순위가 더욱 높은 클래스명이 적용되고
이로 인해 스타일링이 꼬이는 일이 비일비재하게 발생했습니다. 클래스명을 겹치지 않게 구현하려니 머리 아팠고요.
이를 간단하게 해결해주기 위해 .module.css 파일을 활용할 수 있게 됩니다.
.css 파일이 아닌 .module.css 파일은 css를 모듈로 내보내는 것 아닌가요?
반은 맞고 반은 틀립니다. 정확히 알아봅시다.
우선 원래 정식 웹 표준에 따르면 브라우저가 스타일링을 위해 파싱할 수 있는 파일은 .css가 유일합니다.
.module.css는 비표준이고, 빌드 도구에 의해 변환 후 사용됩니다.
이 변환 과정을 이해하면, css가 모듈로 내보내지는 것이 아니라는 것을 알 수 있습니다.
.module.css 파일의 변환 과정
빌드도구(혹은 번들러)가 변환을 담당합니다. Webpack을 예시로 들면, css-loader라는 설정을 통해 .module.css 파일을 변환합니다.
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.module\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]_[local]__[hash:base64:6]',
},
},
},
],
},
],
},
};
다음과 같은 .module.css 파일이 존재한다고 가정해봅시다.
/* Button.module.css */
.button {
background-color: blue;
color: white;
padding: 10px 15px;
border-radius: 4px;
}
.primary {
background-color: #0066cc;
}
.secondary {
background-color: #6c757d;
}
이를 빌드 도구에서는 다음과 같은 과정을 거칩니다.
1. CSS 파일 파싱
2. 클래스명에 고유 식별자(해시) 추가
3. (지역 스코프 구현)매핑 객체 생성 (원본 클래스명 → 변환된 클래스명)
4. CSS 내용과 매핑 객체를 별도로 처리
과정을 상세히 봅시다.
1. 원본 css의 클래스명을 고유한 해시값이 포함된 형태로 변환합니다.
/* 변환된 실제 CSS (빌드 후) */
.Button_button__a1b2c3 {
background-color: blue;
color: white;
padding: 10px 15px;
border-radius: 4px;
}
.Button_primary__d4e5f6 {
background-color: #0066cc;
}
.Button_secondary__g7h8i9 {
background-color: #6c757d;
}
2. 원본 클래스명과 변환된 클래스명 간의 매핑을 담은 자바스크립트 객체로 생성하여 내보냅니다.
// 빌드 과정에서 생성되는 가상의 JavaScript 모듈
export default {
"button": "Button_button__a1b2c3",
"primary": "Button_primary__d4e5f6",
"secondary": "Button_secondary__g7h8i9"
};
여기까지 이해되셨죠? 그럼 실제로 코드가 변환되는 예시를 봅시다.
원래는 아래와 같은 코드가
// Button.jsx
import styles from './Button.module.css';
function Button({ variant = 'button', children }) {
return (
<button className={`${styles.button} ${styles[variant]}`}>
{children}
</button>
);
}
export default Button;
아래와 같이 변환됩니다. (빌드 이후)
// 빌드 과정에서 자동으로 생성되는 코드 (단순화 버전)
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
// 실제 CSS 내용을 주입
const cssContent = `
.Button_button__a1b2c3 {
background-color: blue;
color: white;
padding: 10px 15px;
border-radius: 4px;
}
.Button_primary__d4e5f6 {
background-color: #0066cc;
}
.Button_secondary__g7h8i9 {
background-color: #6c757d;
}`;
// 스타일을 DOM에 주입하는 함수
const injectStylesIntoDOM = (css) => {
const styleElement = document.createElement('style');
styleElement.textContent = css;
document.head.appendChild(styleElement);
};
// CSS 내용을 DOM에 주입
injectStylesIntoDOM(cssContent);
// 클래스 매핑 객체를 export
export default {
"button": "Button_button__a1b2c3",
"primary": "Button_primary__d4e5f6",
"secondary": "Button_secondary__g7h8i9"
};
Vanlia-extract와 Styled-component의 차이
이제는 deprecated된 스타일드 컴포넌트. 바닐라 익스트랙트는 제로런타임을 지원하는데에 비해 스타일드 컴포넌트는 그렇지 못하죠.
제로런타임의 기준은, 클래스들을 빌드 타임에 생성해두느냐 안해두느냐의 차이입니다. 적용 자체는 동적으로 할 수도 있어요.
1. styled-component 로 구성된 프로젝트
해당 프로젝트를 까보면, <style>이라는 태그가 <head>에 들어가있는 것을 볼 수 있고 <style>태그는 비어져있는 것을 볼 수 있어요.
(하지만 눌러서 style 패널을 확인해보면 내용은 차있습니다.) 이는 자바스크립트가 실행되면서 동적으로 style의 요소들이 삽입되기 때문이에요. 그래서 <style>을 만듭니다.
2. vanlia-extract 로 구성된 프로젝트
해당 프로젝트는 <link> 라는 태그에 stylesheet으로 링크가 들어가 있고, 이미 css 파일이 로드되어있는 것을 볼 수 있습니다.
즉, 빌드타임에 만든 css 파일을 로드해서 가져오는 거죠! 그래서 성능이 더 좋습니다.
css-in-js 에서는 import 하잖아요
네, 일반적으로 저희는 대부분 스타일링 라이브러리를 사용하곤 하는데 이를 위해 import를 사용하긴 합니다.
하지만 결국 빌드를 하게 되면 HTML에 css파일을 추출하여 링킹해둡니다.
결론은?
css를 모듈처럼 불러올 수는 있으나, 진정한 의미의 모듈은 아닙니다.
전역적인 스코프를 지니기 때문입니다.
'Develop > Frontend' 카테고리의 다른 글
| Tanstack Query (React Query) 필수 지식 (0) | 2025.05.11 |
|---|---|
| 하이드레이션 에러(Hydration Error) (0) | 2025.05.07 |
| React Reconciliation 심화 이해를 바탕으로 리액트 잘 활용하기 (2) | 2025.04.18 |
| 개발자도구 Element 탭 강조 표시의 기준 (1) | 2025.04.18 |
| requestAnimationFrame 에 대한 이해 (4) | 2025.04.17 |