본문 바로가기

React

이슈 14 조금 편하려고 했을 뿐인데 마주친 오류... electron-webpack 배포하기; Uncaught Error: Cannot find module './main'

반응형

오늘은 클라이언트 프로그램 개발이 다 끝나서, 배포를 위해 yarn dist를 시도해봤습니다. 다음과 같은 에러가 발생합니다.

Uncaught Error: Cannot find module './main'

무엇이 잘못되었는지 확인하기 위해 electron-webpack-quick-start로 새로운 프로젝트를 만들었습니다. 기존 코드를 새 프로젝트로 하나하나 옮기다 보니, 모듈을 dynamic import하는 index.js 부분에서 문제가 생기는 것을 확인했습니다. index.js가 어떤 파일인지는 아래 게시글을 보시면 확인하실 수 있습니다.

2020/01/17 - [ElectronWebpack + React] - 이슈 6 단 한 줄! index.js로 내 코드 깔끔하게 import하기

 

이슈 6 단 한 줄! index.js로 내 코드 깔끔하게 import하기

수정 사항 이 코드는 export default class에 대해 작동하지만, export { foo, bar }와 같은 형식에선 작동하지 않습니다. 이에 대한 수정 사항이은 아래 게시글에서 확인하실 수 있습니다. 이슈 11 React.createE..

roomedia.tistory.com

2020/01/31 - [ElectronWebpack + React] - 이슈 11 React.createElement: type is invalid

 

이슈 11 React.createElement: type is invalid

2020/01/17 - [엥휴/Electron-Webpack + React] - 단 한 줄! index.js로 내 코드 깔끔하게 import하기에서 소개한대로, export default 를 통해 각 파일에서 클래스를 내보낸 뒤, index.js 를 통해 import 하다보..

roomedia.tistory.com

제가 여러모로 잘못된 정보를 전파한 것 같네요...

개발할 때는 잘 돌아갔는데 같은 환경에서 배포용 어플리케이션을 돌리면 문제가 생깁니다. 그 이유는 바로,, 경로가 바뀌기 때문입니다. package.json을 살펴보면 yarn dist는 다음과 같은 명령어를 실행합니다.

    // ...
    "scripts": {
    "dev": "electron-webpack dev",
    "compile": "electron-webpack",
    "dist": "yarn compile && electron-builder",
    "dist:dir": "yarn dist --dir -c.compression=store -c.mac.identity=null"
  },
    // ...

바로 위에 yarn compileelectron-webpack을 실행한다고 나와있으니 결국 yarn distnpx electron-webpack && npx electron-builder를 실행하는 것과 같은 효과를 가집니다.

electron-webpack 명령어를 실행하면 프로젝트 폴더 안에 dist 폴더가 생깁니다. 기본 설정을 건드리지 않았다면 구조는 다음과 같습니다. webpack을 통해 단일 파일로 형성됩니다.

webpack에서 사용하는 기본 config는 다음과 같습니다.

{
    context: 'path/to/project/folder',
    devtool: 'eval-source-map',
    externals: [
      'external',
            'libraries'
    ],
    node: { __dirname: true, __filename: true },
    output: {
      filename: '[name].js',
      chunkFilename: '[name].bundle.js',
      libraryTarget: 'commonjs2',
      path: '/path/that/files/in/renderer/folder/are/saved'
    },
    target: 'electron-renderer',
    resolve: {
      alias: {
        '@': '/path/to/renderer'
      },
      extensions: [ '.js', '.json', '.node', '.css' ]
    },
        // ...
  }

개발 환경에서는 현재 디렉토리 경로를 가리키던 __dirname 변수가 배포 환경에서는 앱 내부 주소를 가리킵니다. (macOS 기준 /path/to/project/folder/dist/mac/test.app/Contents/Resources/app.asar)

파일이 유동적으로 변하는 개발 환경에서는 기존 방식을 사용하고, 배포 환경에서는 하나하나 import 하기로 결정했습니다.

const glob = require('glob')
const path = require('path')
const isDevelopment = process.env.NODE_ENV !== 'production'

let files
module.exports = {}

if (isDevelopment) {
  files = glob.sync(`./${__dirname}/!(index).js`)
}
else {
  files = ['module1', 'module2']
}

for (const file of files) {
  const m = require(`./${path.basename(file)}`)
  for (const ele of Object.values(m)) {
    module.exports[ele.name] = ele
  }
}

와 해결! 난 천재다!! 이런 결과면 얼마나 좋겠습니까... 다시 yarn dist를 진행한 후 켜보면 다음과 같이 나옵니다.

Error: Minified React error #130; visit https://reactjs.org/docs/error-decoder.html?invariant=130&args[]=undefined&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at Error (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:273)
    at ch (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:143)
    at C (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:146)
    at z (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:150)
    at gh (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:172)
    at R (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:263)
    at gk (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:230)
    at fk (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:229)
    at Zj (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:223)
    at Lj (webpack:/node_modules/react-dom/cjs/react-dom.production.min.js:214)

console에 찍어보면 undefined라고 나옵니다. 왜일까요? webpack 때문에 클래스 이름이 바뀌어서 그렇습니다.

f, s 등으로 바뀌어버린 클래스 이름...

아아.. 결국 일일이 찍어주는 수 밖에 없나 봅니다...

module.exports = {}
const isDevelopment = process.env.NODE_ENV !== 'production'

if (isDevelopment) {
  const glob = require('glob')
  const path = require('path')
  const files = glob.sync(`./${__dirname}/!(index).js`)

  for (const file of files) {
    const m = require(`./${path.basename(file)}`)
    for (const ele of Object.values(m)) {
      module.exports[ele.name] = ele
    }
  }
}
else {
  const makeModule = (name, module=[]) => ({
    name: name,
    module: module,
  })

  const files = [
    makeModule("Info"),
    makeModule("Seat"),
    makeModule("Title"),
    makeModule("SeatID"),
    makeModule("Caution"),
    makeModule("Feedback"),
    makeModule("Password"),
    makeModule("StudentID"),
    makeModule("Navigation"),
    makeModule("SelectSeat"),
  ]

  for (const file of files) {
    const m = require(`./${file.name.toLowerCase()}`)
    module.exports[file.name] = m.default
    for (const c of file.module) {
      module.exports[c] = m[c]
    }
  }
}

  • package.json에 아래와 같은 코드를 추가하면 임포트 할 때 import { module1 } from 'common'과 같은 식으로 쓸 수 있다!!! 맨날 상대경로로만 사용했는데 절대경로로 추가할 수 있게 되어 기쁘다...

      // ...
      "electronWebpack": {
      "commonSourceDirectory": "src/components",
    }
      // ...
반응형