PostCSS 도입하면서 겪은 멘탈붕괴와 야근을 다 정리했다. 금요일 밤 10시에 autoprefixer 때문에 IE에서 레이아웃 박살나서 새벽까지 삽질한 사람이면 공감할 거다.
"5분이면 된다"는 거짓말
1. 기본 설치
npm install -D postcss postcss-cli autoprefixer
2. postcss.config.js 생성
module.exports = {
plugins: [
require('autoprefixer')
]
}
3. 즉시 써먹을 수 있는 CSS
/* input.css */
.card {
display: flex;
user-select: none;
backdrop-filter: blur(10px);
}
/* 자동으로 prefix 추가됨 */
.card {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
현실적인 Plugin 구성 - 삽질 끝에 찾은 황금 조합
최소한 이것들은 깔아라 (모든 프로젝트)
module.exports = {
plugins: [
require('autoprefixer'), // vendor prefix 자동 추가
require('cssnano')({ // 프로덕션 최적화
preset: 'default'
})
]
}
좀 더 제대로 쓰려면 (프로덕션 프로젝트)
module.exports = {
plugins: [
require('postcss-import'), // @import 처리
require('postcss-nesting'), // CSS nesting 지원
require('postcss-custom-properties'), // CSS 변수 fallback
require('autoprefixer'),
require('cssnano')
]
}
대기업급 구성 (삽질 마스터용)
module.exports = {
plugins: [
require('postcss-import'),
require('postcss-mixins'), // Sass-like mixin
require('postcss-simple-vars'), // Sass-like 변수
require('postcss-nesting'),
require('postcss-preset-env')({ // 미래 CSS 기능
stage: 0,
features: {
'nesting-rules': false // 중복 방지
}
}),
require('autoprefixer'),
process.env.NODE_ENV === 'production' ? require('cssnano') : null
].filter(Boolean)
}
주요 Framework별 통합
Next.js (기본 지원)
// next.config.js
module.exports = {
experimental: {
// PostCSS는 기본 활성화됨
}
}
// postcss.config.js만 만들면 자동 적용
Vite
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
css: {
postcss: './postcss.config.js'
}
})
Webpack
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader' // postcss-loader 추가
]
}
]
}
}
한국 개발환경에서 자주 터지는 문제들
개발하다가 진짜로 멘붕온 경험들을 정리했다. 특히 IE11 때문에 밤샌 사람들은 공감할 거다.
1. IE11 지옥 - "IE 때문에 또 터졌다"
공공기관 프로젝트에서 IE11 때문에 3일 동안 삽질한 결과물이다. CSS 변수 쓸 때마다 IE에서 안 보이는 거 진짜 짜증났는데, 이렇게 해결했다:
// IE11 때문에 밤샌 결과물
module.exports = {
plugins: [
require('postcss-custom-properties')({
preserve: true,
fallback: true // CSS 변수 fallback 생성
}),
require('autoprefixer')({
overrideBrowserslist: ['> 0.5%', 'last 2 versions', 'IE 11']
}),
require('postcss-flexbugs-fixes') // IE flexbox 버그 수정
]
}
2. 한글 폰트 로딩 지옥 - "왜 3초나 걸려?"
첫 로딩에 폰트 때문에 깜빡거리는 FOIT(Flash of Invisible Text) 현상. 특히 한글 폰트는 크기가 커서 더 심하다.
/* postcss-font-display plugin으로 해결 */
@font-face {
font-family: 'Noto Sans KR';
src: url('./fonts/NotoSansKR.woff2');
/* 자동으로 font-display: swap; 추가됨 */
}
3. 빌드 성능 문제 - "왜 이렇게 느려?"
가끔 Node.js 버전 바뀌면서 PostCSS가 갑자기 느려지는 경우가 있다. 대용량 CSS 파일 처리할 때 메모리 부족으로 빌드가 터지는 경우도 있고:
Error: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
이런 에러 나면 node --max-old-space-size=8192 옵션으로 해결되긴 하는데, 근본적으로는 이런 식으로 설정하면 좀 나아진다:
// 대용량 CSS 때문에 멘탈 터졌을 때 쓰는 설정
module.exports = {
plugins: [
require('postcss-import')({
skipDuplicates: true // 중복 import 방지 (메모리 절약)
}),
require('postcss-url')({
url: 'inline',
maxSize: 10, // 10kb 이하만 inline (용량 조절)
fallback: 'copy'
})
]
}
진짜 성능 어떤가? - 뻥 안 치고 실제 경험
빨라지긴 빨라진다. 우리 회사 프로젝트에서 Sass 빌드가 1분 10초 걸렸는데, PostCSS로 바꾸고 25초로 줄었다. Evil Martians가 성능 자랑하는 게 허풍이 아니었다.
근데 프로젝트마다 차이가 개판이다. 간단한 CSS 몇 개짜리 프로젝트면 별 차이 없고, 컴포넌트 500개 넘는 대형 프로젝트에서야 확실히 체감된다. 특히 Jenkins 같은 CI에서 빌드할 때 시간 차이가 확연하다.
자주 터지는 에러들과 해결법
Plugin 로딩 순서 문제
Error: Cannot read property 'forEach' of undefined
at processor.process.plugins.forEach (/node_modules/postcss/lib/processor.js:36:35)
아 이거 진짜 짜증난다. postcss-import를 제일 위에 안 두고 중간에 껴넣어서 생기는 문제다. postcss-import는 무조건 첫 번째에 두고 다시 돌려봐라.
Windows 경로 문제
Error: PostCSS plugin postcss-import: Didn't find ./styles.css in [
C:\Users\개발자\매우긴경로
ode_modules\...
]
Windows에서 PATH 길이 제한 때문에 터지는 경우가 있다. 프로젝트를 C:\dev 같은 짧은 경로로 옮기거나 절대경로로 바꿔라.
Plugin 버전 충돌
Error: Plugin "postcss-preset-env" requires PostCSS 8.1.0 but we have PostCSS 7.0.36
이거 진짜 짜증난다. npm ls postcss로 버전 확인하고 package.json에서 ^8.0.0으로 명시해라.
프로덕션 장애 경험담 - 진짜 실화
작년 12월에 cssnano 때문에 프로덕션에서 CSS 애니메이션이 전부 뒤져버린 적이 있다. cssnano가 우리 커스텀 keyframes를 "중복이네?" 하면서 알아서 삭제해버렸다. 금요일 6시에 배포하고 집에 갔는데 토요일 오전에 팀장한테 "애니메이션 다 망가졌는데 뭐 했냐?" 전화 받고 개빡침.
// 이 설정 때문에 주말에 회사 나가서 롤백함
cssnano({
preset: ['default', {
discardDuplicates: true // 이 새끼가 문제였음
}]
})
결국 discardDuplicates: false
로 해결했는데, 그 2시간 동안 고객센터에서 "웹사이트 이상하다" 전화 폭탄 맞았다. 그날부터 프로덕션 배포는 목요일까지만 하기로 팀 룰 정했음.
DevOps 통합 팁
Jenkins Pipeline
stage('CSS Build') {
steps {
sh 'npm ci'
sh 'npx postcss src/**/*.css --dir dist/'
}
}
GitHub Actions
- name: Build CSS
run: |
npm ci
npx postcss src/**/*.css --dir dist/
- name: Cache PostCSS
uses: actions/cache@v3
with:
path: node_modules/.cache/postcss
key: postcss-${{ hashFiles('postcss.config.js') }}
내가 실제로 쓰는 플러그인들
신규 프로젝트 (진짜 필수만)
- autoprefixer - 이거 없으면 IE 때문에 죽음
- cssnano - 번들 크기 때문에 필수
- postcss-preset-env - 미래 CSS 쓰고 싶으면
- postcss-import - 파일 분리 안 하면 코드 지옥
Sass에서 넘어갈 때
- postcss-simple-vars - $variable 그리운 사람들용
- postcss-nesting - 중첩 없으면 못 산다
- postcss-mixins - @mixin 중독자용
- postcss-sass - 점진적 이전 할 때
결론: PostCSS 쓸 만하다. Sass보다 빠르고, plugin도 많다. 단점은 처음에 뭘 깔아야 할지 몰라서 삽질하는 것. 그래도 한 번 설정하면 계속 우려먹을 수 있어서 투자 가치는 있다.
다만 팀에 Sass 고수가 있고, 기존 코드도 Sass로 멀쩡하게 돌아간다면 굳이 건드릴 필요 없다. "돌아가는 코드 건드리면 죽는다"는 개발자의 철칙을 잊지 말자.