Postcss는 css에서 바벨과 같은 역할을 수행하는 도구라고 할 수 있습니다.
웹팩이나 걸프와 같은 빌드 도구와 사용할 수 있는 각종 플러그인을 제공하죠.
그 중 대표적인 것들로는
등이 있습니다.
create-react-app
에서도 적극적으로 Postcss
를 활용하고 있는데요,
웹팩 설정 부분을 찬찬히 살펴보면 Postcss
의 여러 플러그인(특히 autoprefixer)을 활용하고 있음을 알 수 있습니다.
그래서 flexbox
나 grid
같은 속성에 일일히 -moz
와 같은 접두사를 붙여주지 않아도 됐던 것이죠.
제가 오늘 소개할 플러그인은 postcss-preset-env
입니다.
복잡한 바벨 설정이 preset-env
하나로 거의 대부분 정리되듯이 Postcss
를 사용하는 대부분의 use-case가 이 플러그인 하나로 해결됩니다.
create-react-app v2를 사용하시는 분들은 이미
postcss-preset-env
가 설정되어 있습니다! 😃
아래의 설정은 react-scripts version 1.1.5 기준입니다.
먼저 eject
명령을 수행합니다.
yarn eject // 질문에 Y를 선택하시면 최종적으로 eject됩니다.
이제 폴더를 확인하시면 config
폴더가 생성된 것을 확인할 수 있을텐데요,
먼저 개발 설정부터 건드려봅시다.
webpack.config.dev
에 들어가서
{
loader: require.resolve('postcss-loader'),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
위와 같은 부분을 찾아줍니다.
그리고 아래의 플러그인 설정을
plugins: () => [
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
stage: 0, // 모든 기능 사용
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
와 같이 바꿔준다음,
yarn add postcss-preset-env -D
로 설치해줍니다.
설정 끝이냐구요? 네! 끝입니다. 정말 간단하죠.
이제 App.css
를 조금 바꿔봅시다.
.App {
text-align: center;
& .logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
& .header {
background-color: var(--black);
height: 150px;
padding: 20px;
color: var(--white);
}
& .title {
font-size: 1.5em;
}
& .intro {
font-size: large;
}
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
그리고 App.js
를 조금 수정해줍니다.
// App-header > App header 와 같이 클래스명을 변경해줍니다.
<header className="App header">
<img src={logo} className="App logo" alt="logo" />
<h1 className="App title">Welcome to React</h1>
</header>
scss
를 사용하시던 분이라면 바로 눈치챌 수 있는 부분도 있을텐데요,
nesting
의 spec이 scss
와는 조금 다릅니다.
scss
에서는
.App {
&-logo {
/* rules */
}
}
와 같이 표현할 경우 .App-logo
클래스를 타겟으로 규칙을 적용합니다.
이 같은 경우를 편의상 scss
의 name prefix라고 표현하겠습니다.
name prefix의 개념은 scss
에서는 유효하지만 안타깝게도 css의 nesting module의 최신 spec (Level 3) 에서는 그렇지 않습니다.
오로지 두 개의 다른 대상을 선택자 혹은 선택자 없이 연결할 때만 nesting을 활용할 수 있습니다.
다음은 표준 스펙 문서의 Motivation 부분에 수록된 코드입니다.
table.colortable td {
text-align:center;
}
table.colortable td.c {
text-transform:uppercase;
}
table.colortable td:first-child, table.colortable td:first-child+td {
border:1px solid black;
}
table.colortable th {
text-align:center;
background:black;
color:white;
}
와 같이 중복되는 부분을
table.colortable {
& td {
text-align:center;
&.c { text-transform:uppercase }
&:first-child, &:first-child + td { border:1px solid black }
}
& th {
text-align:center;
background:black;
color:white;
}
}
이렇게 줄였습니다.
하지만 앞서 말했듯 name prefix 형태의 활용은 불가능합니다.
이 부분은 조금 염두에 둬야할 부분이 되겠네요.
각설하고, 개발 서버를 띄우면 전과 같은 모습의 화면을 볼 수 있습니다.
Tada! 완벽하군요. 전처리기의 힘을 빌려야만 가능했던 rule nesting이 css 확장자에서도 가능해졌습니다.
설정이 간편한건 덤이구요.
create-react-app
을 사용하고 있는 분들이라면 eslint의 경고창이 굉장히 익숙하게 느껴지실 겁니다.
css도 lint를 통해 코드 품질을 높일 수 있습니다.
다시 webpack 설정을 열어볼까요?
마지막으로 설정했던 부분에 아래와 같이 stylelint를 추가해줍니다.
plugins: () => [
require('stylelint')({
configFile: `${process.cwd()}/.stylelintrc.json`,
}),
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
그리고 src 디렉토리 바깥에 .stylelintrc.json
파일을 생성해주시고,
아래의 내용을 추가해줍니다.
{
"extends": "stylelint-config-standard"
}
마지막으로 필요한 플러그인들을 설치해줍니다.
yarn add stylelint stylelint-config-standard -D
끝!
에러가 정상적으로 표시되나 한 번 관찰해볼까요?
세미콜론을 빠트렸다고 알려주는군요.
좋습니다.
vscode 사용자들을 위한 팁
마켓에서 postcss를 검색하면 다운로드 수가 가장 많은 플러그인은postcss syntax
지만syntax highlighting
이 잘 되지 않습니다. 대신 language postcss를 추천합니다.
모든 기능은 이 곳에서 확인하실 수 있습니다.
유용한 기능들이 정말 많았지만 제가 미리 써보고 가장 괜찮았던 기능 몇 가지만 먼저 소개하겠습니다.
첫 번째는 css 변수입니다.
오랫동안 scss
와 같은 전처리기를 사용해야하는 이유였던 변수를 css에서 바로 사용해볼 수 있습니다.
:root {
--h1-font-size: 2.25rem;
}
h1 {
font-size: var(--h1-font-size);
}
/* 블락 레벨 사용 */
.box {
--padding: 24px;
padding: var(--padding);
}
위의 예시처럼 :root
가상 선택자를 사용하면 전역변수로 사용할 수 있습니다.
문법은 --변수명이며, -로 단어 사이를 구분해줍니다.
사용할 때는 var(변수명)과 같이 할 수 있습니다.
css 변수는 padding이나 margin을 계산할 때 굉장히 유용하게 활용할 수 있습니다.
:root {
--base-padding: 8px;
}
.box {
/* 80px의 padding이 추가되었습니다! */
padding: calc(var(--base-padding) * 10);
}
두 번째는 custom media query와 custom selector입니다.
사용법은 아래와 같습니다.
@custom-media --narrow-window (30em <= width < 50em);
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
:--heading {
text-align: center;
/* nesting과 결합하면 더욱 표현력 좋게 나타낼 수 있습니다. */
@media (--narow-window) {
text-align: right;
}
}
@custom-media --narrow-window (30em <= width < 50em);
이 부분을 눈여겨 보신 분들도 있을 텐데요, media query ranges spec에 따라 일반적으로 범위를 표현하듯이 범위를 나타낼 수 있게 되었습니다.
@custom-media --narrow-window (30em <= width) and (width < 50em);
대신 말이죠.
세 번째는 두 개의 새로운 선택자입니다.
:matches : 1개 이상의 클래스를 매치할 수 있습니다.
:not : 해당되는 클래스를 제외하고 매치합니다.
이런 선택자가 유용한 경우는 보통 list에서 마지막 아이템이나 첫 아이템을 제외하고 스타일링을 할 때입니다.
ul {
& li:not(:last-child) {
padding-bottom: 16px;
}
}
이제 더 이상 마지막 아이템에 들어간 padding-bottom
을 없애기 위해 추가적인 룰을 더하지 않아도 됩니다.
만세!
네 번째는 logical property/values 입니다.
이 spec은 React Native
를 다뤄봤다면 익숙할 수평, 수직 방향의 padding 혹은 margin을 더하기 위한 속성입니다.
spec의 기본적인 내용은 글을 쓰는 순서에 따라 서로 다른 방향으로 요소를 정렬하기 때문에 right나 left같은 단어 대신 start나 end를 사용한다는 것인데요,
아래 그림을 보시죠.
<출처> W3C css spec
위처럼 글을 쓰는 방향에 따라 border나 margin, padding 등이 다르게 적용된다는 것이죠.
이 속성은 마치 flexbox처럼 x축과 y축에 따라 영향을 주는 방향이 결정되는데요,
글쓰는 방향 (dir)을 바꾸지 않았다면 x축은 inline, y축은 block으로 생각할 수 있습니다.
그러므로
.box {
border-block: 1px solid black;
padding-block: 24px;
}
와 같이 설정하면,
.box {
border-top: 1px solid black;
border-bottom: 1px solid black;
padding-top: 24px;
padding-bottom: 24px;
}
와 같이 변환됩니다.
수평 padding이나 margin을 줘야할 때가 굉장히 잦은데 저런 식으로 간편하게 해결할 수 있게 되었습니다.
이상으로 기능 소개를 마치겠습니다.
css는 css3를 통해 굉장한 변화를 겪었습니다.
정말 많은 property가 생기고 자연스러운 애니메이션, 유용한 레이아웃 도구 등이 생겨났죠.
아마 앞으로도 더 많은 변화를 겪을 것이고, 웹 개발자로서 항상 새로운 spec에 관심을 두고 지켜봐야한다고 생각합니다.
css를 잘 다룬다는 것은 사실 얼마나 spec에 대해 잘 알고 있으냐와 같은 말입니다.
모두 즐거운 css 합시다!
참고
- spec 을 알아야하는 이유: 왜 margin: auto는 수직방향으로 정렬하지 않나요?
- w3c css spec 둘러보기
감사합니다!
좋은 글 감사합니다.
"scss는 name prefix의 개념으로 활용할 수도 있었지만 css의 nesting module의 최신 spec (Level 3) 상에는 그런 개념이 없습니다."
이 부분이 잘 이해가 안갔는데, name prefix 개념이 정확히 어떤건가요?