Next.js: _app ์™ธ๋ถ€์˜ node_modules์—์„œ ์˜ฌ๋ฐ”๋ฅธ ๋ฒ”์œ„์˜ CSS ๊ฐ€์ ธ์˜ค๊ธฐ

์— ๋งŒ๋“  2020๋…„ 04์›” 21์ผ  ยท  49์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: vercel/next.js

๋ฒ„๊ทธ ์‹ ๊ณ 

๋ฒ„๊ทธ ์„ค๋ช…

๋‹ค์Œ์€ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ ํŒจํ‚ค์ง€์—์„œ ์Šคํƒ€์ผ์‹œํŠธ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์€ ํŽ˜์ด์ง€์—์„œ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

Global CSS cannot be imported from files other than your Custom <App>. Please move all global CSS imports to pages/_app.tsx.
Read more: https://err.sh/next.js/css-global

์ด๊ฒƒ์ด ์–ด๋””์—์„œ ์œ ๋ž˜ํ–ˆ๋Š”์ง€ ์ดํ•ดํ•˜๋Š” ๋™์•ˆ ์ฝ”๋“œ ๋ถ„ํ• ์„ ๋ถˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ๊ตฌ์„ฑ ์š”์†Œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด CSS๋„ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์„ ํƒ๊ธฐ์˜ ๋ฒ”์œ„๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ง€์ •ํ•˜์ง€ ์•Š๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ด์„œ ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฎ์–ด์“ฐ๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜จ CSS๋Š” ๋ณธ์งˆ์ ์œผ๋กœ "์ „์—ญ"์ด ์•„๋‹™๋‹ˆ๋‹ค.

์žฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด

  1. import "my-library/index.css"
  2. yarn dev
  3. ๋‚˜๋Š” ์œ„์—์„œ ์˜ค๋ฅ˜๋ฅผ ์–ป๋Š”๋‹ค

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™

ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ€๋Šฅํ•œ ์†”๋ฃจ์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • next.config.js ๊ธ€๋กœ๋ฒŒ ํ”Œ๋ž˜๊ทธ
  • ๊ฐ€์ ธ์˜ค๊ธฐ ์‹œ ์ฃผ์„ ์ฃผ์„
  • ์™„์ „ํžˆ ์ฐจ๋‹จํ•˜๋Š” ๋Œ€์‹  ๊ฒฝ๊ณ 
  • CSS์— ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋œ ์„ ํƒ์ž๋งŒ ํฌํ•จ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ

์ถ”๊ฐ€ ์ปจํ…์ŠคํŠธ

์ด์— ๋Œ€ํ•œ ์ด์ „ ๋…ผ์˜๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ธ”๋กœ๊ทธ์—์„œ

์Šคํƒ€์ผ์‹œํŠธ๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ์ „์—ญ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— Custom์œผ๋กœ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.์š”์†Œ. ์ด๋Š” ์ „์—ญ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ํด๋ž˜์Šค ์ด๋ฆ„ ๋ฐ ์ˆœ์„œ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด ๋ง์— ๋™์˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ถ”๋ก ์€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ CSS ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ๊ฐ€์ ธ์˜ฌ CSS ํŒŒ์ผ๋กœ ํŒจํ‚ค์ง•ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์™„๋ฒฝํ•˜๊ฒŒ ์œ ํšจํ•˜๊ณ  ์ผ๋ฐ˜์ ์ธ ๊ด€ํ–‰์ด๋ฉฐ ๋ถ€์ž‘์šฉ์ด ์—†์Šต๋‹ˆ๋‹ค .

#10059

_app ์˜ ์ „์—ญ ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ์„ ํƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ๋Š” ์ข…๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์ด ๋Œ“๊ธ€ ์€ ์ •ํ™•ํ•œ ๋ฌธ์ œ๋ฅผ ์„ค๋ช…ํ•˜์ง€๋งŒ ๋ฌธ์ œ๊ฐ€ ์ข…๋ฃŒ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์‘๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค. ๋Œ“๊ธ€์€ ๊ธ์ •์ ์ธ ๋ฐ˜์‘์„ ๋งŽ์ด ๋ฐ›์•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด ๋ฌธ์ œ๋ฅผ ๊ฒช๊ณ  ์žˆ๋Š” ์‚ฌ๋žŒ์€ ๋‚˜๋ฟ๋งŒ์ด ์•„๋‹ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

#10975

๊ด€๋ จ ์—†๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

#9830

๊ด€๋ จ์ด ์žˆ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€

๋‚˜๋Š” ๋งŽ์€ ์‚ฌ์šฉ์ž ์ •์˜ ์•„ํŠธ์›๊ณผ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์ผ๋Ÿฌ์ŠคํŠธ๋ ˆ์ด์…˜์œผ๋กœ ๊ธด ๊ธฐ์‚ฌ๋ฅผ ์”๋‹ˆ๋‹ค. ๊ธฐ์‚ฌ๋Š” ์ƒ๋‹นํ•œ ์–‘์˜ CSS๋กœ SVG๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๋ฐ˜์‘ ๊ตฌ์„ฑ ์š”์†Œ์™€ ํ•จ๊ป˜ ๊ฐœ์ธ npm ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒจํ‚ค์ง€๋Š” CSS ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๊ณ  index.js ๋ฐ index.css ๋‚ด๋ณด๋ƒ…๋‹ˆ๋‹ค. ๋ชจ๋“  CSS ํŒŒ์ผ์„ _app ํ•˜๋ฉด ์‚ฌ๋žŒ๋“ค์ด ํ™ˆํŽ˜์ด์ง€, ์—ฐ๋ฝ์ฒ˜ ์–‘์‹ ๋˜๋Š” ๊ธฐํƒ€ ๋ฌธ์„œ์— ์žˆ๋Š” ๊ฒฝ์šฐ์—๋„ 100% ์‚ฌ์šฉ๋˜์ง€ ์•Š๋”๋ผ๋„ ๋ชจ๋“  CSS๊ฐ€ ๋กœ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฑฐ์˜ ๋ชจ๋“  ํŽ˜์ด์ง€๊ฐ€ _app ์˜ CSS ๊ฐ€์ ธ์˜ค๊ธฐ์— ํ•ด๋‹นํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŒŒ์ผ ์‹œ์Šคํ…œ์ด ํŽ˜์ด์ง€๋ฅผ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ๋„ ๋ฐ˜๋Œ€์ž…๋‹ˆ๋‹ค.

story 8 feature request

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋‹ค์Œ ์ฃผ(์นด๋‚˜๋ฆฌ์•„์—์„œ) ๋‚ด์— node_modules ์—์„œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ ํŒŒ์ผ๋กœ CSS๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ํ…Œ์ŠคํŠธ ์ค€๋น„๊ฐ€ ๋˜๋ฉด ์—ฌ๊ธฐ์— ๊ฒŒ์‹œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  49 ๋Œ“๊ธ€

์ž์ฒด ํด๋ž˜์Šค ์ด๋ฆ„์˜ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•˜๋Š” Linaria๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋ฌธ์ œ์— ์ง๋ฉดํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑํ•˜๋Š” CSS ํŒŒ์ผ์ด .module.css ๋๋‚˜์ง€๋Š” ์•Š์ง€๋งŒ "๋ชจ๋“ˆ"์ž…๋‹ˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์™œ nextjs๋กœ ๋‹ค์‹œ ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๊นŒ?

๋˜ํ•œ node_modules ์™ธ๋ถ€์—์„œ GlobalCSS๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ CSS ๋ชจ๋“ˆ์„ ์ ์ง„์ ์œผ๋กœ ์ฑ„ํƒํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋„ค ์ด๊ฒƒ์€ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค! ๋งŽ์€ npm ํŒจํ‚ค์ง€๊ฐ€ nextjs์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์ง€๋งŒ CRA ๋˜๋Š” ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

@use ์ง€์› ๋ฐ sass ๋ชจ๋“ˆ๊ณผ ๊ฐ™์€ dart sass์˜ js ๊ตฌํ˜„๊ณผ ํ•จ๊ป˜ ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋ ค๋Š” ๋‹ค๋ฅธ ์‚ฌ๋žŒ์„ ์œ„ํ•ด, node-sass์— ์ข…์†์„ฑ์ด ์žˆ๋Š” ๋‹ค๋ฅธ ๋…ธ๋“œ ๋ชจ๋“ˆ์ด ์žˆ๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ ๋‹ค์Œ ์„ค์ • sass ๋Œ€์‹  node-sass๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ์—์„œ ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

// example next.config.js
module.exports = {
webpack(config, options) {
  config.module.rules.forEach(rule => {
          if (rule.oneOf) {
            const nestedScss = rule.oneOf.find((one) => {
              return one.test
                && 'some.scss'.match(one.test) 
                && one.issuer 
                && one.issuer.include 
                && one.issuer.include.includes('_app');
            });
            if (nestedScss) {
              const sassLoader = nestedScss.use.find(u => u.loader.includes('sass-loader'));
              // Set implementation to sass instead of node-sass here.
              sassLoader.options.implementation = require('sass');
            }
          }
        })
  }
}

๊ทธ๋Ÿฐ ๋‹ค์Œ _app.js ์—์„œ scss ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

@smurrayatwork ์ฝ”๋”ฉ์ด ์•„๋‹ˆ๋ผ ํ•ดํ‚น์ž…๋‹ˆ๋‹ค ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค

๋˜ํ•œ _app.js ์ „์šฉ์ด๋ผ๋Š” ์ œํ•œ๋„ ์•ฝ๊ฐ„ ๋ฒˆ๊ฑฐ๋กญ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ณณ์—์„œ CSS ์ฐธ์กฐ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ ค๋ฉด _app (๋‹ค๋ฅธ ๊ณณ์—์„œ๋Š” ์ฐธ์กฐ๋˜์ง€ ์•Š์Œ)์˜ ์ง์ ‘์ ์ธ ์ข…์†์„ฑ์—์„œ๋„ CSS๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?
์ฆ‰. _app (๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ ๊ณณ์€ ์•„๋‹˜)์—์„œ ์š”๊ตฌํ•˜๋Š” ๊ฒฝ์šฐ CSS์— ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋Š” ๊ฒฐ์ •์  ์ˆœ์„œ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

์ด์ƒ์ ์ด์ง€๋Š” ์•Š์ง€๋งŒ ๋‚ด๊ฐ€ ๊ฐ€์ง„ ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ๊ณต์œ  CSS๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ณต์œ  ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์˜ค๋Š” ์—ฌ๋Ÿฌ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ๊ณต์œ ํ•˜๋Š” ํ•˜๋‚˜์˜ ์ฝ”๋“œ๋ฒ ์ด์Šค๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•ด _app.js์—์„œ ๊ณต์œ  CSS ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ๋ณต์ œํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ๋‹ค๋ฅธ ๋ชจ๋“ˆ์—์„œ CSS๋ฅผ ์š”๊ตฌํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ‹์ง„ js ๋ฉ”ํƒ€ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋Œ€์‹  ๋ชจ๋“  ๊ณต์œ  CSS๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” "App Factory"๊ฐ€ ์žˆ๋Š” ํ˜„์žฌ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ _app์€ ํŒฉํ† ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ณต์œ  CSS ์œ„์— ์ž์ฒด CSS๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ์™€ ๊ด€๋ จ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์—ฌ https://github.com/vercel/next.js/discussions/13991 ์„ ์ถ”๊ฐ€ํ•˜๊ณ 

์ด๊ฒƒ์— +100. ๋…ธ๋“œ ๋ชจ๋“ˆ CSS ํŒŒ์ผ์„ ๋ณต์‚ฌํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์— ๋ถ™์—ฌ๋„ฃ๊ณ  .module.css๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ ๋˜ ๋‹ค๋ฅธ ์˜ˆ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

pdf-viewer-reactjs ํŒจํ‚ค์ง€์˜ ๊ฒฝ์šฐ ์ข…์†์„ฑ์—๋Š” _app.js ์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” CSS๋„ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์ „์ฒด ์•ฑ์— ๋Œ€ํ•œ CSS๋ฅผ ๋ถ€ํ’€๋ฆฌ๊ฒŒ ํ•˜๊ณ  ์ด ๋‹จ๊ณ„์—์„œ ์ถฉ๋Œ์— ๋Œ€ํ•ด ํ™•์‹ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

import 'react-quill/dist/quill.snow.css';
import 'react-image-crop/dist/ReactCrop.css';
๊ฐ€์ ธ์˜ค๊ธฐ '../../node_modules/material-design-icons/iconfont/material-icons.css';
๊ฐ€์ ธ์˜ค๊ธฐ '../../node_modules/bulma/css/bulma.css';
๊ฐ€์ ธ์˜ค๊ธฐ '../../node_modules/bulma-helpers/css/bulma-helpers.min.css';

๋˜ํ•œ ๋‹ค์Œ์€ ์ฝ˜์†”์— ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

๊ฒฝ๊ณ  - ./node_modules/material-design-icons/iconfont/material-icons.css
์ „์—ญ CSS๋Š” node_modules ๋‚ด์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋” ์ฝ์–ด๋ณด๊ธฐ: https://err.sh/next.js/css-npm
์œ„์น˜: node_modules/pdf-viewer-reactjs/dist/pdf-viewer-reactjs.js

./node_modules/bulma/css/bulma.css
์ „์—ญ CSS๋Š” node_modules ๋‚ด์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋” ์ฝ์–ด๋ณด๊ธฐ: https://err.sh/next.js/css-npm
์œ„์น˜: node_modules/pdf-viewer-reactjs/dist/pdf-viewer-reactjs.js

./node_modules/bulma-helpers/css/bulma-helpers.min.css
์ „์—ญ CSS๋Š” node_modules ๋‚ด์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋” ์ฝ์–ด๋ณด๊ธฐ: https://err.sh/next.js/css-npm
์œ„์น˜: node_modules/pdf-viewer-reactjs/dist/pdf-viewer-reactjs.js

./node_modules/material-design-icons/iconfont/material-icons.css
๋ชจ๋“ˆ ๋นŒ๋“œ ์‹คํŒจ: ์˜ค๋ฅ˜: ์ตœ์ข… ๋กœ๋”(./node_modules/next/dist/build/webpack/loaders/error-loader.js)๊ฐ€ ๋ฒ„ํผ ๋˜๋Š” ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

./node_modules/bulma/css/bulma.css
๋ชจ๋“ˆ ๋นŒ๋“œ ์‹คํŒจ: ์˜ค๋ฅ˜: ์ตœ์ข… ๋กœ๋”(./node_modules/next/dist/build/webpack/loaders/error-loader.js)๊ฐ€ ๋ฒ„ํผ ๋˜๋Š” ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

./node_modules/bulma-helpers/css/bulma-helpers.min.css
๋ชจ๋“ˆ ๋นŒ๋“œ ์‹คํŒจ: ์˜ค๋ฅ˜: ์ตœ์ข… ๋กœ๋”(./node_modules/next/dist/build/webpack/loaders/error-loader.js)๊ฐ€ ๋ฒ„ํผ ๋˜๋Š” ๋ฌธ์ž์—ด์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์•ˆ๋…• ! ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๊นŒ? ๊ทธ ๋•Œ๋ฌธ์— ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๋Š” ๋…ธ๋“œ ๋ชจ๋“ˆ์ด ๋„ˆ๋ฌด ๋งŽ์Šต๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ์—์„œ ์ „์—ญ ์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ next.config.js ๋ฅผ ํ†ตํ•ด ํ™œ์„ฑํ™”๋˜๊ฑฐ๋‚˜ NextJS์˜ ๋ชจ๋ฒ” ์‚ฌ๋ก€/์˜๊ฒฌ์„ ์œ„๋ฐ˜ํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ์šฐ๋ ค๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ „์—ญ ์Šคํƒ€์ผ์— ๋Œ€ํ•œ ๋ชป์ƒ๊ธด ์ฝ˜์†” ๊ฒฝ๊ณ ๊ฐ€ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ CRA > NextJS์—์„œ ๋ณ€ํ™˜ํ•˜๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์ „ํ™˜ํ•  ์ˆ˜ ์—†๊ณ  CSS ๋ชจ๋“ˆ๊ณผ ๊ฐ™์€ ๊ฒƒ์„ ์ ์ง„์ ์œผ๋กœ ์ฑ„ํƒํ•  ์ˆ˜ ์—†๋Š” ์šฐ๋ฆฌ๋ฅผ ์œ„ํ•œ ์ฐจ๋‹จ๊ธฐ์ž…๋‹ˆ๋‹ค.

์—ฌ์ „ํžˆ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ด ์ž์‹ ์˜ ํ•„์š”์— ๋”ฐ๋ผ ์‚ฌ์šฉ์ž ์ •์˜ CSS ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ์ด๊ฒƒ์€ ๋‚ด์žฅ CSS ์ง€์›์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์ง€๋งŒ ๋ชจ๋“  ๊ฒฝ์šฐ์— ์ข‹์€ ์†”๋ฃจ์…˜์€ ์•„๋‹ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค . ํŒจํ‚ค์ง€ ์ž‘์„ฑ์ž๊ฐ€ ์ •๋ฆฌํ•  ๋•Œ๊นŒ์ง€๋งŒ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

next.config.js

const withCSS = require('@zeit/next-css');
const withPlugins = require('next-compose-plugins');
...
module.exports = withPlugins([
...
withCSS,
]);

@abdelrahmantoptal 's SASS์—์„œ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

CSS์—์„œ ์ž‘๋™ํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ SASS ๊ฐ€์ ธ์˜ค๊ธฐ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

error - ./src/components/layouts/Footer.scss 1:0
Module parse failed: Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> <strong i="8">@import</strong> 'styles/vars';
| 
| footer {

๊ทธ๋ž˜์„œ withCSS ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์›นํŒฉ ์„ค์ •์— SASS ๋กœ๋”๋ฅผ ์ถ”๊ฐ€ํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

      config.module.rules.push({
        test: /\.s[ac]ss$/i,
        use: [
          // Creates `style` nodes from JS strings
          'style-loader',
          // Translates CSS into CommonJS
          'css-loader',
          // Compiles Sass to CSS
          'sass-loader'
        ]
      });

๊ทธ๋Ÿฌ๋‚˜ ๊ทธ ์›์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

error - ./src/components/App.scss
ReferenceError: self is not defined

๋‚˜๋Š” ๋˜ํ•œ @zeit/next-sass ๋Œ€์ฒดํ•˜๋ ค๊ณ  ์‹œ๋„ํ–ˆ์ง€๋งŒ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

error - ./src/components/App.scss
ReferenceError: self is not defined

SASS๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์กฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์ œ์•ˆ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ https://github.com/balena-io-modules/rendition์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„ ํ–ˆ์Šต๋‹ˆ๊นŒ?

https://github.com/balena-io-modules/rendition/issues/1164

์ €๋Š” ์ด์ œ Gatsby์™€ ๊ณง Next.js๋ฅผ ํ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ๊ณผ ๊ฐ™์ด ์ž‘์ง€๋งŒ ๋งค์šฐ ์ฐจ๋‹จ๋˜๊ณ  ๋…๋‹จ์ ์ธ ๊ธฐ๋Šฅ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ์˜ค๋ฅ˜๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ์ด์ œ CKEditor 5์šฉ CodeBlock ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ตฌ์„ฑ์„ ์ˆœํ™˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ํ•ญ์ƒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด @Timer ๋˜๋Š” ๋ฐ›๋Š” ๊ฒƒ์ด ๋งค์šฐ ๋„์›€์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Next.js์˜ ํฐ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ๊ณ„ํš์ด ์žˆ์Šต๋‹ˆ๊นŒ?

๋™์˜! ๋…ธ๋“œ ๋ชจ๋“ˆ์— CSS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋กœ์„œ ์ €๋Š” ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋…ธ๋“œ ๋ชจ๋“ˆ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ์–ดํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋Š” ๋…ธ๋“œ ๋ชจ๋“ˆ์— CSS๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์ด ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์†์ƒ์‹œํ‚ฌ ๊ฒƒ์ด๋ผ๊ณ  ๊ธฐ๋Œ€ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@OssiPesonen ์ด๊ฑฐ ๋ณธ ์  ์žˆ์–ด ? ์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์ด์ƒ์ ์ด์ง€๋Š” ์•Š์ง€๋งŒ ๊ทธ ๋™์•ˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

@OssiPesonen ์ด๊ฑฐ ๋ณธ ์  ์žˆ์–ด ? ์ด ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์€ ์ด์ƒ์ ์ด์ง€๋Š” ์•Š์ง€๋งŒ ๊ทธ ๋™์•ˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒŒ ์–ด๋–ป๊ฒŒ ๋„์›€์ด ๋˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ์–ด? ๋ฌธ์ œ๋Š” ๋…ธ๋“œ ๋ชจ๋“ˆ์—์„œ ์ผ๋ถ€ CSS ํŒŒ์ผ์„ ์ˆ˜๋™์œผ๋กœ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” CSS ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ž์ฒด์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” npm ํŒจํ‚ค์ง€์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ–‰์„ ํฌํ•จํ•˜๋Š” ํŒจํ‚ค์ง€:

import '../theme/stylesheet.css'

next.js๊ฐ€ ๋ณต์ˆ˜๋กœ ์ถฉ๋Œ์„ ์ผ์œผํ‚ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ถ„๋ช…ํžˆ ์œ ์ง€ ๋ณด์ˆ˜์˜ ์กฐ์–ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์œ ์ง€ ๊ด€๋ฆฌ์ž์—๊ฒŒ ์—ฐ๋ฝํ•˜์—ฌ ์ข…์†์„ฑ์˜ ์ปดํŒŒ์ผ๋œ ๋ฒ„์ „์„ ๊ฒŒ์‹œํ•˜๋„๋ก ์š”์ฒญํ•˜์‹ญ์‹œ์˜ค.

์‚ฌ๋žŒ๋“ค์€ ์–ด๋–ค ์ข…๋ฅ˜์˜ ํ™˜์ƒ์˜ ๋‚˜๋ผ์— ์‚ด๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์œ ์ง€ ๊ด€๋ฆฌ์ž์—๊ฒŒ ์—ฐ๋ฝํ•˜์—ฌ ๋งค์šฐ ๋น ๋ฅธ ์†๋„๋กœ ํŒจํ‚ค์ง€๋ฅผ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•˜๋„๋ก ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๊นŒ? ์ด๊ฒƒ์€ ๋ช‡ ์ฃผ ๋™์•ˆ ๋ˆ„๊ตฌ์—๊ฒŒ๋‚˜ ๋ฐฉํ•ด๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค! ์ด ํ‹ฐ์ผ“์€ 4๊ฐœ์›” ๋™์•ˆ ์˜คํ”ˆ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋น ๋ฅด๊ฒŒ ์›€์ง์ด๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์ž‘์—…ํ•  ๋•Œ๋Š” ํ—ˆ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฃผ(์นด๋‚˜๋ฆฌ์•„์—์„œ) ๋‚ด์— node_modules ์—์„œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ ํŒŒ์ผ๋กœ CSS๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ํ…Œ์ŠคํŠธ ์ค€๋น„๊ฐ€ ๋˜๋ฉด ์—ฌ๊ธฐ์— ๊ฒŒ์‹œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ฆด๋ฆฌ์Šค ์ด์ „์— ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ด๊ฒƒ์„ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ CSS๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” node_modules ์—์„œ ๋ชจ๋“ˆ์„ ํŠธ๋žœ์Šค ํŒŒ์ผ ํ•˜๊ธฐ ์œ„ํ•ด

@BrandonE ๋Š” next-transpile-modules์— *.module.css ๋ผ๋Š” ๋ชจ๋“ˆ์ด ์—ฌ์ „ํžˆ ํ•„์š”ํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ ๋ฐฉ๋ฒ•์„ ์ฐพ์œผ์…จ๋‚˜์š”?

@rjoaopereira ๋‚˜๋Š” ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๊นŠ์ด ์ดํ•ดํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๋งํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ CSS๋ฅผ ๊ฐ€์ ธ์˜จ ๋Œ€๋ถ€๋ถ„์˜ node_modules ๋Š” @zeit/next-css ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ๋งŒ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ•œ ๊ฐ€์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์•˜์œผ๋ฉฐ ์ด ์‹œ์ ์—์„œ ํŠธ๋žœ์ŠคํŒŒ์ผ๋กœ ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์šฐ์•„ํ•œ ์†”๋ฃจ์…˜๊ณผ๋Š” ๊ฑฐ๋ฆฌ๊ฐ€ ๋ฉ€๊ณ  Next.js์˜ ๋ฏธ๋ž˜ ๋ฒ„์ „์—์„œ๋Š” Babel/Webpack ์—ฐ๊ธˆ์ˆ ์— ๋” ์ ์€ ์‹œ๊ฐ„์„ ํ• ์• ํ•˜๊ณ  ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“œ๋Š” ๋ฐ ๋” ๋งŽ์€ ์‹œ๊ฐ„์„ ํ• ์• ํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์œผ๋กœ ๊ฑฐ์˜ ์ž‘๋™ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ 9.5.3
๋‹ค์Œ ํŠธ๋žœ์ŠคํŒŒ์ผ ๋ชจ๋“ˆ 4.1.0
๊ฐ์„ฑ์„ ๋‹ด์€ ํผ์ŠคํŠธ ํŒŒํ‹ฐ ๊ตฌ์„ฑ ์š”์†Œ.
CSS ๋ชจ๋“ˆ๊ณผ ๊ธ€๋กœ๋ฒŒ CSS๊ฐ€ ํ˜ผํ•ฉ๋œ ํƒ€์‚ฌ ๊ตฌ์„ฑ ์š”์†Œ

scopedcomponents ๋Š” ์‚ฌ์šฉ ์ค‘์ธ ํƒ€์‚ฌ ๊ตฌ์„ฑ ์š”์†Œ๋กœ ๊ต์ฒด๋ฉ๋‹ˆ๋‹ค.

//next.config.js
const withCustomWebpack = require("./webpack-custom.config");
const withNextCSSOverride = require("./next.config.css");
const withTM = require("next-transpile-modules")(["@scopedcomponents"]);

module.exports = withCustomWebpack(
  withTM(
    withNextCSSOverride({
      poweredByHeader: false
    })
  )
);

///next.config.css.js
const {
  getCssModuleLocalIdent
} = require("next/dist/build/webpack/config/blocks/css/loaders/getCssModuleLocalIdent");
const path = require("path");
/**
 * Stolen from https://stackoverflow.com/questions/10776600/testing-for-equality-of-regular-expressions
 */
const regexEqual = (x, y) => {
  return (
    x instanceof RegExp &&
    y instanceof RegExp &&
    x.source === y.source &&
    x.global === y.global &&
    x.ignoreCase === y.ignoreCase &&
    x.multiline === y.multiline
  );
};

module.exports = (nextConfig = {}) => {
  return Object.assign({}, nextConfig, {
    webpack(config, options) {
      const nextCssLoaders = config.module.rules.find(
        rule => typeof rule.oneOf === "object"
      );

      if (nextCssLoaders) {
        const nextCssLoader = nextCssLoaders.oneOf.find(
          rule =>
            rule.sideEffects === false &&
            regexEqual(rule.test, /\.module\.css$/)
        );

        if (nextCssLoader) {
          /***********************************************************
           * change the rule to match all scopedcomponents css files
           ***********************************************************/
          nextCssLoader.test = /(@scopedcomponents|react\-virtualized)\/.*\.css$/;

          const cssLoader = nextCssLoader.use.find(({ loader }) =>
            loader.includes("css-loader")
          );

          if (cssLoader) {
            /***********************************************************
             * Override the default behaviour for CSS modules discovery
             * auto = true makes webpack search for *.module.css
             * https://webpack.js.org/loaders/css-loader/#auto
             ***********************************************************/
            cssLoader.options.modules.auto = /@scopedcomponents\/.*\.css$/;
            /***********************************************************
             * Nextjs overrides the default mode to "Pure"
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack/config/blocks/css/loaders/modules.ts#L35
             * Put it back to normal
             ***********************************************************/
            cssLoader.options.modules.mode = "local";
            /***********************************************************************************************************************
             * There is a problem when using components built with css-modules with Nextjs.                                        *
             * NextJS will consume code from `lib` on the server side and from `es` on the client.                                 *
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack-config.ts#L374                            *
             * This raises a problem when generating the classes for different environments,                                       *
             * throwing an error of className mismatch due to the hash created being based on the file path                        *
             * https://github.com/vercel/next.js/blob/v9.5.2/packages/next/build/webpack/config/blocks/css/loaders/modules.ts#L26  *
             * https://github.com/webpack/loader-utils/blob/v1.4.0/lib/interpolateName.js#L39                                      *
             * To solve this, when generating the classNames for 3rd party components,                                                 *
             * we need to tell cssloader to always use the same path                                                               *                                                                          *
             *                                                                                                                     *
             *  https://github.com/zeit/next-plugins/issues/595                                                                    *
             ***********************************************************************************************************************/
            cssLoader.options.modules.getLocalIdent = (
              context,
              localIdentName,
              localName,
              options
            ) => {
              const newContext = { ...context };
              if (newContext.resourcePath.includes("@scopedcomponents")) {
                newContext.resourcePath = newContext.resourcePath.replace(
                  `${path.sep}es${path.sep}`,
                  `${path.sep}lib${path.sep}`
                );
              }
              return getCssModuleLocalIdent(
                newContext,
                localIdentName,
                localName,
                options
              );
            };
          }
        }
      }

      if (typeof nextConfig.webpack === "function") {
        return nextConfig.webpack(config, options);
      }

      return config;
    }
  });
};

๋ฌธ์ œ:

  • ๋” ์ด์ƒ ๊ฐœ๋ฐœ ๋ชจ๋“œ์— SSR์ด ์—†์Šต๋‹ˆ๋‹ค. ์ƒ์‚ฐ ์ž‘์—….
  • ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์—ฌ์ „ํžˆ CSS๊ฐ€ ์žฌ์ •์˜๋˜์ง€๋งŒ dev์—์„œ๋Š” ์žฌ์ •์˜๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋Š” https://github.com/vercel/next.js/issues/11448 ๋ฐ https://github.com ์—์„œ ์–ธ๊ธ‰ํ•œ ๋Œ€๋กœ next-css๋ฅผ ์ œ๊ฑฐํ•œ ๋™๊ธฐ์˜€์Šต๋‹ˆ๋‹ค.

@Timer ์ด์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

๋‹ค์Œ ์ฃผ(์นด๋‚˜๋ฆฌ์•„์—์„œ) ๋‚ด์— node_modules ์—์„œ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ ํŒŒ์ผ๋กœ CSS๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค! ํ…Œ์ŠคํŠธ ์ค€๋น„๊ฐ€ ๋˜๋ฉด ์—ฌ๊ธฐ์— ๊ฒŒ์‹œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ์ˆ˜์ • ํ›„์— ๊ตฌ์„ฑ ์š”์†Œ์—์„œ CSS๋ฅผ ๋™์ ์œผ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@Timer ๋‹˜ ์ •๋ง ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

next@^9.5.4-canary.10 ์‚ฌ์šฉํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์–ด๋””์—์„œ๋‚˜ node_modules ์—์„œ ์ „์—ญ CSS๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด CSS๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜์ง€๋งŒ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ๋Š˜๋ฆฌ๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š๋Š” ํƒ€์‚ฌ React ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€์˜ ์ƒํ˜ธ ์šด์šฉ์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.

@Timer ๊ทธ ๋ฆด๋ฆฌ์Šค๊ฐ€ ๋„ˆ๋ฌด ๊ธฐ๋Œ€๋ฉ๋‹ˆ๋‹ค. ์ž‘์—…์— ์ •๋ง ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค ๐Ÿ’ฏ โค๏ธ

@Timer ๋‹˜ ๊ฐ์‚ฌ

์ด๊ฒƒ์€ ํ˜„์žฌ ์ €์—๊ฒŒ ์ฐจ๋‹จ ๋ฌธ์ œ์˜€์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์˜ค๋Š˜ ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ–ˆ์„ ๋•Œ ์—ฌ์ „ํžˆ ๋™์ผํ•œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ 9.5.4-canary-10์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ๊ฒƒ ์™ธ์— ๋‹ค๋ฅธ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์ด ์˜ˆ๋Š” ํƒ€์‚ฌ lib @rmwc ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

image

@johmike ๋‹ค์Œ ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€์ ธ

import "@rmwc/avatar/avatar.css";

next ์ตœ์‹  ๋ฒ„์ „์„ ์„ค์น˜ํ•œ ํ›„ ๊ฐœ๋ฐœ ์„œ๋ฒ„๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•ด ๋ณด์…จ์Šต๋‹ˆ๊นŒ?

@Timer ์ด ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด node_modules ํด๋”์—์„œ CSS ํŒŒ์ผ์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

import 'prism-themes/themes/prism-darcula.css';

node_modules ๋””๋ ‰ํ† ๋ฆฌ ์™ธ๋ถ€์˜ ๊ธ€๋กœ๋ฒŒ CSS ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ง€์›ํ•  ๊ณ„ํš์ด ์žˆ์Šต๋‹ˆ๊นŒ?

@sasivarnan
์ด๊ฒƒ์€ @rmwc ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” @require("@rmwc/avatar/avatar.css") ์žˆ์Šต๋‹ˆ๋‹ค. import {Avatar} from "library/Avatar" ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ๋Š”๋ฐ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

@sasivarnan

์ด๊ฒƒ์€ @rmwc ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” @require("@rmwc/avatar/avatar.css") ์žˆ์Šต๋‹ˆ๋‹ค. import {Avatar} from "library/Avatar" ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ๋Š”๋ฐ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์•Œ์•˜์–ด์š”. ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‹น์‹ ์˜ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์ง์ ‘ ๊ฐ€์ ธ์˜จ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ์ž˜๋ชป์ด์•ผ.

์—ฌ๊ธฐ์— ์žˆ๋Š” ์˜๊ฒฌ์œผ๋กœ ํŒ๋‹จํ•˜๋ฉด ์ด๊ฒƒ์€ ์‹ค์ œ๋กœ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ํ•ด๊ฒฐ๋˜์—ˆ์ง€๋งŒ ์—ฌ๊ธฐ์—์„œ ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ๋‹ค๋ฅธ ๋ฌธ์ œ๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์€ ์—ฌ์ „ํžˆ โ€‹โ€‹ํŒจํ‚ค์ง€ ์ž์ฒด(ํŒจํ‚ค์ง€ ํŒŒ์ผ ๋‚ด๋ถ€์˜ import style.css ๋ฌธ)์—์„œ CSS๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ชจ๋“ˆ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ˆ˜์ • ์‚ฌํ•ญ์€ ์•ฑ์ด node_modules/ ๊ฒฝ๋กœ์—์„œ CSS๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด์ง€๋งŒ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ฃผ ์‰ฌ์šด ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์ •๋  ๋•Œ๊นŒ์ง€ ์ง€๊ธˆ์€ CSS๋ฅผ ์•ฑ์— ๋ณต์‚ฌํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ฐจ๋‹จ ์ˆ˜์ค€์˜ ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋”ฑํžˆ ์†์‰ฌ์šด ํ•ด๊ฒฐ์ฑ…์ด ์—†๋Š” ๋ธ”๋กœ์ปค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜์ง€๋Š” ๋ชปํ–ˆ๋‹ค. import ๋ฌธ์ด ์žˆ๋Š” ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ํŒจํ‚ค์ง€ ์ž์ฒด์— ํฌํ•จ๋œ CSS ํŒŒ์ผ๋กœ ๊ฐ€์ ธ์˜ค๋ฉด ์•ฑ์ด ์ถฉ๋Œํ•ฉ๋‹ˆ๋‹ค.

@sasivarnan @OssiPesonen ๋‘ ๋ถ„ ๋ชจ๋‘ ์ด OP ๋ฌธ์ œ์—์„œ ๋…ผ์˜๋˜๊ณ  ์ˆ˜์ •๋œ ๊ฒƒ๊ณผ ๋‹ค๋ฅธ ๋ฌธ์ œ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ํŠนํžˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ CSS๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

// components/MySlider.tsx
import { Slider } from "@reach/slider";
import "@reach/slider/styles.css";

function Example() {
  return <Slider min={0} max={200} step={10} />;
}

๋‹น์‹ ์ด ๋งํ•˜๋Š” ๊ฒƒ์€ #706๊ณผ #13282์˜ ๋ณต์ œ ๋˜๋Š” node_modules ์ž์‚ฌ ์ฝ”๋“œ์ฒ˜๋Ÿผ ์ทจ๊ธ‰ํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

@Timer ๋ฐฉ๊ธˆ ์˜ˆ์ƒ๋˜๋Š” ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ํ…Œ์ŠคํŠธํ–ˆ์œผ๋ฉฐ ์‹ค์ œ๋กœ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ์˜ CSS๋ฅผ ๋‹ค์Œ ๊ตฌ์กฐ์˜ ์ผ๋ถ€๋กœ ์ง์ ‘ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๋ฉฐ ์˜ค๋ฅ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ํ•ด๋‹น ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ๋‹ค์Œ ๊ตฌ์กฐ ์™ธ๋ถ€์˜ ๋‹ค๋ฅธ ํŒจํ‚ค์ง€๋กœ ์ด๋™ํ•˜๊ณ  ํ•ด๋‹น ํŒจํ‚ค์ง€๋ฅผ ๋นŒ๋“œํ•œ ๋‹ค์Œ ์„ค์น˜ํ•˜๋ฉด ์ด์ „๊ณผ ๋™์ผํ•œ ์˜ค๋ฅ˜๋กœ ์žฅ์•  ๋ณต๊ตฌ๋ฉ๋‹ˆ๋‹ค.

์ด ์˜ˆ์—์„œ Avatar ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋„ ์•Š๊ณ  Button ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์ง€๋งŒ Avatar ๋Š” ์‹คํŒจํ•œ ์˜ค๋ฅ˜์ด๋ฏ€๋กœ ๋‹ค๋ฅธ ์ผ์ด ์ง„ํ–‰ ์ค‘์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

image

๋˜ํ•œ monorepo์—์„œ ์ž‘์—…ํ•  ๋•Œ next-transpile-modules ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์ง€๋งŒ ์ด ํŠน์ • ๋ฌธ์ œ์—๋Š” ๋„์›€์ด ๋˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

next-transpile-modules ์ฃผ๋ณ€์˜ ๋‹ค๋ฅธ ๋งŽ์€ ๋ฌธ์ œ๋ฅผ ํŒŒํ—ค์ณ์„œ ์ด์ƒํ•œ ๊ตฌ์„ฑ ํŒŒ์ผ๋กœ ์ด๊ฒƒ์„ ์ž‘๋™์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

const withCSS = require("@zeit/next-css");
module.exports = withCSS();
require.extensions[".css"] = () => {
  return;
};

๋‚˜๋Š” next-transpile-modules ์ œ๊ฑฐํ–ˆ๊ณ  ์ด๊ฒƒ์€ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์ด์œ ๋Š” ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ์•„๋ฌด ์ผ๋„ ํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๊ฒƒ ๊ฐ™์€๋ฐ์š”?

๋‚ด๊ฐ€ ๋„ˆ๋ฌด ๋นจ๋ฆฌ ๋งํ–ˆ๋‹ค! next dev ์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€๋งŒ next build ๋Š” CSS ํŒŒ์ผ ์ค‘ ํ•˜๋‚˜์˜ unknown token . (์ ) ์˜ค๋ฅ˜๋กœ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

@Timer ์–ด๋–ค ์ƒ๊ฐ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋ชจ๋…ธ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์™€ ์—ฌ๋Ÿฌ ํŒจํ‚ค์ง€์—์„œ ์ฆ‰์‹œ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด @team/packageA๊ฐ€ node_modules์—์„œ CSS๋ฅผ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ @team/packageB๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ด์•ผ ํ•˜๋Š” ๋‹ค๋ฅธ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

ํ•ด๋‹น ๋™์ž‘์— ๋Œ€ํ•ด https://github.com/vercel/next.js/issues/13282 ๋ฅผ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[email protected] ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ์–ด๋Š ๊ณณ์—์„œ๋‚˜ CSS๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ scs ํŒŒ์ผ์—์„œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๊นŒ? ํŽ˜์ด์ง€ ์—์„œ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” scss ํŒŒ์ผ๋งŒ ๊ฐ€์ ธ์˜ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

// ํŽ˜์ด์ง€/_app.tsx
import '../styles/common.scss'

// ํŽ˜์ด์ง€/index.tsx ๋ฒ„ํŠผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
import '@mynpm/custom-ui/_Button.scss'

// ํŽ˜์ด์ง€/about.tsx ์บ๋Ÿฌ์…€์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
import '@mynpm/custom-ui/_Carousel.scss'

https://nextjs.org/docs/basic-features/built-in-css-support ์˜ ์˜ˆ
Schermata 2020-10-13 alle 16 43 19

์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
์˜ค๋ฅ˜ - /Users/gp/dev/next-kolumbus/node_modules/@reach/dialog/styles.css
์ „์—ญ CSS๋Š” node_modules ๋‚ด์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋” ์ฝ์–ด๋ณด๊ธฐ: https://err.sh/next.js/css-npm

https://nextjs.org/docs/basic-features/built-in-css-support ์˜ ์˜ˆ
Schermata 2020-10-13 alle 16 43 19

์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
์˜ค๋ฅ˜ - /Users/gp/dev/next-kolumbus/node_modules/@reach/dialog/styles.css
์ „์—ญ CSS๋Š” node_modules ๋‚ด์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋” ์ฝ์–ด๋ณด๊ธฐ: https://err.sh/next.js/css-npm

์ตœ์‹  ๋ฒ„์ „์˜ Next.js๋ฅผ ์‚ฌ์šฉ ์ค‘์ธ์ง€ ํ™•์ธํ•˜์„ธ์š”.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋Œ“๊ธ€์—์„œ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ฒ„์ „ 9.5.5๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. npm์—์„œ ๋ฐฉ๊ธˆ ์—…๋ฐ์ดํŠธํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  .next ์บ์‹œ๋ฅผ ์ง€์› ๊ณ  ์ด์ œ ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

_app --> import "react-gauge-chart-nextjs-support/dist/GaugeChart/style.css"; ์—ฌ์ „ํžˆ ๋ฒ„์ „ 9.5.5 ์˜ค๋ฅ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Screenshot 2020-11-12 at 14 12 11

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰