Next.js: μ™ΈλΆ€ CSS 및 scss 파일 μž‘μ—…

에 λ§Œλ“  2017λ…„ 10μ›” 19일  Β·  38μ½”λ©˜νŠΈ  Β·  좜처: vercel/next.js

μ œκ°€ μ•Œκ³  μžˆλŠ” 쀑볡 λ¬Έμ œμ§€λ§Œ μ˜λ„μ μœΌλ‘œ 이 문제λ₯Ό μ—΄μ—ˆμŠ΅λ‹ˆλ‹€. λ‚΄κ°€ next.js μƒμš©κ΅¬(redux, redux-saga, ... 포함)λ₯Ό μ„€μ •ν•˜λŠ” 데 3일이 걸리고 μ™ΈλΆ€ CSS 및 scss νŒŒμΌμ„ λ‘œλ“œν•˜λŠ” 섀정에 κ°‡νžŒ 지 이틀이 λ˜μ—ˆμŠ΅λ‹ˆλ‹€. with-global-stylesheet 및 with-scoped-stylesheet-and-postcss 예제λ₯Ό ν™•μΈν–ˆμ§€λ§Œ 각각 이전 λ¬Έμ œμ—μ„œ μ–ΈκΈ‰ν•œ μ£Όμš” λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. ν•΄ν‚ΉμœΌλ‘œ 이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 곡개 및 폐쇄 문제λ₯Ό λ„ˆλ¬΄ 많이 λ΄€μŠ΅λ‹ˆλ‹€... μ΅œμƒμ˜ μ†”λ£¨μ…˜μ„ μ°ΎκΈ° μœ„ν•΄ 문제λ₯Ό λ°©μΉ˜ν•˜λŠ” λŒ€μ‹  더 λ‚˜μ€ μ†”λ£¨μ…˜μ„ 찾을 λ•ŒκΉŒμ§€ ν˜„μž¬ μ‚¬μš© κ°€λŠ₯ν•œ μ†”λ£¨μ…˜μœΌλ‘œ ν•΄κ²°ν•˜λŠ” 것이 쒋은 생각이라고 μƒκ°ν•©λ‹ˆλ‹€. λ§Žμ€ μ‚¬λžŒλ“€μ΄ μ§€κΈˆ 이 문제λ₯Ό κ²ͺκ³  있고 μ§€κΈˆ ν•΄κ²°λ˜κΈ°λ₯Ό μ›ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€!

κ°€μž₯ μœ μš©ν•œ λŒ“κΈ€

λ‚˜λŠ” λ˜ν•œ 개인적으둜 μ‚¬λžŒλ“€μ΄ 이 λ¬Έμ œμ— λŒ€ν•΄ 가지고 μžˆλŠ” μ–΄μ‘°λ₯Ό μ’‹μ•„ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 점을 μ§€μ ν•˜κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.
λ‚˜λŠ” 당신이 CSSλ₯Ό κ°€μ Έμ˜€κ³  μ‹Άμ–΄ν•œλ‹€λŠ” 것을 μ™„μ „νžˆ μ΄ν•΄ν•©λ‹ˆλ‹€. 그리고 μš°λ¦¬λŠ” 이 μš”μ²­μ„ 잘 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ μ§€λ‚œ ν•œ μ£Ό λ™μ•ˆ μ΅œμƒμ˜ μ†”λ£¨μ…˜μ„ μ°ΎκΈ° μœ„ν•΄ λ…Έλ ₯ν–ˆμŠ΅λ‹ˆλ‹€ πŸ‘
이것에 λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ€ 곧. κ·Έλ•ŒκΉŒμ§€ μΉœμ ˆν•˜κ³  ν–‰λ³΅ν•œ λͺ…μ ˆ λ³΄λ‚΄μ„Έμš” πŸŽ…πŸ˜„

λͺ¨λ“  38 λŒ“κΈ€

λ‚˜λŠ” styled-jsx만이 κΉ¨λ—ν•œ(ν•« λ¦¬λ‘œλ”© 포함) 지원을 가지고 μžˆλ‹€λŠ” 것에 λ™μ˜ν•©λ‹ˆλ‹€. 이것이 μ œκ°€ λΉ λ₯Έ ν”„λ‘œν† νƒ€μ΄ν•‘μ΄ ν•„μš”ν•  λ•Œλ₯Ό μ œμ™Έν•˜κ³ λŠ” Next.jsλ₯Ό μ‚¬μš©ν•˜μ§€ λͺ»ν•˜κ²Œ ν•˜λŠ” μ΄μœ μž…λ‹ˆλ‹€.

CSS λͺ¨λ“ˆμ˜ CSS λ²”μœ„ 지정 λ¬Έμ œμ— λŒ€ν•œ μ†”λ£¨μ…˜μ€ 훨씬 더 κΉ”λ”ν•œ 것이며 CSS λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜λ©΄ μ—¬μ „νžˆ 클래슀λ₯Ό μžμ‹ ꡬ성 μš”μ†Œμ— 전달할 수 μžˆμŠ΅λ‹ˆλ‹€( babel-plugin-inline-react-svg κ°€μ Έμ˜¨ SVG에 λΉ„μ „μ—­ 클래슀λ₯Ό λ„£μ–΄λ³΄μ‹­μ‹œμ˜€. styled-jsx μ‚¬μš©).

그리고 μ €λŠ” ν‘œμ€€ν™”λœ .css νŒŒμΌμ„ μ‚¬μš©ν•˜μ—¬ κ°€λŠ₯ν•œ ν•œ ν”„λ ˆμž„μ›Œν¬ 락인을 λ°©μ§€ν•˜κ³  캐싱을 μœ„ν•΄ ν”„λ‘œλ•μ…˜μ—μ„œ μ™ΈλΆ€ CSS νŒŒμΌμ„ μ‚¬μš©ν•˜λŠ” 것을 μ„ ν˜Έν•©λ‹ˆλ‹€. μ—¬μ „νžˆ IE8을 지원해야 함).

μ—„μ²­λ‚œ +1
μ™ΈλΆ€ css/scss와 같은 λ‹¨μˆœν•œ 것을 next.js둜 λ‹¬μ„±ν•˜λŠ” 것이 거의 λΆˆκ°€λŠ₯ν•˜μ—¬ λ‚΄ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ 90%μ—μ„œ μ“Έλͺ¨μ—†κ²Œ λœλ‹€λŠ” 것은 큰 μ’Œμ ˆμž…λ‹ˆλ‹€.

μ €λŠ” λΆ€νŠΈμŠ€νŠΈλž©μœΌλ‘œ μž‘μ—…ν•˜κ³  있으며 μ™ΈλΆ€ λ²”μœ„ CSSλ₯Ό μΆ”κ°€ν•˜μ—¬ ν•˜λ‚˜μ˜ κΈ€λ‘œλ²Œ λΆ€νŠΈμŠ€νŠΈλž© CSS κ°€μ Έμ˜€κΈ°κ°€ μžˆλŠ” ꡬ성이 ν•„μš”ν•©λ‹ˆλ‹€.

μŠ€νƒ€μΌμ΄ μ§€μ •λœ jsx 1( 컴파일 처리λ₯Ό μœ„ν•œ webpack )을 μ‚¬μš©ν•˜μ—¬ μ™ΈλΆ€ μŠ€νƒ€μΌλŸ¬μŠ€λ₯Ό μž‘λ™ν•˜λŠ” 데 μ„±κ³΅ν–ˆμ§€λ§Œ λ³„λ„μ˜ CSS νŒŒμΌμ„ μ²˜λ¦¬ν•˜λŠ” λ³€κ²½ 사항이 λ„μž…λœ μ΄ν›„λ‘œ μŠ€νƒ€μΌμ΄ μ§€μ •λœ jsx 2μ—μ„œ 이λ₯Ό νŒŒμ•…ν•˜λŠ” 데 어렀움을 κ²ͺμ—ˆμŠ΅λ‹ˆλ‹€.
ν˜„μž¬ μ ‘κ·Ό 방식:

import ComponentStyles from './footer.styl';
...
      <style jsx>
        {ComponentStyles}
      </style>

https://github.com/zeit/next.js/tree/master/examples/with-styled-jsx-scss κ°€ μ™ΈλΆ€ scss 파일둜 μž‘μ—…ν•˜λŠ” 것을 λ³΄λŠ” 것이 쒋을 κ²ƒμž…λ‹ˆλ‹€.

ν™˜κ²½μ„ ꡬ좕할 λ•Œλ„ 같은 μ‹œλ ¨μ„ κ²ͺμ—ˆμŠ΅λ‹ˆλ‹€.
κ²°κ΅­ μš°λ¦¬λŠ” Lost-Gridκ°€ μžˆλŠ” scss+post CSSκ°€ μžˆλŠ” μ „μ—­ μŠ€νƒ€μΌμ‹œνŠΈλ‘œ ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.
ν•« λ¦¬λ‘œλ”©μ΄ μž‘λ™ν•˜λ―€λ‘œ 이상적인 μ†”λ£¨μ…˜μ€ μ•„λ‹ˆμ§€λ§Œ(전체 μŠ€νƒ€μΌμ‹œνŠΈκ°€ ν•œ λ²ˆμ— λͺ¨λ‘ λ‘œλ“œλ˜κΈ° λ•Œλ¬Έμ—) μ μ ˆν•œ μ ˆμΆ©μ•ˆμž…λ‹ˆλ‹€.

쒅속성

"autoprefixer": "^7.1.6",
"babel-plugin-module-resolver": "^2.7.1",
"babel-plugin-wrap-in-js": "^1.1.1",
"node-sass": "^4.5.3",
"sass-loader": "^6.0.6",
"pixrem": "^4.0.1",
"postcss-easy-import": "^3.0.0",
"postcss-loader": "^2.0.8"

package.json

  ...
  "postcss": {
    "plugins": {
      "lost": {},
      "postcss-easy-import": {
        "prefix": "_"
      },
      "autoprefixer": {},
      "pixrem": {}
    }
  }
  ...

next.config.js

webpack: (config, { dev }) => {
    config.module.rules.push(
      {
        test: /\.(css|scss)/,
        loader: 'emit-file-loader',
        options: {
          name: 'dist/[path][name].[ext]'
        }
      }
    ,
      {
        test: /\.css$/,
        use: ['babel-loader', 'raw-loader', 'postcss-loader']
      }
    ,
      {
        test: /\.s(a|c)ss$/,
        use: ['babel-loader', 'raw-loader', 'postcss-loader',
          { loader: 'sass-loader',
            options: {
              includePaths: ['styles', 'node_modules']
                .map((d) => path.join(__dirname, d))
                .map((g) => glob.sync(g))
                .reduce((a, c) => a.concat(c), [])
            }
          }
        ]
      }
    )
    return config
  }

pages/_document.js

...

import stylesheet from 'styles/main.scss'

   ...
        <Head>
          <style dangerouslySetInnerHTML={{ __html: stylesheet }} />
        </Head>
   ...

그런 λ‹€μŒ /styles/main.scssμ—μ„œ μ‹œμž‘ν•˜λŠ” μŠ€νƒ€μΌμ„ 관리할 수 μžˆμŠ΅λ‹ˆλ‹€.
도움이 되기λ₯Ό λ°”λžλ‹ˆλ‹€.

이 두 가지 μŠ€νƒ€μΌ 예제( with-global-stylesheet 및 with-scoped-stylesheets-and-postcss ) 각각에 λŒ€ν•œ λ‚΄ λ¬Έμ œλŠ” μŠ€λƒ…μƒ·μ˜ CSSλ₯Ό μ‚¬μš©ν•˜μ—¬ Jest 및 Snapshot ν…ŒμŠ€νŠΈμ™€ ν†΅ν•©ν•˜κΈ°κ°€ κ°„λ‹¨ν•˜μ§€

이 SO 닡변에 μ„€λͺ…λœ λŒ€λ‘œ babel-jest μ „μ²˜λ¦¬κΈ° νŒŒμΌμ„ μ‹€ν–‰ν•˜λŠ” 것은 λ‚˜μœ ν•΄ν‚Ήμ²˜λŸΌ λ³΄μž…λ‹ˆλ‹€.

Webpack을 μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” with-global-stylesheet 둜 μ™ΈλΆ€ CSSλ₯Ό μ–»λŠ” 것 κ°™μ§€λ§Œ Jestλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ Webpack에 μ˜μ‘΄ν•  수 μ—†κ³  Babel만 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이 곡간에 아이디어가 μžˆλŠ” μ‚¬λžŒμ΄ μžˆμŠ΅λ‹ˆκΉŒ?

λΉ„μŠ·ν•œ λ¬Έμ œμ— 직면해 μžˆμŠ΅λ‹ˆλ‹€. μ €λŠ” nextjsκ°€ 처음이고 "with-external-scoped-css" μ˜ˆμ œκ°€ μ œλŒ€λ‘œ μž‘λ™ν•˜λ„λ‘ λ§Œλ“€ 수 μ—†μŠ΅λ‹ˆλ‹€. λ•Œλ‘œλŠ” λ‚΄ CSSκ°€λ‘œλ“œλ˜κ³  λ•Œλ‘œλŠ”λ‘œλ“œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ§μ”€ν•˜μ‹  것과 같은 λ¬Έμ œμΈμ§€ λͺ¨λ₯΄κ² μŠ΅λ‹ˆλ‹€.

https://github.com/zeit/next.js/issues/3276

이 λ‘œλ” https://github.com/coox/styled-jsx-css-loader의 μ™ΈλΆ€ μŠ€νƒ€μΌ 문제λ₯Ό ν•΄κ²°ν–ˆμŠ΅λ‹ˆλ‹€.

@ilionic κ·€ν•˜μ˜ μ†”λ£¨μ…˜μ„ ν™•μΈν–ˆμŠ΅λ‹ˆλ‹€. λŒ€λ‹¨ν•΄! κ³ λ§™μŠ΅λ‹ˆλ‹€ :)

@arefaslani 이 λ¬Έμ œκ°€ μ’…λ£Œλ˜μ—ˆλ‹€κ³  μƒκ°ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

HTTP v1μ—μ„œ μˆ˜λ§Žμ€ CSSλ₯Ό λ‘œλ“œν•˜λŠ” 것은 μ—¬μ „νžˆ β€‹β€‹λ”μ°ν•œ μ„±λŠ₯ μ„ΈκΈˆμ΄λ©°, 처음 κ·Έλ¦¬λŠ” μ‹œκ°„μ„ 극적으둜 μ¦κ°€μ‹œν‚΅λ‹ˆλ‹€.

μ μ ˆν•œ μ™ΈλΆ€ μŠ€νƒ€μΌ 지원을 톡해 CSSλ₯Ό κ°€μ Έμ˜¬ 수 있으며 결과적으둜 인라인이 μ•„λ‹Œ