Native CSS
이 가이드는 experiments.css를 사용해 webpack의 네이티브 CSS 처리를 사용하는 방법과, 기존 설정에서 css-loader, style-loader, mini-css-extract-plugin을 제거하며 마이그레이션하는 방법을 설명합니다.
Getting Started
webpack 설정에서 네이티브 CSS 지원을 활성화하세요.
webpack.config.js
export default {
experiments: {
css: true,
},
};이 옵션을 활성화하면 webpack은 .css 파일을 일급 모듈로 이해하고 loader에 전달하는 대신 직접 파싱합니다.
What's built-in
"Built-in"은 체인에 loader 없이 webpack이 직접 작업한다는 의미이며, CSS loader의 모든 기능을 지원한다는 의미는 아닙니다. 정확한 지원 범위는 다음과 같습니다.
| 내장 지원 | 여전히 loader 또는 plugin 필요 |
|---|---|
.css 파싱, @import 및 url()/image-set()/src()/image() 리졸브 | Preprocessor — Sass, Less, Stylus, PostCSS는 항상 해당 loader 유지 |
CSS Modules: composes, @value, :export, :local()/:global() | css-loader의 importLoaders, localIdentRegExp, getJSON, url/import filter callback |
.css 파일 추출 또는 런타임에 <style> 태그 주입 | style-loader의 insert, attributes, styleTagTransform, lazy/singleton 주입 모드 |
| content hash, minification, browserslist vendor prefix | @custom-media/@custom-selector 외에 PostCSS plugin이 수행하는 작업 |
내장 @custom-media 및 @custom-selector | - |
| 스타일시트의 Hot Module Replacement | - |
따라서 기본 옵션으로 css-loader와 style-loader(또는 mini-css-extract-plugin)를 사용하는 프로젝트에는 CSS loader가 필요하지 않습니다. 오른쪽 열의 옵션을 사용하는 프로젝트는 필요한 파일에만 해당 loader를 유지할 수 있으며 두 방식을 규칙별로 함께 사용할 수 있습니다.
Importing CSS
실험 기능을 활성화한 뒤에는 JavaScript에서 .css 파일을 직접 import할 수 있습니다.
src/index.js
import "./styles.css";
const element = document.createElement("h1");
element.textContent = "Hello native CSS";
document.body.appendChild(element);src/styles.css
h1 {
color: #1f6feb;
}Webpack은 이 CSS를 처리해 빌드 결과물에 포함합니다.
CSS module types
Native CSS는 네 가지 Rule.type 값을 도입합니다. 어떤 값이 적용되는지 이해하는 것이 마이그레이션의 핵심인데, 각각이 서로 다른 css-loader modules.mode에 대응하기 때문입니다.
| 타입 | 스코프 지정 | 대응하는 css-loader 설정 |
|---|---|---|
css | 전역, CSS Modules 파싱 없음 | modules: false |
css/global | 전역 selector, 하지만 :local()은 적용됨 | modules.mode: 'global' |
css/module | 기본적으로 local, :global()로 전역으로 빠져나갈 수 있음 | modules.mode: 'local' |
css/auto | *.module.css / *.modules.css에는 css/module, 그 외에는 일반 css(전역, CSS Modules 파싱 없음)를 선택 | modules.auto: true |
webpack이 /\.css$/i에 대해 기본으로 추가하는 rule은 css/auto이므로, *.module.css 파일은 CSS Modules가 되고 나머지는 모두 전역으로 유지됩니다. 이는 가장 일반적인 css-loader 설정과 기본적으로 동일합니다.
CSS Modules
css/auto를 사용할 때는 파일 이름을 *.module.css(또는 *.modules.css)로 지정하면 CSS Modules로 처리할 수 있습니다.
src/button.module.css
.button {
background: #0d6efd;
color: white;
border: 0;
border-radius: 4px;
padding: 8px 12px;
}src/index.js
import * as styles from "./button.module.css";
const button = document.createElement("button");
button.className = styles.button;
button.textContent = "Click me";
document.body.appendChild(button);parser와 generator 옵션으로 CSS Modules 동작을 사용자 정의할 수 있습니다. 아래의 All options with examples를 참고하세요.
webpack.config.js
export default {
experiments: {
css: true,
},
module: {
parser: {
"css/auto": {
namedExports: true,
},
},
generator: {
"css/auto": {
exportsConvention: "camel-case-only",
localIdentName: "[uniqueName]-[id]-[local]",
},
},
},
};Supported CSS Modules features
Native CSS Modules는 css-loader와 같은 authoring 기능을 이해하므로, 대부분의 stylesheet는 수정 없이 그대로 마이그레이션할 수 있습니다.
composes: 하나의 local class를 다른 local class로 합성합니다(composes: foo from "./other.module.css"포함). export 결과는 공백으로 구분된 class name 목록으로 해석됩니다.@value: 재사용 가능한 값을 선언하고 import합니다(@value primary: #1f6feb;,@value primary from "./vars.module.css").:export: 임의의 key/value 쌍을 JavaScript에 노출합니다.:local()/:global(): 어떤 module type에서든 인라인으로 scope를 전환합니다.
/* button.module.css */
@value brand: #1f6feb;
.base {
padding: 8px 12px;
}
.primary {
composes: base;
background: brand;
}
:export {
brandColor: brand;
}Output modes (exportType)
하나의 CSS module은 네 가지 방식으로 출력할 수 있습니다. exportType parser 옵션으로 이를 선택하며, 각각은 기존 툴체인의 다른 부분을 대체합니다.
exportType | 동작 | 대체 대상 |
|---|---|---|
"link" (기본값) | .css 파일로 추출하고 <link>로 로드 | mini-css-extract-plugin |
"style" | 런타임에서 <style> element를 주입 | style-loader |
"text" | CSS를 string으로 export | css-loader exportType: 'string' |
"css-style-sheet" | 생성 가능한 CSSStyleSheet를 export | css-loader exportType: 'css-style-sheet' |
module type별로 전역 설정할 수 있습니다.
export default {
experiments: { css: true },
module: {
parser: {
"css/auto": {
exportType: "style",
},
},
},
};또는 일부 파일에만 rule별로 설정할 수 있습니다.
export default {
experiments: { css: true },
module: {
rules: [
{
test: /\.css$/i,
type: "css/auto",
parser: { exportType: "style" },
},
],
},
};Custom media and custom selectors
Native CSS는 빌드 시점에 @custom-media와 @custom-selector를 리졸브하므로 이 두 규칙을 위해 흔히 사용하던 PostCSS plugin이 필요하지 않습니다.
src/styles.css
@custom-media --narrow (width <= 40rem);
@custom-selector :--heading h1, h2, h3;
@media (--narrow) {
:--heading {
font-size: 1.25rem;
}
}리졸브는 파일 단위입니다. 정의는 이를 선언한 파일에만 적용되며, 해당 파일이 @import한 대상이나 전체 프로젝트에는 적용되지 않습니다. 둘 다 기본적으로 활성화되며 module.parser.css.customMedia와 module.parser.css.customSelectors로 각각 비활성화할 수 있습니다. 체인의 PostCSS plugin이 이미 처리한다면 비활성화하세요.
Minification
optimization.minimize가 활성화되면 내장 minimizer가 CSS 애셋을 최소화합니다. mode: 'production'에서는 기본적으로 활성화되며 별도로 설치하거나 연결할 항목이 없습니다.
webpack.config.js
export default {
mode: "production",
experiments: { css: true },
};이는 css-minimizer-webpack-plugin과 그 기반의 cssnano 체인을 대체합니다. 색상·숫자·값·selector 단축, longhand 및 규칙 병합, 불필요한 규칙 제거, 상수 기반 calc()와 수학 함수 계산, 따옴표 정규화, media query range 표기 등 스타일시트의 의미를 유지하는 모든 변환이 기본 활성화됩니다. script가 다시 읽는 텍스트를 변경하는 rewriteCustomProperties와 압축 후 효과가 적은 convertLengthUnits는 비활성화되어 있습니다.
객체 형태의 optimization.minimize로 설정하세요.
export default {
mode: "production",
experiments: { css: true },
optimization: {
minimize: {
css: {
comments: false,
// `getComputedStyle().getPropertyValue()`는 작성된 custom property
// 텍스트를 그대로 반환하므로 아무도 읽지 않을 때만 활성화합니다.
rewriteCustomProperties: true,
},
},
},
};JavaScript minification은 유지하면서 CSS만 비활성화하려면 minimize: { css: false }를 사용하세요.
Vendor prefixes
minimizer는 browserslist target에 맞춰 vendor prefix를 관리합니다. 선택한 브라우저에 여전히 필요한 property, at-rule, pseudo-selector의 -webkit-/-moz-/-ms- 표기를 추가하고 아무 브라우저에도 필요하지 않은 표기는 제거합니다. 따라서 대부분의 프로젝트에서 autoprefixer와 이를 위한 postcss-loader를 의존성에서 제거할 수 있습니다.
.browserslistrc
> 0.5%
last 2 versions
not deadwebpack.config.js
export default {
mode: "production",
target: "browserslist",
experiments: { css: true },
};Resource hints and font preloading
CSS에서 참조하는 애셋에 plugin 없이 <link rel="preload">/prefetch hint를 지정할 수 있습니다. module.parser.css.fontPreload는 HTML 엔트리의 초기 CSS에서 접근 가능한 각 @font-face에 preload 하나를 추가합니다. 모든 형식을 preload하면 폰트를 두 번 다운로드하므로 기본 src URL만 사용합니다.
webpack.config.js
export default {
experiments: { css: true, html: true },
output: {
// preload는 폰트의 CORS 요청과 일치해야 합니다.
crossOriginLoading: "anonymous",
},
module: {
parser: {
css: {
fontPreload: true,
},
},
},
};module.parser.css.urlHints는 url(...)로 접근하는 다른 항목에 애셋별 규칙을 제공하고, output.resourceHints는 프로젝트 전체 규칙을 설정합니다.
export default {
experiments: { css: true, html: true },
module: {
parser: {
css: {
urlHints: [
{ test: /\.woff2$/, preload: true, as: "font" },
{ test: /hero\.avif$/, preload: true, as: "image" },
],
},
},
},
};CSS referenced from HTML
experiments.html을 활성화하면 HTML 페이지의 <link rel="stylesheet">가 CSS 청크 엔트리가 되고 인라인 <style> 본문과 style="" 속성도 같은 CSS 파이프라인을 거칩니다. 내부의 @import와 url()은 HTML 파일을 기준으로 리졸브되며 위의 minimizer 옵션도 적용됩니다.
webpack.config.js
export default {
entry: "./src/index.html",
experiments: { css: true, html: true },
};전체 HTML 기능은 Native HTML 가이드를 참고하세요.
Migration Guide
Automated migration (codemod)
@webpack/css-plugins-to-native-css codemod를 사용하면 마이그레이션의 대부분을 자동으로 처리할 수 있습니다.
npx codemod @webpack/css-plugins-to-native-css이 codemod는 해당 릴리스에서 도입된 experiments.css: "auto" 기본값을 사용하므로 webpack 5.109.0 이상이 필요합니다.
At a glance
| 기존 설정 | 네이티브 대응 기능 |
|---|---|
mini-css-extract-plugin (MiniCssExtractPlugin.loader) | 내장 추출 기능(기본값 exportType: "link") |
MiniCssExtractPlugin filename / chunkFilename | output.cssFilename / output.cssChunkFilename |
style-loader | exportType: "style" |
css-loader | 내장 CSS 파싱 기능(loader 불필요) |
css-loader url / import | module.parser.css.url / import (둘 다 기본값 true) |
css-loader modules (.module.css 자동 감지) | css/auto 모듈 타입 |
css-loader modules.mode | css/module / css/global type + pure |
css-loader modules.localIdentName | generator localIdentName |
css-loader modules.exportLocalsConvention | generator exportsConvention |
css-loader modules.namedExport | module.parser.css.namedExports (기본값 true) |
css-loader modules.exportOnlyLocals | generator exportsOnly |
css-loader esModule | generator esModule(기본값 true) |
css-loader exportType: 'string' / 'css-style-sheet' | exportType: "text" / "css-style-sheet" |
css-minimizer-webpack-plugin / cssnano | optimization.minimize — production에서 기본 활성화 |
postcss-loader + autoprefixer | optimization.minimize.css.vendorPrefixes — browserslist target에서 기본 활성화 |
postcss-custom-media / postcss-custom-selectors | 내장 @custom-media / @custom-selector |
preload-webpack-plugin (폰트) | module.parser.css.fontPreload |
한 번에 loader 하나씩 마이그레이션하세요. 아래 섹션은 각 단계에서 빌드가 깨지지 않도록 하는 순서로 구성되어 있습니다.
1. Start from a classic setup
webpack.config.js
import MiniCssExtractPlugin from "mini-css-extract-plugin";
export default {
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [new MiniCssExtractPlugin()],
};2. Enable native CSS
webpack.config.js
export default {
experiments: {
css: true,
},
};이제 내장된 /\.css$/i → css/auto rule이 .css import를 처리합니다. 아래 섹션에서 각 옵션에 대응되는 기능을 확인한 뒤, 기존의 custom rule과 plugin을 제거하세요.
3. Replace mini-css-extract-plugin
Native CSS는 기본적으로 stylesheet를 추출하고(exportType: "link"), 여기에 content hash까지 추가하므로 plugin과 그 loader가 더 이상 필요하지 않습니다.
webpack.config.js
-import MiniCssExtractPlugin from "mini-css-extract-plugin";
-
export default {
+ experiments: {
+ css: true,
+ },
- module: {
- rules: [
- {
- test: /\.css$/i,
- use: [MiniCssExtractPlugin.loader, "css-loader"],
- },
- ],
- },
- plugins: [new MiniCssExtractPlugin()],
};남은 plugin 옵션은 다음과 같이 대응됩니다.
mini-css-extract-plugin | 네이티브 대응 기능 |
|---|---|
filename | output.cssFilename |
chunkFilename | output.cssChunkFilename |
loader publicPath | output.publicPath |
loader esModule | generator esModule(기본값 true) |
ignoreOrder | 해당 없음 — 순서 충돌 경고는 옵션으로 무음 처리할 수 없습니다. ignoreWarnings를 사용하거나 CssModulesPlugin.getCompilationHooks(compilation).orderModules로 직접 순서를 정의하세요 |
webpack.config.js
export default {
experiments: { css: true },
output: {
cssFilename: "[name].[contenthash].css",
cssChunkFilename: "[id].[contenthash].css",
},
};4. Replace css-loader
대부분의 css-loader 옵션은 module.parser.css와 module.generator.css 아래에 네이티브 대응 기능이 있습니다. 일반적인 기본값(url, import, namedExports 모두 활성화)은 보통의 css-loader 설정과 이미 일치하므로, 많은 프로젝트에서는 parser 설정이 아예 필요하지 않습니다.
css-loader 옵션 | 네이티브 대응 기능 |
|---|---|
url | module.parser.css.url, 기본값 true |
import | module.parser.css.import, 기본값 true |
importLoaders | n/a, 체인에 있는 loader가 @import된 파일에도 자동으로 적용됨 |
sourceMap | devtool로 제어됨(css별 설정 지원) |
esModule | module.generator.css.esModule, 기본값 true |
exportType: 'string' | parser exportType: "text" |
exportType: 'css-style-sheet' | parser exportType: "css-style-sheet" |
modules (auto-detect) | css/auto module type(내장) |
modules.mode: 'local' | css/module type |
modules.mode: 'global' | css/global type |
modules.mode: 'pure' | parser pure: true |
modules.localIdentName | generator localIdentName |
modules.exportLocalsConvention | generator exportsConvention |
modules.namedExport | parser namedExports, 기본값 true |
modules.exportOnlyLocals | generator exportsOnly |
modules.localIdentHashSalt | generator localIdentHashSalt |
modules.localIdentHashFunction | generator localIdentHashFunction |
예를 들어, 아래와 같은 css-loader CSS Modules 설정은
export default {
module: {
rules: [
{
test: /\.module\.css$/i,
use: [
{
loader: "css-loader",
options: {
modules: {
localIdentName: "[local]-[hash:base64:6]",
exportLocalsConvention: "camel-case-only",
namedExport: true,
},
},
},
],
},
],
},
};다음과 같이 바뀝니다.
webpack.config.js
export default {
experiments: { css: true },
module: {
parser: {
"css/auto": {
namedExports: true,
},
},
generator: {
"css/auto": {
localIdentName: "[local]-[hash:base64:6]",
exportsConvention: "camel-case-only",
},
},
},
};일부 css-loader 옵션은 동작 방식이 다릅니다.
getLocalIdent: custom function 대신 Native CSS는localIdentName템플릿을 사용하며, 이 값도 function을 받을 수 있습니다.getJSON: class-name mapping은 CSS module 자체에서 export되며 compilation의 module graph에서도 읽을 수 있으므로, framework가 디스크의 JSON 파일을 필요로 할 때는 작은 plugin으로 직렬화할 수 있습니다. 서버 사이드 렌더링에서는 보통 아예 필요하지 않습니다. 자세한 내용은 Server-side rendering을 참고하세요.localIdentRegExp와 filter 스타일의url/importcallback은 네이티브 대응 기능이 없습니다. 해당 파일에는 계속css-loader를 사용하거나,IgnorePlugin으로 특정 request를 제외하세요.
5. Replace style-loader
파일을 추출하는 대신 런타임에 style을 주입하기 위해 style-loader를 사용했다면 exportType: "style"을 설정하세요.
webpack.config.js
export default {
experiments: { css: true },
module: {
parser: {
"css/auto": {
exportType: "style",
},
},
},
};이 설정은 webpack runtime에서 <style> element를 주입하므로, 기본 style-loader(injectType: "styleTag") 사용 사례를 대체할 수 있습니다. 일부 파일만 주입하고 나머지는 추출하려면 하나의 rule에만 범위를 제한하세요.
export default {
experiments: { css: true },
module: {
rules: [
{
test: /\.inline\.css$/i,
type: "css/auto",
parser: { exportType: "style" },
},
],
},
};style-loader 옵션에 대한 메모: injectType: "linkTag"는 기본 exportType: "link"(추출)에 대응합니다. attributes, insert, styleTagTransform에는 네이티브 대응 기능이 없으므로, 이 기능에 의존한다면 계속 style-loader를 사용하세요.
6. Keep using preprocessors (Sass, Less, PostCSS)
Native CSS는 CSS loader를 대체하는 것이지 preprocessor loader를 대체하는 것은 아닙니다. use에는 preprocessor loader를 유지하고, webpack이 loader의 출력을 CSS로 취급하도록 rule의 type을 css/auto로 설정하세요.
webpack.config.js
export default {
experiments: { css: true },
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: ["postcss-loader", "sass-loader"],
type: "css/auto",
},
],
},
};sass-loader는 CSS로 컴파일하고, postcss-loader는 이를 후처리하며, 그 이후에는 Native CSS가 추출, url(), CSS Modules를 담당합니다. 같은 패턴을 less-loader, stylus-loader 등에도 적용할 수 있습니다.
7. Server-side rendering (node + web)
SSR에서는 보통 두 번 빌드합니다. 브라우저용 web bundle 하나와 서버용 node bundle 하나입니다. 이때 CSS Modules의 class name이 서로 일치해야 서버에서 렌더링한 마크업이 클라이언트에서 정상적으로 hydrate됩니다. css-loader의 getJSON은 이런 정보를 왕복하기 위해 자주 사용되었지만, Native CSS에서는 target 간에 localIdentName을 결정론적으로 맞춰 이 왕복 자체를 피할 수 있습니다.
두 빌드가 localIdentName 템플릿을 공유하고, 템플릿의 모든 placeholder를 같은 값으로 해석하며(예를 들어 [id]는 module ID이므로 별도 빌드 사이에서 달라질 수 있습니다), 아래에서 설명하는 로컬 식별자 해시 입력을 공유한다면 target 간에 class name이 결정론적으로 생성됩니다. 읽기 쉬운 path 기반 템플릿은 모든 target에서 동작합니다.
webpack.config.js
const common = {
experiments: { css: true },
module: {
rules: [
{
test: /\.module\.css$/i,
type: "css/module",
generator: {
// `[file]__[local]`은 target 간에 안정적이므로 `getJSON` 동기화가 필요 없습니다.
localIdentName: "[file]__[local]",
},
},
],
},
};
export default [
{ ...common, name: "web", target: "web" },
{ ...common, name: "node", target: "node" },
];node target에서는 CSS generator의 기본값이 exportsOnly: true이므로, 서버 빌드는 class-name mapping만 export하고 stylesheet는 생성하지 않습니다. 이는 SSR renderer에 정확히 필요한 동작입니다. 브라우저 빌드는 여전히 실제 CSS를 추출합니다. 단일 config를 선호한다면 target: ["web", "node"]로 두 환경 모두에서 동작하는 universal bundle을 만들 수도 있습니다.
프로덕션 기본값인 localIdentName: "[fullhash]"(및 모든 [hash] 템플릿)도 SSR에서 안전합니다. 이름과 달리 localIdentName의 [fullhash]는 compilation hash가 아닙니다. 다음 값으로 계산되는 로컬 식별자 해시입니다.
localIdentHashSalt(기본값은output.hashSalt)output.uniqueName— 템플릿에[uniqueName]이 없어도 포함됩니다.context에 대한 상대 리소스 경로- 로컬 class name
따라서 web 빌드와 node 빌드에서 서로 일치하는 짧은 class name(기본값은 6자)을 얻을 수 있으며, 두 파일에서 같은 로컬 이름을 사용해도 서로 다른 class name이 생성됩니다.
8. Drop the CSS minimizer and autoprefixer
production에서는 optimization.minimize가 둘 다 지원하므로 minimizer plugin과, 대부분의 프로젝트에서는 autoprefixer만 실행하던 PostCSS 체인을 제거할 수 있습니다.
webpack.config.js
-import CssMinimizerPlugin from "css-minimizer-webpack-plugin";
-
export default {
mode: "production",
+ target: "browserslist",
+ experiments: { css: true },
- optimization: {
- minimizer: ["...", new CssMinimizerPlugin()],
- },
};내장 minimizer의 동작과 설정 방법은 Minification을 참고하세요.
9. Keep imports unchanged and validate
JavaScript import는 그대로 유지됩니다.
import "./styles.css";
import * as styles from "./button.module.css";그다음 아래 항목을 확인하세요.
- 개발 환경에서 style이 올바르게 적용되는지
- 프로덕션에서 추출된
.css파일이 생성되는지 - CSS Modules export가 기존 사용 방식과 일치하는지
All options with examples
옵션은 module.parser와 module.generator 아래에서 module type별로 설정합니다. key는 css, css/auto, css/global, css/module이며, 아래 예제는 기본 rule에 사용되는 css/auto를 기준으로 합니다.
Parser options
표에 별도로 표시하지 않은 boolean parser 옵션의 기본값은 true입니다.
| 옵션 | 타입 | 기본값 | 설명 | |||
|---|---|---|---|---|---|---|
import | boolean | true | @import at-rule을 처리합니다. | - | - | - |
url | boolean | true | url()/image-set()/src()/image()를 처리합니다. | - | - | - |
namedExports | boolean | true | CSS Modules local을 ES module named export로 내보냅니다. | - | - | - |
exportType | "link" | "style" | "text" | "css-style-sheet" | "link" | CSS 출력 방식입니다(Output modes 참고). | |||
pure | boolean | false | 모든 selector에 local class/id가 필요한 strict pure mode입니다. css/module, css/auto 전용입니다. | - | - | - |
as | "stylesheet" | "block-contents" | "stylesheet" | 전체 stylesheet 또는 block 내용으로 파싱합니다. | - | - | |
animation | boolean | true | local @keyframes 이름을 변경합니다. | - | - | - |
container | boolean | true | local @container 이름을 변경합니다. | - | - | - |
customIdents | boolean | true | custom identifier 이름을 변경합니다. | - | - | - |
dashedIdents | boolean | true | dashed identifier(custom property) 이름을 변경합니다. | - | - | - |
function | boolean | true | local @function 이름을 변경합니다. | - | - | - |
grid | boolean | true | grid line/area identifier 이름을 변경합니다. | - | - | - |
customMedia | boolean | true | 빌드 시점에 @custom-media를 리졸브합니다. | - | - | - |
customSelectors | boolean | true | 빌드 시점에 @custom-selector를 리졸브합니다. | - | - | - |
fontPreload | boolean | false | HTML 엔트리에서 각 @font-face의 기본 src를 preload합니다. | - | - | - |
urlHints | UrlHintRule[] | — | url(...)로 참조한 애셋의 resource hint 규칙입니다. | - | - | - |
export default {
experiments: { css: true },
module: {
parser: {
"css/auto": {
import: true,
url: true,
namedExports: true,
exportType: "link",
pure: false,
// `@keyframes`만 이름을 바꾸고, `@container` / grid identifier는 그대로 둡니다.
animation: true,
container: false,
grid: false,
},
},
},
};Generator options
| 옵션 | 타입 | 기본값 | 설명 | |||||
|---|---|---|---|---|---|---|---|---|
localIdentName | string | function | "[uniqueName]-[id]-[local]" (dev) / "[fullhash]" (prod) | 생성할 local class name의 템플릿입니다. | - | - | - | - | |
exportsConvention | "as-is" | "camel-case" | "camel-case-only" | "dashes" | "dashes-only" | function | "as-is" | export되는 local 값의 naming convention입니다. | |||||
exportsOnly | boolean | document가 없는 target(예: node)에서는 true, 그 외에는 false | local 값만 export하고 stylesheet 생성은 건너뜁니다(SSR). | - | - | - | - | - |
esModule | boolean | true | 생성되는 JS에 ES module 문법을 사용합니다. | - | - | - | - | - |
localIdentHashFunction | string | output.hashFunction | localIdentName hash에 사용할 hash function입니다. | - | - | - | - | - |
localIdentHashDigest | string | "base64url" | local ident의 hash digest 인코딩입니다. | - | - | - | - | - |
localIdentHashDigestLength | number | 6 | local ident의 hash digest 길이입니다. | - | - | - | - | - |
localIdentHashSalt | string | output.hashSalt | local ident에 사용할 hash salt입니다. | - | - | - | - | - |
export default {
experiments: { css: true },
module: {
generator: {
"css/auto": {
localIdentName: "[uniqueName]-[id]-[local]",
exportsConvention: "camel-case-only",
esModule: true,
exportsOnly: false,
localIdentHashDigest: "base64url",
localIdentHashDigestLength: 6,
},
},
},
};exportsConvention도 string 또는 string[]를 반환하는 function을 받을 수 있습니다. 배열을 반환하면 local 값을 여러 alias로 export하게 되며, 이는 css-loader와 동일한 동작입니다.
Popular examples
CSS Modules with named exports
src/app.module.css
.primary {
color: #1f6feb;
}
.large-text {
font-size: 2rem;
}src/index.js
import { largeText, primary } from "./app.module.css";
document.body.classList.add(primary, largeText);webpack.config.js
export default {
experiments: { css: true },
module: {
generator: {
"css/auto": {
exportsConvention: "camel-case-only",
},
},
},
};Extract hashed CSS files for production
webpack.config.js
export default {
mode: "production",
experiments: { css: true },
output: {
cssFilename: "css/[name].[contenthash].css",
cssChunkFilename: "css/[id].[contenthash].css",
},
};Inject <style> tags at runtime (style-loader style)
webpack.config.js
export default {
experiments: { css: true },
module: {
parser: {
"css/auto": {
exportType: "style",
},
},
},
};Import a constructable stylesheet
src/index.js
import sheet from "./theme.css" with { type: "css" };
document.adoptedStyleSheets = [sheet];Webpack은 with { type: "css" } import assertion을 자동으로 exportType: "css-style-sheet"에 연결하므로, CSSStyleSheet instance를 바로 얻을 수 있습니다.
Import CSS as a string
webpack.config.js
export default {
experiments: { css: true },
module: {
parser: {
"css/auto": {
exportType: "text",
},
},
},
};src/index.js
import css from "./styles.css";
const style = new CSSStyleSheet();
style.replaceSync(css);Global styles + scoped modules side by side
기본 css/auto rule에서는 *.module.css는 scoped로, 나머지는 전역으로 처리되므로 추가 설정이 필요 없습니다.
import "./reset.css"; // 전역
import * as card from "./card.module.css"; // 범위 지정됨Tailwind CSS (or any PostCSS chain)
Native CSS는 PostCSS가 아니라 CSS loader를 대체합니다. postcss-loader를 유지하고 규칙에 CSS module 타입을 지정하여 webpack이 출력 결과를 처리하도록 하세요.
webpack.config.js
export default {
experiments: { css: true },
module: {
rules: [
{
test: /\.css$/i,
use: ["postcss-loader"],
type: "css/auto",
},
],
},
};Sass with CSS Modules
webpack.config.js
export default {
experiments: { css: true },
module: {
rules: [
{
test: /\.module\.s[ac]ss$/i,
use: ["sass-loader"],
type: "css/module",
},
{
test: /\.s[ac]ss$/i,
exclude: /\.module\.s[ac]ss$/i,
use: ["sass-loader"],
type: "css/global",
},
],
},
};Group every stylesheet into one file
기본적으로 CSS는 청크 그래프를 따르므로 각 엔트리에 자체 스타일시트가 생성됩니다. CSS module 타입을 key로 사용하는 cache group을 사용하면 모두 하나로 모을 수 있습니다.
webpack.config.js
export default {
mode: "production",
entry: { home: "./src/home.js", about: "./src/about.js" },
experiments: { css: true },
output: {
cssFilename: "css/[name].[contenthash].css",
},
optimization: {
splitChunks: {
cacheGroups: {
styles: {
type: "css/auto",
name: "styles",
chunks: "all",
enforce: true,
},
},
},
},
};Theme switching with constructable stylesheets
src/index.js
import dark from "./theme-dark.css" with { type: "css" };
import light from "./theme-light.css" with { type: "css" };
const media = matchMedia("(prefers-color-scheme: dark)");
const apply = () => {
document.adoptedStyleSheets = [media.matches ? dark : light];
};
media.addEventListener("change", apply);
apply();각 theme는 별도의 CSSStyleSheet이므로 교체할 때 다시 파싱하지 않으며 스타일이 적용되지 않은 콘텐츠가 순간적으로 표시되지 않습니다.
Development server with hot-reloaded styles
webpack.config.js
export default {
mode: "development",
experiments: { css: true },
devServer: {
hot: true,
},
};CSS 변경 사항은 제자리에서 적용되며 설정할 style-loader나 별도의 HMR 연결이 없습니다.
Importing a package's stylesheet
/* src/styles.css */
@import "bootstrap";Webpack은 패키지의 package.json에서 style 필드를 찾고 없으면 main을 사용하므로 bare specifier가 JavaScript와 같은 방식으로 CSS에서도 작동합니다.
Experimental status & known limitations
experiments.css는 명시적으로 실험 단계의 기능이므로, 선택적으로 도입하고 넓게 적용하기 전에 충분히 테스트해야 합니다.
- API와 동작은 webpack v6에서 기본값이 되기 전까지 계속 바뀔 수 있습니다.
- 일부 loader 옵션에는 바로 대응되는 기능이 없습니다.
css-loader의localIdentRegExp와 filter callback,style-loader의attributes/insert/styleTagTransform이 여기에 해당합니다. 이런 기능이 필요한 파일에는 해당 loader를 계속 사용하세요. (getLocalIdent는localIdentName의 function 형태로 대응할 수 있고,getJSON/SSR은 target 간 class name 일치로 다룰 수 있습니다.) importLoaders에 해당하는 기능은 없습니다. 체인에 있는 loader는@import된 파일에도 자동으로 적용됩니다.- 프로젝트가 고급 loader chain에 의존하고 있다면 완전히 마이그레이션하기 전에 각 부분을 검증하세요.



