๐Ÿ’ป๐Ÿ’€/๊ฐœ๋ฐœ

[Monorepo] Jest ์‹คํ–‰ ๋•Œ ์ž๊พธ SyntaxError๊ฐ€ ๋‚œ๋‹ค๋ฉด Babel ์„ค์ •์„ ๋‹ค์‹œ ํ•ด๋ณด์ž

db2 2022. 7. 24. 17:08

SyntaxError: Cannot use import statement outside a module

Monorepo ๊ตฌ์กฐ์˜ ํ”„๋กœ์ ํŠธ์—์„œ Jest๋ฅผ ๋„์ž…ํ•˜๋Š” ์ดˆ๊ธฐ ๊ณผ์ •์—์„œ ์ด๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ๋‚ฌ๋‹ค.

์ด๋ ‡๊ฒŒ import ํ•  ๋•Œ๋ถ€ํ„ฐ ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค๋ฉด Babel์ด ํŠธ๋žœ์ŠคํŒŒ์ผ๋ง์„ ๋ชปํ•˜๊ณ  ์žˆ๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Webpack ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ๋นŒ๋“œํ•  ๋• ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ๋Š”๋ฐ ์™œ Jest๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์ด๋Ÿฐ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š” ๊ฑธ๊นŒ? ๋จผ์ € Monorepo์—์„œ์˜ Babel ์„ค์ •์— ๋Œ€ํ•ด ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

Monorepo์—์„œ Babel ์„ค์ •ํ•˜๊ธฐ

Monorepo์—์„œ๋Š” ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์˜ ๋ฃจํŠธ์— babel.config.json ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์„œ ํ•ต์‹ฌ ์„ค์ •์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. .babelrc.json ํŒŒ์ผ์„ ํ†ตํ•ด ๊ฐ ํ•˜์œ„ ํŒจํ‚ค์ง€์—์„œ ๊ฐœ๋ณ„ ์„ค์ •์„ ํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, ์ด ๋•Œ๋„ ๋ฃจํŠธ ๋ ˆ๋ฒจ์˜ ์˜ต์…˜์„ ๋จผ์ € ์„ค์ •ํ•œ๋’ค overrides ์˜ต์…˜์„ ์‚ฌ์šฉํ•ด ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. Babel 7๋ถ€ํ„ฐ ์ด๋Ÿฐ ๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ์˜ ๊ฐœ๋…์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์œผ๋ฉฐ, Babel์€ ์ด ๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ babel.config.json ํŒŒ์ผ(.js, .cjs, .mjs ํ™•์žฅ์ž ์ง€์›)์„ ์ž๋™์œผ๋กœ ๊ฒ€์ƒ‰ํ•ด ์ ์šฉํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ Monorepo์—์„œ์˜ Babel์€ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์˜ ๋ฃจํŠธ๊ฐ€ ์•„๋‹Œ, Babel์ด ์‹คํ–‰๋˜๋Š” ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋…ผ๋ฆฌ์ ์ธ ๋ฃจํŠธ๋กœ์„œ ์ทจ๊ธ‰ํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ํ•˜์œ„ ํŒจํ‚ค์ง€์—์„œ Babel ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋Š” ๊ฒฝ์šฐ, ํ•ด๋‹น ํŒจํ‚ค์ง€๊ฐ€ ๋ฃจํŠธ๋กœ ์ทจ๊ธ‰๋˜์–ด ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์˜ ๋ฃจํŠธ๊ฐ€ ์•„๋‹Œ ํŒจํ‚ค์ง€ ๋‚ด์—์„œ babel.config.json ํŒŒ์ผ์„ ์ฐพ๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์€ SyntaxError๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๋•Œ๋Š” Babel์—๊ฒŒ ์–ด๋””์„œ ์„ค์ • ํŒŒ์ผ์„ ์ฐพ์•„์•ผํ•˜๋Š”์ง€ ์•Œ๋ ค์ค˜์•ผ ํ•œ๋‹ค. ๊ณต์‹๋ฌธ์„œ์—์„œ ์ถ”์ฒœํ•˜๋Š” ๋ฐฉ๋ฒ•์€ rootMode ์˜ต์…˜์„ upward๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด Babel์ด ์ž‘์—… ๋””๋ ‰ํ„ฐ๋ฆฌ์—์„œ๋ถ€ํ„ฐ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ ๋ฃจํŠธ ๋ฐฉํ–ฅ์œผ๋กœ babel.config.json ํŒŒ์ผ์„ ์ฐพ๊ณ , ํŒŒ์ผ์„ ์ฐพ์€ ๊ทธ ์œ„์น˜๋ฅผ root ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒŒ ๋œ๋‹ค.

Webpack์—์„œ Babel Config ํŒŒ์ผ ๋ถ„๋ฆฌํ•˜๊ธฐ

๋‚ด Monorepo์˜ ๊ตฌ์กฐ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค. ๋ฒˆ๋“ค๋Ÿฌ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” Webpack์˜ ๊ณตํ†ต ์„ค์ •์„ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ ๋ฃจํŠธ์˜ webpack.base.js ์— ์ •์˜ํ•˜๊ณ ,
๊ฐ ํ•˜์œ„ ํŒจํ‚ค์ง€์˜ webpack.config.js์—์„œ ๋จธ์ง€ํ•œ ๋’ค ํŒจํ‚ค์ง€๋งˆ๋‹ค ๋…๋ฆฝ์ ์œผ๋กœ Webpack ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

webpack.base.js
packages/
  app1/
    src/index.js
    webpack.config.js
    package.json
  app2/
    src/index.js
    webpack.config.js
    package.json

์ด ๋•Œ Babel ์„ค์ •์„ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜์ง€ ์•Š๊ณ  Webpack์˜ ์„ค์ •์— ํ•จ๊ป˜ ์ •์˜๋ฅผ ํ–ˆ์—ˆ๋Š”๋ฐ Jest๊ฐ€ ์ด ๋•Œ๋ฌธ์— Babel ์„ค์ •์„ ์ œ๋Œ€๋กœ ์ฐพ์ง€ ๋ชปํ•œ๋‹ค๋Š” ์˜์‹ฌ์ด ๋“ค์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ Webpack ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋• ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ์ง€๋งŒ Jest ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋๋˜ ๊ฒŒ ์•„๋‹๊นŒ ์ƒ๊ฐํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ Babel ์„ค์ •์„ ๋ณ„๋„์˜ babel.config.js ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

์ด๋ ‡๊ฒŒ babel-loader์˜ options ํ”„๋กœํผํ‹ฐ์— ์ •์˜ํ–ˆ๋˜ Babel ์„ค์ •์„

// webpack.base.js

module: {
  rules: [
    {
      test: /\.[jt]sx?$/,
      use: [
        {
          loader: 'babel-loader',
          options: {
            presets: [ ... ],
            plugins: [ ... ],
          },
        },
      ],
    },
  ]
}

์•„๋ž˜์ฒ˜๋Ÿผ ๋ถ„๋ฆฌํ•ด์คฌ๋‹ค. ์ด ๋•Œ ์ฝ”๋“œ์ƒ์—์„œ development mode์ผ ๋•Œ ๋ณ„๋„ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ด์„œ .json์ด ์•„๋‹ˆ๋ผ.js ํŒŒ์ผ๋กœ ์ž‘์„ฑํ–ˆ๋‹ค.

// babel.config.js
const isDevelopment = process.env.NODE_ENV === 'development'

const config = (api) => {
    api.cache(true)

    const presets = [ ... ]
    const plugins = [
        'some plugin',
        isDevelopment && 'other plugin',
    ].filter(Boolean)

    return {
        presets,
        plugins,
    }
}

module.exports = config

๊ทธ๋Ÿฐ ๋’ค Webpack์˜ babel-loader์—๊ฒŒ ๋ฃจํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ ๋ฃจํŠธ๋กœ ๋ณ€๊ฒฝํ•˜๋ผ๊ณ  ์•Œ๋ ค์ฃผ๋ฉด ๋œ๋‹ค.

// webpack.base.js

module: {
  rules: [
    {
      test: /\.[jt]sx?$/,
      use: [
        {
          loader: 'babel-loader',
          options: {
            rootMode: 'upward', // ์ด ์˜ต์…˜์ด ์—†์œผ๋ฉด ๋ฃจํŠธ์˜ babel.config.js ํŒŒ์ผ์„ ์ฐพ์ง€ ๋ชปํ•œ๋‹ค
          },
        },
      ],
    },
  ]
}

๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹ˆ๋‹ค! Jest๋„ babel์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์œ„์น˜์— ๋”ฐ๋ผ์„œ ๋ณ„๋„์˜ ์„ค์ •์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‚œ ์ด ๋ถ€๋ถ„์—์„œ ์‚ฝ์งˆ์„ ๊ฒ๋‚˜ ํ–ˆ๋‹ค.

Jest๋Š” babel-jest๋ฅผ ์‚ฌ์šฉํ•ด ํŠธ๋žœ์ŠคํŒŒ์ผ๋งํ•œ๋‹ค

Jest๋Š” ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์— JSX, TypeScript์™€ ๊ฐ™์€ ์ฝ”๋“œ๋Š” JS๋กœ ํŠธ๋žœ์ŠคํŒŒ์ผ๋ง์ด ํ•„์š”ํ•˜๋‹ค. ๊ธฐ๋ณธ์ ์œผ๋กœ jest๋ฅผ ์„ค์น˜ํ•˜๋ฉด babel-jest๊ฐ€ ์„ค์น˜๋˜๋ฉฐ, ์ž๋™์ ์œผ๋กœ ์ด๋ฅผ transformer๋กœ ์‚ฌ์šฉํ•ด ํ”„๋กœ์ ํŠธ์— ์กด์žฌํ•˜๋Š” Babel ์„ค์ •์„ ์ฐพ์•„ .js, .jsx, .ts, .tsx ํŒŒ์ผ์„ ํŠธ๋žœ์ŠคํŒŒ์ผ๋งํ•œ๋‹ค.

๋”ฐ๋ผ์„œ Jest ์„ค์ •์˜ transform ์˜ต์…˜์˜ ๊ธฐ๋ณธ๊ฐ’์€ {"\\.[jt]sx?$": "babel-jest"}์ด๋ฉฐ transformer์— ๋ณ„๋„์˜ ์˜ต์…˜์ด ํ•„์š”ํ•˜๋‹ค๋ฉด {"\\.[jt]sx?$": ["babel-jest", {options}]}์™€ ๊ฐ™์ด ํŠœํ”Œ ํ˜•ํƒœ๋กœ ๋‘ ๋ฒˆ์งธ ์ธ์ž์— ์˜ต์…˜์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

โญ๏ธ babel-jest์˜ rootMode ์˜ต์…˜

Jest๋ฅผ Monorepo์˜ ๋ฃจํŠธ์— ์„ค์น˜ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋ฉด ๋ณ„๋„์˜ ์„ค์ •์—†์ด๋„ ์ž˜ ๋  ๊ฑฐ๋ผ๊ณ  ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋‚˜์ฒ˜๋Ÿผ ํ•˜์œ„ ํŒจํ‚ค์ง€์— Jest๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” babel-loader์—๊ฒŒ rootMode ์˜ต์…˜์„ ์„ค์ •ํ•ด์คฌ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ babel-jestํ•œํ…Œ๋„ ๋™์ผํ•œ ์˜ต์…˜์„ ์„ค์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค!

babel.config.json
webpack.base.js
packages/
  app1/
    src/index.js
    jest.config.js    #app1 ํŒจํ‚ค์ง€์—์„œ๋งŒ jest๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ๊ตฌ์กฐ
    webpack.base.js
    package.json    

์™œ๋ƒ๋ฉด ํ•˜์œ„ ํŒจํ‚ค์ง€์—์„œ babel-jest๋กœ ์ธํ•ด Babel ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰๋  ๊ฑฐ๊ณ , Babel ํŠน์„ฑ์ƒ ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ ๋ฃจํŠธ๊ฐ€ ์•„๋‹Œ ํ•ด๋‹น ํŒจํ‚ค์ง€๋ฅผ ๋ฃจํŠธ๋กœ ์ทจ๊ธ‰ํ•˜๊ณ  babel.config.json์„ ์ฐพ์œผ๋ ค๊ณ  ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ๋ž˜์„œ babel-jest์—๊ฒŒ๋„ ์„ค์ • ํŒŒ์ผ์„ ์ฐพ์•„์•ผํ•˜๋Š” ์œ„์น˜๋ฅผ ์•Œ๋ ค์ค˜์•ผ ํ•œ๋‹ค! ์žŠ์ง€ ๋ง์ž!!

// jest.config.js 

 transform: {
   '^.+\\.[jt]sx?$': ['babel-jest', { rootMode: 'upward' }],
 },