๐Ÿง–๐Ÿปโ€โ™‚๏ธ NextJS 12 ์—…๋ฐ์ดํŠธ๋ฅผ ํ•ด๋ณด์•˜์๋‹ˆ๋‹ค ๐Ÿง–๐Ÿปโ€โ™‚๏ธ

9rganizedChaosยท2022๋…„ 9์›” 2์ผ
3
post-thumbnail

๋‚ด๊ฐ€ ํšŒ์‚ฌ์—์„œ ๋‹ค๋ฃจ๊ณ  ์žˆ๋Š” ์ œํ’ˆ์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๋กœ ๋ถ„๋ฅ˜ํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ทธ ์ค‘ ํ•˜๋‚˜์ธ ์šฐ๋ฆฌ ํšŒ์‚ฌ ์›น์‚ฌ์ดํŠธ(๋žœ๋”ฉํŽ˜์ด์ง€)๋Š” NextJS๋ฅผ ๋ฒ ์ด์Šค๋กœ ํ•˜๊ณ  ์žˆ๋‹ค. NextJS ๋„์ž…์„ ๋”ฑํžˆ ๋‚ด๊ฐ€ ๊ฒฐ์ •ํ•œ ๊ฒƒ์€ ์•„๋‹Œ๋ฐ, ๋‚˜๋ณด๊ณ  ์„ ํƒํ•˜๋ผ๊ณ  ํ–ˆ์–ด๋„ ๊ฐ™์€ ์„ ํƒ์„ ํ–ˆ์„ ๊ฒƒ์ด๋‹ค. ์œ ์ €๋“ค๊ณผ์˜ ์‹ค์‹œ๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ์ด ๋”ฑํžˆ ๋งŽ์€ ๊ฒƒ์ด ์•„๋‹Œ๋ฐ๋‹ค, ์ •๋ณด ์ „๋‹ฌ ์œ„์ฃผ์˜ ํŽ˜์ด์ง€๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์–ด์„œ...! ํŠนํžˆ ํšŒ์‚ฌ ๋‚ด๋ถ€ ํฌ๋ฃจ๋“ค์ด ์‚ฌ์šฉํ•˜๋Š” ์‹œ์Šคํ…œ์ด ์•„๋‹ˆ๋ผ, ์ผ๋ฐ˜ ์œ ์ €๋“ค์—๊ฒŒ ์šฐ๋ฆฌ ํšŒ์‚ฌ ์ œํ’ˆ๋“ค์„ ์•Œ๋ฆฌ๋Š” ๊ฒƒ์ด ์ฃผ๋ชฉ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— SEO์—๋„ ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋“ค ๋ณด๋‹ค๋Š” ๋”์šฑ ์‹ ๊ฒฝ์„ ์“ฐ๊ณ  ์žˆ๋‹ค.

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

๊ทธ ์ค‘ ํ•œ๊ฐ€์ง€ ๋ฒผ๋ฅด๊ณ  ์žˆ๋˜ ๊ฒƒ์ด React์™€ NextJS๋ฅผ ์ตœ์‹ ๋ฒ„์ „์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

NextJS ์—…๊ทธ๋ ˆ์ด๋“œ๊ฐ€ ์™œ ํ•„์š”ํ–ˆ๋‚˜?

๊ทผ๋ž˜ ๋งˆ์ผ€ํŒ…ํŒ€์œผ๋กœ๋ถ€ํ„ฐ ํšŒ์‚ฌ ์›น์‚ฌ์ดํŠธ์˜ ๋ฐฐ๋„ˆ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ์ด ์กฐ๊ธˆ ๋Š๋ ค์กŒ๋‹ค๋Š” ์ด์Šˆ๊ฐ€ ์ œ๋ณด๋˜์—ˆ๋‹ค. ์›น์‚ฌ์ดํŠธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋“ค์„ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹ ์ž์ฒด์— ์•ฝ๊ฐ„์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐ๋˜๋‚˜ ์ด ๋ถ€๋ถ„์€ ๋‹น์žฅ ๋น ๋ฅด๊ฒŒ ๋ณ€ํ™”๋ฅผ ์‹œ๋„ํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ๋ถ€๋ถ„์ด ์•„๋‹ˆ๋ผ, ์šฐ์„  ํ”„๋ก ํŠธ ๊ฐœ๋ฐœ์ž๋“ค๋ผ๋ฆฌ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค์„ ๋ฆฌ์ŠคํŠธ์—…ํ•ด๋ณด์•˜๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•ด์„œ ์ƒ๊ฐํ•ด๋‚ธ ๊ฒƒ์ด ์ด๋ฏธ์ง€ ํƒœ๊ทธ์—lazy loding๊ณผ async decoding์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด์—ˆ๊ณ , ๋˜ ํ•˜๋‚˜๋Š” NextJS๊ฐ€ ์ œ๊ณตํ•˜๋Š” Image ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋ƒ‰ํผ Image ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ ์šฉํ•˜๋ ค๋˜ ์ฐจ์—, Image ์ปดํฌ๋„ŒํŠธ์˜ layout ์†์„ฑ์— fill ๊ฐ’์„ ๋„˜๊ธฐ๋‹ˆ ์ž๊พธ๋งŒ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ๊ฐ€. ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋กœ Image์˜ Props๋ฅผ ์‚ดํŽด๋ณด๋‹ˆ, ์•„๋‹ˆ๋‚˜ ๋‹ค๋ฅผ๊นŒ, layout์— ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์— "fill"์ด ์—†๋‹ค...? ์ƒ๊ฐํ•ด๋ณด๋‹ˆ, ํšŒ์‚ฌ์— ๋“ค์–ด์˜ค๊ณ  ๋‚˜์„œ NextJS๋ฅผ ํ•œ ๋ฒˆ๋„ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•ด๋ณธ ์ ์ด ์—†์—ˆ๊ณ , ์šฐ๋ฆฌ๋Š” ํ˜„์žฌ NextJS ๋ฒ„์ „10์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค.

๋งˆ์นจ ํ•œ์ฐธ ๋ฐ”์˜๋‹ค๊ฐ€ ์กฐ๊ธˆ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋‚จ๋Š” ์š” ๋ฉฐ์น ์ด์—ˆ๊ธฐ์—, ๋‘ ๋ฒˆ ๊ณ ๋ฏผํ•  ๊ฒƒ ์—†์ด ๋ƒ‰ํผ NextJS ๋ฒ„์ „ ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ๊ฒฐ์‹ฌํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค!

๋ณธ๊ฒฉ ์—…๋ฐ์ดํŠธ ํ•˜๊ธฐ!

์•„์ฃผ ์นœ์ ˆํ•˜๊ฒŒ๋„, NextJS๋Š” Upgrade Guide๋ฅผ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ๋‹ค. ๊ฐ€์ด๋“œ์˜ ๋‚ด์šฉ์„ ์ด๊ณณ์— ๋ฐ˜๋ณตํ•  ํ•„์š”๋Š” ์—†์„ ๊ฒƒ ๊ฐ™๊ณ  ๋‚ด๊ฐ€ ๊ฒช์€ ์—๋Ÿฌ๋“ค์„ ์œ„์ฃผ๋กœ ํฌ์ŠคํŒ…์„ ์ด์–ด๊ฐ€๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค!

Error 1: Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.

npm install react@latest react-dom@latest
npm install next@12

์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ์œ„ํ•ด ์œ„ ๋‘ ๋งˆ๋ฒ•์ฃผ๋ฌธ์„ ํ„ฐ๋ฏธ๋„์— ์™ธ์นœ ํ›„, ์šฐ์„  ๋ณ„์ผ์ด ์—†์ž ๊ธฐ๋„ํ•˜๋Š” ๋งˆ์Œ์œผ๋กœ npm run dev๋ฅผ ์‹คํ–‰ํ•˜์˜€๋‹ค.
NextJS ์—…๋ฐ์ดํŠธ ๋„ˆ๋ฌด ์‰ฌ์šด๋ฐ! ๋ผ๊ณ  ๋ฐฉ์‹ฌํ•˜๋Š” ์ˆœ๊ฐ„!

...^^*

์œ„ ์—๋Ÿฌ๋ฅผ ๊ตฌ๊ธ€๋งํ•ด๋ณธ ๊ฒฐ๊ณผ, next.config.js๋ฅผ ๊ฑด๋“œ๋ ค์ฃผ์–ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ˆˆ์น˜๋กœ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค!
NextJS Upgrade Guide์—์„œ๋Š” SWC๋ฅผ ์ ์šฉํ•˜๋ฉด Babel๋ณด๋‹ค ์ตœ๋Œ€ 17๋ฐฐ ๋น ๋ฅด๊ณ ... ๋ญ ๊ทธ๋Ÿฐ ์žฅ์ ์„ ์†Œ๊ฐœํ•˜๋ฉฐ

module.exports = {
  swcMinify: true,
}

์š”๋Ÿฐ ์ฝ”๋“œ๋ฅผ ์ œ์•ˆํ•ด์ฃผ๊ณ  ์žˆ๋Š”๋ฐ, ์ด๊ฑด ๋˜ ์ด๊ฒƒ๋Œ€๋กœ ์—๋Ÿฌ๋ฅผ ๋ฑ‰๋Š”๋‹ค...!

๊ฒฐ๊ตญ ๊นŠ๊ณ  ๊นŠ์€ StackOverflow์˜ ๋ฐ”๋‹ค๋ฅผ ํ—ค์—„์ณ ์ฐพ์•„๋‚ธ ํ•ด๋ฒ•์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

//next.config.js

// [BEFORE] ์ด๋ ‡๊ฒŒ ์ž‘์„ฑ๋˜์–ด์žˆ๋˜ ์ฝ”๋“œ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฐ”๊ฟˆ!
module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      issuer: {
        test: /\.(js|ts)x?$/
      },
      use: ['@svgr/webpack']
    });

    return config;
  }
};

// [AFTER] issuer ๋ถ€๋ถ„์„ ์‚ญ์ œํ•จ!
module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: ['@svgr/webpack']
    });

    return config;
  }
};

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜์˜€๋”๋‹ˆ! ์•„๋ž˜์™€ ๊ฐ™์ด ์šฐ์„  ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ๋ธŒ๋ผ์šฐ์ €์— ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋ง์€ ๋œ๋‹ค!

Error 2: Hydration failed because the initial UI does not match what was rendered on the server.

์™œ... ํŽ˜์ด์ง€๊ฐ€ ๋‘ ๋ฒˆ ๋ Œ๋”๋˜์ง€...?

๋‹ค ๋๋‚˜๋ด!!๐Ÿ˜ญ๐Ÿ˜ญ๐Ÿ˜ญ๐Ÿ˜ญ ๋ผ๊ณ  ๊ฐ๋™์˜ ๋ˆˆ๋ฌผ์„ ํ˜๋ฆฌ๋ ค๋˜ ์ฐฐ๋‚˜. ๋ Œ๋”๋ง๋œ ํŽ˜์ด์ง€๊ฐ€ ์–ด๋”˜๊ฐ€ ์ด์ƒํ•˜๋‹ค๋Š” ๊ฒƒ์„ ๊ฐ์ง€ํ•œ ๋‚˜.
์–ด์ฉ์ง€ ์Šคํฌ๋กค๋ฐ”์˜ ํฌ๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ์งง๊ฒŒ ๋Š๊ปด์ง€๋Š” ๊ฒƒ์ด ์•„๋‹Œ๊ฐ€.

์ขŒ์ธก์ด ๋ผ์ด๋ธŒ ํ™˜๊ฒฝ์— ํŽ˜์ด์ง€๊ฐ€ ์ œ๋Œ€๋กœ ๋žœ๋”๋ง๋œ ๋ชจ์Šต, ์šฐ์ธก์ด Next ์—…๊ทธ๋ ˆ์ด๋“œ ํ›„ ๊ฐœ๋ฐœํ™˜๊ฒฝ์—์„œ ํŽ˜์ด์ง€๊ฐ€ ๋ Œ๋”๋ง๋œ ๋ชจ์Šต์ด๋‹ค.
๋ฌผ๋ก  ํ•œ๋ˆˆ์— ๋ด๋„ ์Šคํฌ๋กค๋ฐ” ๋งŒ์ด ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ž€ ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ํ•˜์—ฌ... ์ด๊ฒŒ ๋ฌด์Šจ ์ผ์ด๊ณ ... ํ•˜๋ฉฐ ์Šคํฌ๋กค์„ ๋‚ด๋ ค๋ณด๋‹ˆ...!

๋Œ€์ฒด ์™œ ํ‘ธํ„ฐ ๋ฐ‘์— ํŽ˜์ด์ง€๊ฐ€ ํ•œ ๋ฒˆ ๋” ๋“ฑ์žฅํ•˜๋Š” ๊ฑด๋ฐ?
๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋กœ ๋ƒ‰ํผ ๊นŒ๋ณด๋‹ˆ... ํŽ˜์ด์ง€๊ฐ€ ๋‘ ๋ฒˆ ๋ Œ๋”๋ง๋˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹Œ๊ฐ€...!?
๋„ˆ๋ฌด ์ด์ƒํ•ด์„œ NextJS 12 render twice ๋“ฑ์œผ๋กœ ์ˆฑํ•˜๊ฒŒ ๊ตฌ๊ธ€๋ง์„ ํ•ด๋ณด์•˜์œผ๋‚˜, ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋„ˆ๋ฌด ์—†์—ˆ๋‹ค. ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋„ˆ๋ฌด ์—†์„ ๋• ์‹ญ์ค‘ํŒ”๊ตฌ... ์ด๊ฑด NextJS๋ผ๊ธฐ๋ณด๋‹ค๋Š” ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ ์–ด๋”˜๊ฐ€์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ๊ฒƒ... ๊ฒธ์†ํ•œ ๋งˆ์Œ์œผ๋กœ ๋‹ค์‹œ ์ฝ”๋“œ๋ฅผ ๋‘˜๋Ÿฌ๋ณด์•˜๋‹ค!

๊ทธ๋Ÿฌ๋‹ค๊ฐ€ ์˜์‹ฌํ•  ๋งŒํ•œ ๊ณณ์ด ๋ฐ”๋กœ ์ด๊ณณ์ด์—ˆ๋‹ค!

//_app.tsx
// ์ƒ๋žต ํ›„ ์ผ๋ถ€ ๋ฐœ์ทŒ 

  return (
    // ์ƒ๋žต
            hackleClient ? (
              <HackleProvider hackleClient={hackleClient} user={hackleUser}>
                <Component {...pageProps} />
              </HackleProvider>
            ) : (
              <Component {...pageProps} />
            )
    // ์ƒ๋žต
  );

hackle์€ ์šฐ๋ฆฌ ํšŒ์‚ฌ์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ๋งˆ์ผ€ํŒ… ์†”๋ฃจ์…˜ ํˆด์ธ๋ฐ, hackleClient๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋  ๋•Œ๋งŒ ์ œ๋Œ€๋กœ ์ƒ์„ฑ๋˜์–ด์„œ ์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋‹ค. (hackleClient๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด sdk ๊ณ ์œ  ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ด๊ฒŒ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ €์žฅ๋˜์–ด์žˆ์–ด์„œ...!) ์•„๋ฌด๋ฆฌ ์ƒ๊ฐํ•ด๋„ ํŽ˜์ด์ง€ ์ „์ฒด๊ฐ€ ๋‘๋ฒˆ ๋ Œ๋”๋ง๋  ์ด์œ ๋Š” ์œ„ ์ฝ”๋“œ๋ฐ–์— ์—†๋Š” ๋“ฏ ํ•˜์—ฌ ์šฐ์„  ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋”๋‹ˆ, ๋งˆ์นจ๋‚ด ํŽ˜์ด์ง€๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ํ•œ๋ฒˆ๋งŒ ๋ Œ๋”๋ง ๋˜์—ˆ๋‹ค.

  return (
    // ์ƒ๋žต
            hackleClient ? (
              <HackleProvider hackleClient={hackleClient} user={hackleUser}>
                <Component {...pageProps} />
              </HackleProvider>
            ) : null
    // ์ƒ๋žต
  );

๊ทธ๋Ÿฐ๋ฐ ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•ด๋†“๊ณ  ๋ณด๋‹ˆ... ์•„๋ฌด๋ฆฌ hackle Client๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” ์ œ๋Œ€๋กœ ์ž‘๋™ํ•  ๊ฒƒ์ด๋ผ ํ™•์‹ ํ•œ๋‹ค ํ•œ๋“ค... ๋งŒ์ผ hackleClient์— ๋ฌด์Šจ ๋ฌธ์ œ๋ผ๋„ ์žˆ์„ ์‹œ์—๋Š” ํŽ˜์ด์ง€ ์ „์ฒด๊ฐ€ ๋กœ๋”ฉ์ด ๋˜์ง€ ์•Š์„์ง€๋„ ๋ชจ๋ฅธ๋‹ค๋Š” ์ƒ๊ฐ์— ์ข€ ๋ถˆ์•ˆํ•ด์กŒ๋‹ค. ์šฐ๋ฆฌ ํšŒ์‚ฌ ํŽ˜์ด์ง€๊ฐ€ ์™ธ๋ถ€ ์†”๋ฃจ์…˜์— ์˜์กด์„ฑ์ด ์ƒ๊ฒจ์„œ๋Š” ์•ˆ ๋œ๋‹ค๋Š” ํŒ๋‹จ์ด์—ˆ๋‹ค.

๊ฒฐ๊ตญ hackleClient๋ฅผ useState๋ฅผ ํ†ตํ•ด ์ƒํƒœ๋กœ ๊ด€๋ฆฌํ•ด์ฃผ๋ฉด์„œ ๋ฌธ์ œ๋ฅผ ์–ผ์ถ”ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ ๋งˆ์ฃผํ•œ ์—๋Ÿฌ์—์„œ ์œ„์—์„œ ํ–ˆ๋˜ ๋งŽ์€ ์‚ฝ์งˆ์˜ ์›์ผ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค!
(์•„๋ฌด๋ฆฌ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋”˜๊ฐ€ ์ฝ”๋“œ๊ฐ€ ์ด์ƒํ•˜๊ฒŒ ์จ์žˆ์—ˆ๋‹ค๊ณ  ํ•ด๋„, NextJS ์—…๋ฐ์ดํŠธ ์ด์ „์—๋Š” ํŽ˜์ด์ง€๊ฐ€ ๋™์‹œ์— ๋‘๋ฒˆ ๋ Œ๋”๋ง ๋˜๋Š” ์ผ์€ ์—†์—ˆ๋‹ค...)

React Hydration!

์œ„ ์—๋Ÿฌ๋Š” NextJS ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ์นœ์ ˆํžˆ ์„ค๋ช…ํ•ด์ค€๋‹ค.

Why This Error Occurred
While rendering your application, there was a difference between the React tree that was pre-rendered (SSR/SSG) and the React tree that rendered during the first render in the Browser. The first render is called Hydration which is a feature of React.
This can cause the React tree to be out of sync with the DOM and result in unexpected content/attributes being present.

๊ณต์‹ ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด...!
SSR ํ˜น์€ SSG๋ฅผ ํ†ตํ•ด ํ”„๋ฆฌ๋ Œ๋”๋˜๋Š” ๋ฆฌ์•กํŠธ ํŠธ๋ฆฌ์™€ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ฒซ๋ฒˆ์งธ๋กœ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ Œ๋”๋  ๋•Œ, ๊ทธ ์‚ฌ์ด์—๋Š” ์ฐจ์ด๊ฐ€ ์กด์žฌํ•œ๋‹ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ฒซ ๋ฒˆ์งธ ๋ Œ๋”๋ง์ด Hydration์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๋ฆฌ์•กํŠธ์˜ ๊ธฐ๋Šฅ์ด๋ผ๊ณ  ํ•œ๋‹ค. ์ด ๋•Œ๋ฌธ์— DOM๊ณผ์˜ ์‹ฑํฌ๊ฐ€ ๋งž์ง€ ์•Š๊ฒŒ ๋  ์ˆ˜๋„, ์˜๋„์ง€ ์•Š์€ ์ฝ˜ํ…์ธ ๋‚˜ ์†์„ฑ์ด ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ ...

์ด๋ ‡๊ฒŒ๋งŒ ์ฝ์–ด์„œ๋Š” Hydration์— ๋Œ€ํ•ด ์ดํ•ดํ•˜๊ธฐ๊ฐ€ ์กฐ๊ธˆ ์–ด๋ ค์›Œ ์ž๋ฃŒ๋ฅผ ์กฐ๊ธˆ ๋” ์ฐพ์•„๋ณด์•˜๋‹ค!

SSR์€ ์ •์  ํŽ˜์ด์ง€๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ  js ํŒŒ์ผ๋„ ๋ฒˆ๋“ค๋งํ•œ ํ›„์— ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ๋กœ ์ด ๋‘˜์„ ๋ชจ๋‘ ๋‚ด๋ ค์ฃผ๊ฒŒ ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋•Œ DOM์€ ๋™์ ์ธ ์ด๋ฒคํŠธ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์€ ๋ฉ”๋งˆ๋ฅธ ์ƒํƒœ์ด๋‹ค. ์ด๋•Œ ๋ฉ”๋งˆ๋ฅธ DOM์— html ์ฝ”๋“œ์™€ js๋ฅผ ๋งค์นญํ•ด ์›น์‚ฌ์ดํŠธ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ๋ Œ๋”๋ง ํ•˜๋Š” ๊ธฐ์ˆ ์ด ๋ฐ”๋กœ Hydration์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด Hydration์—๋Š” ์–ด์ฉ” ์ˆ˜ ์—†๋Š” ๋‹จ์ ์ด ์กด์žฌํ–ˆ๋Š”๋ฐ, ๊ทธ๊ฒƒ์€ ๋ฐ”๋กœ ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ JS๊ฐ€ ์‹คํ–‰๋˜์–ด ํ•ธ๋“ค๋Ÿฌ ๋“ฑ์œผ๋กœ ๋ผˆ๋Œ€๊ฐ€ ์ˆ˜๋ถ„๋ณด์ถฉ ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ํŽ˜์ด๊ฐ€ ๋กœ๋“œ๋˜์—ˆ์–ด๋„ ์œ ์ €์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ React18์—์„œ๋Š” HTML Streaming๊ณผ Hydration์„ ์ œ๊ณตํ•œ๋‹ค. ์•„์ง ์™„๋ฒฝํžˆ ์ดํ•ดํ•˜์ง€๋Š” ๋ชปํ–ˆ์ง€๋งŒ, React v18๋ถ€ํ„ฐ๋Š” pipeToNodeWritable์„ ์ด์šฉํ•ด html์ฝ”๋“œ๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐ ํ˜•์‹์„ ํ†ตํ•ด ์ž‘์€ ์ฒญํฌ ํ˜•ํƒœ๋กœ ๋ณด๋‚ด์ค€๋‹ค๊ณ  ํ•œ๋‹ค. ๋‹ค์‹œ ๋งํ•ด ํด๋ผ์ด์–ธํŠธ ์ชฝ์—์„œ๋Š” ๋ Œ๋”๋ง ๋น„์šฉ์ด ํฐ ์ปดํฌ๋„ŒํŠธ๋“ค์€ Suspense๋กœ ๊ฐ์‹ธ์„œ ๋ถ€๋ถ„์ ์ด๊ณ  ๋…๋ฆฝ์ ์œผ๋กœ ๋”ฐ๋กœ hydrate๋ฅผ ์‹œ์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค. ์‚ฌ์ด๋“œ๋ฐ”๋‚˜ ๋„ค๋น„๊ฒŒ์ด์…˜ ๊ฐ™์ด ๋‹ค๋ฅธ ๊ฒƒ๋“ค๋ณด๋‹ค ๋น ๋ฅด๊ฒŒ ์ธํ„ฐ๋ž™์…˜์„ ์ œ๊ณตํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์ด๋ผ๋ฉด Suspense๋กœ ๊ฐ์‹ธ๋‘๋ฉด ๋  ๊ฒƒ์ด๋‹ค.

๋ชจ์ชผ๋ก ์œ„์™€ ๊ฐ™์€ React v18์˜ hydration๊ณผ ๊ด€๋ จํ•œ ์—…๋ฐ์ดํŠธ ๋•Œ๋ฌธ์— ๊ฒช์—ˆ๋˜ ์—๋Ÿฌ๋กœ ์ง€๊ธˆ์€ ํŒ๋‹จํ•˜์—ฌ, ์œ„ ํ‚ค์›Œ๋“œ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๊ตฌ๊ธ€๋ง์„ ํ•ด ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ด๋ณด์•˜๋‹ค!

๊ทธ๋ฆฌํ•˜์—ฌ...!

const App = ({ Component, pageProps }: AppProps) => {
  const [domLoaded, setDomLoaded] = useState(false); 
  
  // ์ƒ๋žต
  
  useEffect(() => {
    setDomLoaded(true);
  }, []);
  
  // ์ƒ๋žต
  
  if (!domLoaded) {
    return <></>;
  } else {
    return (
      // ์ƒ๋žต
              {hackleClient && hackleUser ? (
                <HackleProvider hackleClient={hackleClient} user={hackleUser}>
                  <Component {...pageProps} />
                </HackleProvider>
              ) : (
                <Component {...pageProps} />
              )}
    // ์ƒ๋žต
    );
  }
}

์œ„์—์„œ ์‚ดํŽด๋ณธ ์—๋Ÿฌ๋Š” ์ฃผ๋กœ window ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž„ํฌํŠธํ•˜๊ฑฐ๋‚˜ ์œˆ๋„์šฐ ๊ฐ์ฒด๋ฅผ ๋ฐ”๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์— ๋ฐœ์ƒํ•˜๊ฒŒ ๋˜๋Š”๋ฐ, (๋ธŒ๋ผ์šฐ์ €์—์„œ ์ •์  html๊ณผ js๋ฒˆ๋“ค์„ ๋‚ด๋ ค๋ฐ›๊ธฐ ์ „์—๋Š” window ๊ฐ์ฒด๊ฐ€ ์—†์œผ๋ฏ€๋กœ... ๋ณดํ†ต css-in-js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋งŽ์ด ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค๊ณ  ํ•œ๋‹ค!) useEffect๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

useEffect๋Š” hydration ๋ ๋•Œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ•œ ๋ฒˆ ์‹คํ–‰๋˜๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ์•ˆ์—์„œ ํŠน์ • ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ๊ณ , ํ•ด๋‹น ์ƒํƒœ๋ฅผ ์กฐ๊ฑด์œผ๋กœ ๊ฑธ์–ด return ๋ฌธ ๋‚ด๋ถ€๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๋ฉด, ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง ๋ ๋•Œ์™€์˜ ์‹ฑํฌ๋ฅผ ๋งž์ถœ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค! ํŠนํžˆ hydration ๋ ๋•Œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹คํ–‰๋œ๋‹ค๋Š” ๊ฒƒ์€ ๊ฒฐ๊ตญ ์œˆ๋„์šฐ ๊ฐ์ฒด๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋˜ ๋‹ค๋ฅธ useEffect์˜ ์˜์กด์„ฑ๋ฐฐ์—ด์— ์œ„์—์„œ ์„ค์ •ํ•œ ์ƒํƒœ์˜ ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ณ  ํ™œ์šฉํ•˜๋ฉด, ์šฐ๋ฆฌ๋Š” ํ”„๋ฆฌ๋ Œ๋” ๋•Œ์™€์˜ ์‹ฑํฌ๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์ž์œ ๋กญ๊ฒŒ ์œˆ๋„์šฐ ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ด๋‹ค! ๐Ÿฅฒ

(๊ทผ๋ฐ ์ด๊ฑธ ํ•ด๊ฒฐ์ด๋ผ๊ณ  ์จ๋†“๊ณ  ์˜ค๋Š˜ ์šด๋™๋‹ค๋…€์˜ค๋ฉด์„œ ์ƒ๊ฐํ•ด๋ณด๋‹ˆ๊นŒ, ์ด๋Ÿฌ๋ฉด SSR์˜ ์˜๋ฏธ๊ฐ€ ์—†์–ด์ง€๋Š”๊ฒŒ ์•„๋‹Œ๊ฐ€ ํ•˜๋Š” ์ƒ๊ฐ๋„ ๋“ ๋‹ค. pํƒœ๊ทธ๋ฅผ ๋ฌธ์ œ ์‚ผ๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋˜๋ฐ, ๊ทธ ์—ญ์‹œ ์‹œ๋„ํ•ด๋ด์•ผ๊ฒ ๋‹ค...)

NextJS 12 New Feature ์ ์šฉ์—†์ด ์šฐ์„  ์—…๊ทธ๋ ˆ์ด๋“œ์— ์„ฑ๊ณต...!

์‚ฌ์‹ค ๋Œ€๋ถ€๋ถ„์˜ NextJS 12 ์—…๊ทธ๋ ˆ์ด๋“œ ํฌ์ŠคํŒ…๋“ค์€ ์ด๋Ÿฌ์ด๋Ÿฌํ•œ New Feature๊ฐ€ ๋‚˜์™”๊ณ , ์ด๊ฒƒ๋“ค์„ ์ ์šฉํ•ด๋ณด์•˜๋‹ค! ์˜ ๋‚ด์šฉ์ธ๋ฐ, ๋‚˜๋Š” ์—…๊ทธ๋ ˆ์ด๋“œ๋ฅผ ํ•˜๊ณ , ์ œ๋Œ€๋กœ ๋ Œ๋”๋ง ๋˜๊ฒŒ๋” ๋งŒ๋“ค๊ธฐ ์œ„ํ•œ ์—๋Ÿฌ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋‚ด์šฉ์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ํšŒ๊ณ ๋ฅผ ๊ฝ‰ ์ฑ„์› ๋‹ค. js/๋ฆฌ์•กํŠธ ๊ฐœ๋ฐœ์ž์ด์ง€๋งŒ ๋ฆฌ์•กํŠธ์˜ ๋ Œ๋”๋ง ๊ณผ์ •, ํ˜น์€ hydration๊ณผ ๊ฐ™์ด ์กฐ๊ธˆ๋งŒ ๋ณต์žกํ•œ ๊ฐœ๋…์ด ๋‚˜์˜ค๋ฉด ๋ฐ”๋กœ ๋จธ๋ฆฌ๊ฐ€ ์•„ํŒŒ์˜ค๋Š” ๊ฒƒ์ด ์‚ฌ์‹ค์ด๋‹ค. ๋งค๋ฒˆ ์ œ๋Œ€๋กœ ๊ณต๋ถ€ํ•ด์•ผ์ง€ ๋‹ค์งํ•˜๊ณ  ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ผœ๊ณคํ•˜์ง€๋งŒ, ๊ณต์‹๋ฌธ์„œ ํ˜น์€ ๋ธ”๋กœ๊ทธ ์ž๋ฃŒ๋“ค์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‚ด์šฉ์€ ์ฝ๊ณ ์žˆ์ž๋ฉด ์ข€ ๋‚˜์™€ ๋™๋–จ์–ด์ง„ ๋‚ด์šฉ๊ฐ™๊ณ ... ๊ทธ๋ ‡๋‹ค... ์ด๋ ‡๊ฒŒ ์‹ค๋ฌด์™€ ๋งž๋‹ฟ์•„ ์žˆ์„ ๋•Œ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์ด ๊ทธ๋ž˜๋„ ๊ฐ€์žฅ ํฐ ๋„์›€์ด ๋˜๋Š” ๋“ฏํ•˜๋‹ค... ๊ธฐ์–ต์—๋„ ๋งŽ์ด ๋‚จ๊ณ .

New Feature๋Š” ์ด์ œ๋ถ€ํ„ฐ ์ ์šฉํ† ๋ก ํ•˜๊ฒ ์๋‹ˆ๋‹ค~

์ฐธ๊ณ  ์ž๋ฃŒ

https://nextjs.org/docs/messages/react-hydration-error
https://velog.io/@huurray/React-Hydration-%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC

profile
๋ถ€์ •ํ™•ํ•œ ์ •๋ณด๋‚˜ ์ž˜๋ชป๋œ ์ •๋ณด๋Š” ๋Œ“๊ธ€๋กœ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ๋น ๋ฅด๊ฒŒ ์ˆ˜์ •ํ† ๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

4๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2022๋…„ 9์›” 3์ผ

์ž˜ ์ฝ๊ณ  ๊ฐ‘๋‹ˆ๋‹ค!!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2022๋…„ 9์›” 5์ผ

์™€ ๋„ˆ๋ฌด ์œ ์šฉํ•ด์š”
์ €ํฌ ํšŒ์‚ฌ ์›น์‚ฌ์ดํŠธ์—๋„ ์ ์šฉํ•ด๋ด์•ผ๊ฒ ์–ด์š”
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ^.^

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2022๋…„ 9์›” 20์ผ

์–ด๋งˆ๋ฌด์‹œํ•˜๋„ค์š”!

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ
comment-user-thumbnail
2022๋…„ 10์›” 26์ผ

์•ˆ๋…•ํ•˜์„ธ์š”. ํ•ตํด์—์„œ ๊ฐœ๋ฐœ ์ค‘์ธ ๊ถŒ์˜ค๋นˆ์ž…๋‹ˆ๋‹ค.
์ž‘์„ฑํ•˜์‹  ๋ธ”๋กœ๊ทธ๊ธ€ ์ž˜ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค!

์ด๋ฒˆ์— ์ €ํฌ SDK๊ฐ€ 11.3.1 ๋กœ ์—…๋ฐ์ดํŠธ ๋˜๋ฉด์„œ ๊ธฐ์กด ์ฒ˜๋Ÿผ ๋ถ„๊ธฐ ์—†์ด ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ๋„๋ก ์—…๋ฐ์ดํŠธ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

props์— supportSSR๋งŒ ๋„ฃ์–ด์ฃผ์‹œ๋ฉด, ๊ธฐ์กด์ฒ˜๋Ÿผ ๋ณ„๋„ ๋ถ„๊ธฐ ์—†์ด ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง€๋„๋ก ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. :)
Hydrate ๋ฌธ์ œ๋„ ๋‚ด๋ถ€์ ์œผ๋กœ
useLoadableVariation, useLoadableFeature ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ, isLoading ๊ฐ’์— ๋”ฐ๋ผ์„œ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๋ถ€๋ถ„์—์„œ isLoading ์ด true ์ธ ๊ฒฝ์šฐ์—๋งŒ ๊ฐ’์„ ํ™œ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ๋„๋ก ํ•˜์—ฌ SSR ํ™˜๊ฒฝ์—์„œ ๋”์šฑ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒ€ํ†  ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค. :)

๋‹ต๊ธ€ ๋‹ฌ๊ธฐ