Postcss?

Postcss는 css에서 바벨과 같은 역할을 수행하는 도구라고 할 수 있습니다.

웹팩이나 걸프와 같은 빌드 도구와 사용할 수 있는 각종 플러그인을 제공하죠.

그 중 대표적인 것들로는

  • autoprefixer: vender-prefix를 추가해줌
  • stylelint: eslint처럼 문법 검사를 수행
  • postcss-module: css module 지원

등이 있습니다.

create-react-app에서도 적극적으로 Postcss를 활용하고 있는데요,
웹팩 설정 부분을 찬찬히 살펴보면 Postcss의 여러 플러그인(특히 autoprefixer)을 활용하고 있음을 알 수 있습니다.

그래서 flexboxgrid 같은 속성에 일일히 -moz와 같은 접두사를 붙여주지 않아도 됐던 것이죠.

Postcss-preset-env

제가 오늘 소개할 플러그인은 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 형태의 활용은 불가능합니다.

이 부분은 조금 염두에 둬야할 부분이 되겠네요.

각설하고, 개발 서버를 띄우면 전과 같은 모습의 화면을 볼 수 있습니다.

스크린샷 2018-10-22 오후 4.37.28.png

Tada! 완벽하군요. 전처리기의 힘을 빌려야만 가능했던 rule nesting이 css 확장자에서도 가능해졌습니다.

설정이 간편한건 덤이구요.

Stylelint?

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

끝!

에러가 정상적으로 표시되나 한 번 관찰해볼까요?

스크린샷 2018-10-22 오후 6.09.12.png

세미콜론을 빠트렸다고 알려주는군요.

좋습니다.

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를 사용한다는 것인데요,

아래 그림을 보시죠.

스크린샷 2018-10-22 오후 5.10.03.png

<출처> 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의 미래?

css는 css3를 통해 굉장한 변화를 겪었습니다.

정말 많은 property가 생기고 자연스러운 애니메이션, 유용한 레이아웃 도구 등이 생겨났죠.

아마 앞으로도 더 많은 변화를 겪을 것이고, 웹 개발자로서 항상 새로운 spec에 관심을 두고 지켜봐야한다고 생각합니다.

css를 잘 다룬다는 것은 사실 얼마나 spec에 대해 잘 알고 있으냐와 같은 말입니다.

모두 즐거운 css 합시다!

참고