Next.js: λͺ¨λ“ˆμ„ 찾을 수 μ—†μŒ: 'fs'λ₯Ό 확인할 수 μ—†μŠ΅λ‹ˆλ‹€.

에 λ§Œλ“  2019λ…„ 07μ›” 05일  Β·  13μ½”λ©˜νŠΈ  Β·  좜처: vercel/next.js

버그 μ‹ κ³ 

버그 μ„€λͺ…

λ‚˜λŠ” 인기가 μ‚¬μš©ν•˜κ³  발견 μ—… 이 NPM νŒ¨ν‚€μ§€, μœ„μΉ˜ 경둜 μ˜μ‘΄μ„± 등을. locate-path λŠ” μ½”λ“œ 내에 fs κ°€ ν•„μš”ν•©λ‹ˆλ‹€.

λ‚΄ 앱을 μ‹€ν–‰ν•˜λ €κ³  ν•˜λ©΄ λ‹€μŒ 였λ₯˜ λ©”μ‹œμ§€κ°€ λ‚˜νƒ€λ‚©λ‹ˆλ‹€.

[ error ] ./node_modules/locate-path/index.js
Module not found: Can't resolve 'fs' in 'C:\...\node_modules\locate-path'
Could not find files for /index in .next/build-manifest.json
Promise { <pending> }
ModuleNotFoundError: Module not found: Error: Can't resolve 'fs' in 'C:\...\node_modules\locate-path'
    at factory.create (C:\...\node_modules\webpack\lib\Compilation.js:823:10)
    at factory (C:\...\node_modules\webpack\lib\NormalModuleFactory.js:397:22)
    at resolver (C:\...\node_modules\webpack\lib\NormalModuleFactory.js:130:21)
    at asyncLib.parallel (C:\...\node_modules\webpack\lib\NormalModuleFactory.js:224:22)
    at C:\...\node_modules\neo-async\async.js:2830:7
    at C:\...\node_modules\neo-async\async.js:6877:13
    at normalResolver.resolve (C:\...\node_modules\webpack\lib\NormalModuleFactory.js:214:25)
    at doResolve (C:\...\node_modules\enhanced-resolve\lib\Resolver.js:184:12)
    at hook.callAsync (C:\...\node_modules\enhanced-resolve\lib\Resolver.js:238:5)
    at _fn0 (eval at create (C:\...\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:15:1)
    at resolver.doResolve (C:\...\node_modules\enhanced-resolve\lib\UnsafeCachePlugin.js:37:5)
    at hook.callAsync (C:\...\node_modules\enhanced-resolve\lib\Resolver.js:238:5)
    at _fn0 (eval at create (C:\...\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:15:1)
    at hook.callAsync (C:\...\node_modules\enhanced-resolve\lib\Resolver.js:238:5)
    at _fn0 (eval at create (C:\...\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:27:1)
    at resolver.doResolve (C:\...\node_modules\enhanced-resolve\lib\DescriptionFilePlugin.js:42:38)

λ‚˜λŠ” μœ„μΉ˜ μ°ΎκΈ° 리포지토리

μž¬ν˜„ν•˜κΈ° μœ„ν•΄

https://github.com/TidyIQ/nextjs-issueλ₯Ό λ³΅μ œν•˜κ³  npm run dev ν•©λ‹ˆλ‹€.

μ˜ˆμƒλ˜λŠ” 행동

문제 μ—†μŒ

μ‹œμŠ€ν…œ 정보

  • 운영 체제: μœˆλ„μš° 10
  • Next.js 버전: 8.1.1-canary.67

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

μ΅œμ‹  Next.js μ—…λ°μ΄νŠΈ(9.4+)

getStaticProps λ˜λŠ” getServerSideProps λ‚΄μ—μ„œ fs μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•  수 있으며 μΆ”κ°€ ꡬ성이 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ . 데이터 수λͺ… μ£ΌκΈ°μ—μ„œ λ³€μˆ˜λ₯Ό μ°Έμ‘°ν•˜μ—¬ μ˜¬λ°”λ₯΄κ²Œ νŠΈλ¦¬κ°€ 흔듀릴 수 μžˆλ„λ‘ ν•˜μ‹­μ‹œμ˜€.

이 도ꡬ λ₯Ό μ‚¬μš©ν•˜μ—¬ μž‘λ™ 방식을 μ‹œκ°μ μœΌλ‘œ 배울 수 μžˆμŠ΅λ‹ˆλ‹€!

getInitialProps 둜 λ ˆκ±°μ‹œ Next.js 버전을 계속 κ΅¬μΆ•ν•˜κ³  μžˆλ‹€λ©΄ μ•„λž˜λ₯Ό μ½μ–΄λ³΄μ„Έμš” πŸ‘‡


제곡된 μ½”λ“œκ°€ μœ νš¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이 νŒŒμΌμ€ λ Œλ”λ§ν•˜λŠ” λ™μ•ˆ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ μ‚¬μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

https://github.com/TidyIQ/nextjs-issue/blob/aef67b12d91d299d0978550005a40cbb34f74b71/pages/index.js#L5

_μ„œλ²„μ—μ„œ_ν•˜λŠ” λ™μ•ˆμ—λ§Œ FS κ΄€λ ¨ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŒμ„ κΈ°μ–΅ν•˜μ‹­μ‹œμ˜€. 즉, λ Œλ”λ§ν•˜λŠ” λ™μ•ˆ fs λ₯Ό μ‚¬μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

fs λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 getInitialProps 내에 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.

λΉŒλ“œν•  ν΄λΌμ΄μ–ΈνŠΈ λ²ˆλ“€μ„ κ°€μ Έμ˜€λ €λ©΄ λ‹€μŒ μ½˜ν…μΈ λ‘œ next.config.js νŒŒμΌμ„ 생성해야 ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

module.exports = {
  webpack: (config, { isServer }) => {
    // Fixes npm packages that depend on `fs` module
    if (!isServer) {
      config.node = {
        fs: 'empty'
      }
    }

    return config
  }
}

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

μ΅œμ‹  Next.js μ—…λ°μ΄νŠΈ(9.4+)

getStaticProps λ˜λŠ” getServerSideProps λ‚΄μ—μ„œ fs μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•  수 있으며 μΆ”κ°€ ꡬ성이 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€ . 데이터 수λͺ… μ£ΌκΈ°μ—μ„œ λ³€μˆ˜λ₯Ό μ°Έμ‘°ν•˜μ—¬ μ˜¬λ°”λ₯΄κ²Œ νŠΈλ¦¬κ°€ 흔듀릴 수 μžˆλ„λ‘ ν•˜μ‹­μ‹œμ˜€.

이 도ꡬ λ₯Ό μ‚¬μš©ν•˜μ—¬ μž‘λ™ 방식을 μ‹œκ°μ μœΌλ‘œ 배울 수 μžˆμŠ΅λ‹ˆλ‹€!

getInitialProps 둜 λ ˆκ±°μ‹œ Next.js 버전을 계속 κ΅¬μΆ•ν•˜κ³  μžˆλ‹€λ©΄ μ•„λž˜λ₯Ό μ½μ–΄λ³΄μ„Έμš” πŸ‘‡


제곡된 μ½”λ“œκ°€ μœ νš¨ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 이 νŒŒμΌμ€ λ Œλ”λ§ν•˜λŠ” λ™μ•ˆ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ μ‚¬μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

https://github.com/TidyIQ/nextjs-issue/blob/aef67b12d91d299d0978550005a40cbb34f74b71/pages/index.js#L5

_μ„œλ²„μ—μ„œ_ν•˜λŠ” λ™μ•ˆμ—λ§Œ FS κ΄€λ ¨ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŒμ„ κΈ°μ–΅ν•˜μ‹­μ‹œμ˜€. 즉, λ Œλ”λ§ν•˜λŠ” λ™μ•ˆ fs λ₯Ό μ‚¬μš©ν•  수 μ—†μŠ΅λ‹ˆλ‹€.

fs λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 getInitialProps 내에 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.

λΉŒλ“œν•  ν΄λΌμ΄μ–ΈνŠΈ λ²ˆλ“€μ„ κ°€μ Έμ˜€λ €λ©΄ λ‹€μŒ μ½˜ν…μΈ λ‘œ next.config.js νŒŒμΌμ„ 생성해야 ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

module.exports = {
  webpack: (config, { isServer }) => {
    // Fixes npm packages that depend on `fs` module
    if (!isServer) {
      config.node = {
        fs: 'empty'
      }
    }

    return config
  }
}

λ‚΄ 둜컬, μ‹€μ§ˆμ μœΌλ‘œ 바닐라 μ„€μΉ˜μ— λ™μΌν•œ λ¬Έμ œκ°€ μžˆμ§€λ§Œ nextjs의 μ˜ˆμ œμ—μ„œλŠ” λ¬Έμ œκ°€ λ˜μ§€ μ•ŠλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€.
https://github.com/zeit/next.js/tree/5787cbd9de33ea9add7cadeb04689b0d4b02976d/examples/blog-starter

ꡬ성 νŒŒμΌμ„ μˆ˜μ •ν•˜μ§€ μ•Šκ³  μž‘λ™ν•˜λŠ” μ΄μœ λŠ” λ¬΄μ—‡μž…λ‹ˆκΉŒ?

getStaticProps / getServerSidePropsλŠ” λΈŒλΌμš°μ € λ²ˆλ“€μ—μ„œ μ œκ±°λ˜λ―€λ‘œ fs 만 μ‚¬μš©ν•˜μ‹­μ‹œμ˜€.

무엇이 λ¬Έμ œμΈμ§€ μ•Œμ•„λƒˆμŠ΅λ‹ˆλ‹€. fs μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜λ₯Ό κ°€μ Έμ˜€μ§€λ§Œ getStaticProps λ‚΄λΆ€μ—μ„œ ν•¨μˆ˜λ₯Ό μ‹€ν–‰/μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄ λΈŒλΌμš°μ € λ²ˆλ“€μ— ν¬ν•¨λ©λ‹ˆλ‹€. ν•¨μˆ˜κ°€ getStaticProps λ‚΄μ—μ„œ 참쑰되면 λΈŒλΌμš°μ € λ²ˆλ“€μ— ν‘œμ‹œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

getStaticPropsμ—μ„œ μ‚¬μš©λ˜μ§€λ§Œ κΈ°λ³Έ λ‚΄λ³΄λ‚΄κΈ°μ—μ„œλŠ” μ‚¬μš©λ˜μ§€ μ•ŠλŠ” κ°€μ Έμ˜€κΈ°λ₯Ό μ œκ±°ν•˜λŠ” μˆ¨κ²¨μ§„ 논리가 μžˆλŠ” 것 κ°™μŠ΅λ‹ˆλ‹€. μž¬ν˜„ν•  수 μžˆμ„ λ•ŒκΉŒμ§€ 디버깅에 λͺ‡ μ‹œκ°„μ„ λ³΄λƒˆμŠ΅λ‹ˆλ‹€. λ¬Έμ„œ μ–΄λ”˜κ°€μ— μ°Έκ³ ν•  κ°€μΉ˜κ°€ μžˆμ„ κ²ƒμž…λ‹ˆλ‹€. :)

νžˆλ“  λ‘œμ§μ€ κ·Έλƒ₯ μ›ΉνŒ©μ˜ 트리 λ–¨λ¦Ό μ•„λ‹Œκ°€μš”? 생각할 λ•Œ μ˜λ―Έκ°€ μžˆμŠ΅λ‹ˆλ‹€. NextλŠ” λΈŒλΌμš°μ € λ²ˆλ“€μ—μ„œ getStaticProps λ₯Ό κ°€μ Έμ˜€μ§€ μ•ŠμœΌλ―€λ‘œ 여기에 μ‚¬μš©λœ κ°€μ Έμ˜¨ κΈ°λŠ₯을 μ œκ±°ν•©λ‹ˆλ‹€.

아무데도 μ°Έμ‘°ν•˜μ§€ μ•ŠμœΌλ©΄ webpack이 λΆ€μž‘μš©μ„ μœ„ν•΄ κ°€μ Έμ˜¨ κ²ƒμœΌλ‘œ κ°„μ£Όν•˜λ―€λ‘œ λͺ¨λ“  λ²ˆλ“€μ— μ—¬μ „νžˆ ν¬ν•¨λ©λ‹ˆλ‹€.

νžˆλ“  λ‘œμ§μ€ κ·Έλƒ₯ μ›ΉνŒ©μ˜ 트리 λ–¨λ¦Ό μ•„λ‹Œκ°€μš”? 생각할 λ•Œ μ˜λ―Έκ°€ μžˆμŠ΅λ‹ˆλ‹€. NextλŠ” λΈŒλΌμš°μ € λ²ˆλ“€μ—μ„œ getStaticProps λ₯Ό κ°€μ Έμ˜€μ§€ μ•ŠμœΌλ―€λ‘œ 여기에 μ‚¬μš©λœ κ°€μ Έμ˜¨ κΈ°λŠ₯을 μ œκ±°ν•©λ‹ˆλ‹€.

아무데도 μ°Έμ‘°ν•˜μ§€ μ•ŠμœΌλ©΄ webpack이 λΆ€μž‘μš©μ„ μœ„ν•΄ κ°€μ Έμ˜¨ κ²ƒμœΌλ‘œ κ°„μ£Όν•˜λ―€λ‘œ λͺ¨λ“  λ²ˆλ“€μ— μ—¬μ „νžˆ ν¬ν•¨λ©λ‹ˆλ‹€.

μ•„λ‹ˆμš”, μ›ΉνŒ© 트리 쉐이킹은 μ΄λŸ¬ν•œ 내보내기λ₯Ό 그런 μ‹μœΌλ‘œ 흔듀 만큼 μ •κ΅ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 트리 쉐이킹 getStaticProps / getServerSideProps / getStaticPathsλŠ” μš°λ¦¬κ°€ λ§Œλ“  이 μ»€μŠ€ν…€ Babel ν”ŒλŸ¬κ·ΈμΈμ— μ˜ν•΄ μ²˜λ¦¬λ©λ‹ˆλ‹€: https://github.com/vercel/next.js/blob/canary/packages/next/build/babel/plugins/next-ssg-transform .ts

였 포인터 μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€. μ›ΉνŒ©μ„ κ³ΌλŒ€ 평가 ν•œ 것 κ°™μŠ΅λ‹ˆλ‹€. :)

이것이 _within_ getServerSideProps μ—μ„œ μΌμ–΄λ‚˜λŠ” μ΄μœ κ°€ λ¬΄μ—‡μž…λ‹ˆκΉŒ?

μ•ˆλ…• λͺ¨λ‘. @aloukissas 처럼 λ‚΄ index.js getStaticProps() λ‚΄μ—μ„œ dotenv μ‚¬μš©ν•  λ•Œλ„ λ™μΌν•œ λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. @Timer 초기 주석과 같이 next.config.js νŒŒμΌμ„ μΆ”κ°€ν•˜λ©΄ ν•΄κ²°λ©λ‹ˆλ‹€.

μ™œ 이런 일이 μΌμ–΄λ‚˜λŠ”μ§€ λ‹¨μ„œκ°€ μžˆμŠ΅λ‹ˆκΉŒ? λ‚˜λŠ” Next.js v.9.4.0을 λΆ€λ₯΄κ³  μžˆλ‹€

감사 ν•΄μš”!

fast-glob λ₯Ό) μ‚¬μš©ν•˜λŠ” λ™μ•ˆ 이 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

이 ν›Œλ₯­ν•œ 도ꡬ λ₯Ό μ‚¬μš©ν•˜μ—¬ ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ λ²ˆλ“€λ‘œ μ œκ³΅λ˜λŠ” μ½”λ“œλ₯Ό μ΄ν•΄ν–ˆμŠ΅λ‹ˆλ‹€.

결과적으둜 λ‚΄λΆ€μ μœΌλ‘œ fs λ₯Ό μ‚¬μš©ν•˜λŠ” fast-glob λ₯Ό μ‚¬μš©ν•˜λŠ” νŒŒμΌμ—μ„œ λ³€μˆ˜λ₯Ό κ°€μ Έμ˜€κ³  μžˆμ—ˆμ§€λ§Œ getStaticProps λ‚΄λΆ€μ˜ μ–΄λŠ κ³³μ—μ„œλ„ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šμ•˜μœΌλ―€λ‘œ 파일이 fast-glob κ°€μ Έμ˜΅λ‹ˆλ‹€

예:

mdxUtils.js

import glob from 'fast-glob'
import path from 'path'

export const BLOG_PATH = path.join(process.cwd(), 'posts')
export const blogFilePaths = glob.sync(`${BLOG_PATH}/blog/**/*.mdx`)

index.js

import { BLOG_PATH, blogFilePaths } from './mdxUtils'

export const getStaticProps = () => {
  const posts = blogFilePaths.map((filePath) => {
    ...
  }
  return { props: { posts } }
}

당신은 λ‚΄κ°€ μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”κ±°μ•Ό λ³Ό 수 μžˆλ“―μ΄ BLOG_PATH μ–΄λ””μ—μ„œ index.js ν•˜μ§€λ§Œ μ—¬μ „νžˆ 그것을 κ°€μ Έ. λ‚˜λŠ” blogFilePaths 만 μ‚¬μš©ν•˜κ³  μžˆμœΌλ―€λ‘œμ΄ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

μžμ„Έν•œ λ‚΄μš©μ€ β†’ https://github.com/vercel/next.js/discussions/17138

κ°μ‚¬ν•©λ‹ˆλ‹€ @deadcoder0904 이것은 λ‚΄ μ½”λ“œμž…λ‹ˆλ‹€:

import Layout from '../components/template'
import Main from '../components/main'
import Menu from '../components/menu'
import 'dotenv/config'

export async function getStaticProps () {
  const avatarLocation = process.env.AVATAR_URL
  const avatarTitle = process.env.AVATAR_TITLE

  return {
    props: {
      avatarLocation,
      avatarTitle
    }
  }
}

export default function RenderMainPage ({ avatarLocation, avatarTitle }) {
  return (
    <Layout
      avatarURL={avatarLocation}
      topLeft={<Menu />}
      middle={<Main avatarURL={avatarLocation} avatarTitle={avatarTitle} />}
    />
  )
}

μ–ΈκΈ‰ν•œ λ„κ΅¬λŠ” import 'dotenv/config' κ°€ ν΄λΌμ΄μ–ΈνŠΈ μ½”λ“œμ— ν¬ν•¨λ˜μ–΄ 있으며 μ•„λ§ˆλ„ 였λ₯˜λ₯Ό ν‘œμ‹œν•˜κ³  μžˆλŠ” κ²ƒμœΌλ‘œ λ³΄μž…λ‹ˆλ‹€. λ¬Έμ œλŠ” env λ³€μˆ˜μ—μ„œ 읽기 μœ„ν•΄ ν•„μš”ν•˜λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ•„λ§ˆλ„ λ‹€λ₯Έ 더 λ‚˜μ€ 방법이 μžˆμ„ κ²ƒμž…λ‹ˆλ‹€. μ €λŠ” μ—¬μ „νžˆ Next.jsλ₯Ό 배우고 μžˆμŠ΅λ‹ˆλ‹€. :)

@ig-perez Next v9.4λΆ€ν„° ν™˜κ²½ λ³€μˆ˜λ₯Ό λ‘œλ“œν•˜λŠ” 방법이 λ‚΄μž₯λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€ β†’ https://nextjs.org/docs/basic-features/environment-variables

μ†”λ£¨μ…˜μ„ 찾으렀면 λ¬Έμ„œλ₯Ό μ½λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€. :)

ꡉμž₯ν•©λ‹ˆλ‹€. λ¬Έμ„œμ—μ„œ κ·Έ 뢀뢄을 λ†“μ³€μŠ΅λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€! μ½”λ“œλ₯Ό μ—…λ°μ΄νŠΈν•˜κ² μŠ΅λ‹ˆλ‹€ πŸ‘πŸ½

이 νŽ˜μ΄μ§€κ°€ 도움이 λ˜μ—ˆλ‚˜μš”?
0 / 5 - 0 λ“±κΈ‰