본문 바로가기

신나는 오류해결파티!!!

vanilla JS esbuild로 번들링 해보기 (feat: cp html, css)

하려고 하는 것 

 

 

esbuild를 활용해서 

js파일을 번들링 하여 server로 제공하고,

html파일도 함께 build하여 정적으로 제공하고자 함.

 

브라우저에 해당 포트로 접속하게 되면 server에서 

static파일인 html파일을 제공

 

특정 동작이 일어나게 되면 그것에 맞게 js로 이벤트를 리스닝 하여 동작하도록

 

  • 수정 전의 build명령어 
  • "scripts": { "dev": "nodemon --watch src --exec 'yarn build && yarn start'", "build": "esbuild src/index.js --bundle --outdir=dist --platform=node --format=esm && cp src/index.html dist/", "start": "node dist/index.js" },

 

 

  • 폴더구조

src
 ┣ UI
 ┃ ┗ index.js
 ┣ domain
 ┃ ┗ index.js
 ┣ utils
 ┃ ┗ util.js
 ┣ app.js
 ┣ index.css
 ┣ index.html
 ┣ index.js
 ┗ template.js

 

 

만난 문제 

build 하여 정적 파일을 dist폴더로 output하는 것 까지는 성공하였다. 

그런데 localhost:3000 실행중 

console에

ReferenceError: require is not defined
라는 에러가 발생함.

 

 

 

$ esbuild src/index.js --bundle --outdir=dist --platform=node --format=esm && cp src/index.html dist/

  dist/index.js  1.1mb ⚠️

warning ../../../package.json: No license field
$ node dist/index.js
file:///Users/jennerh/jennerH/whatever/js-number-up-and-down/dist/index.js:11
  throw Error('Dynamic require of "' + x + '" is not supported');
        ^

Error: Dynamic require of "path" is not supported




위와같은 에러문구와 함께 build가 되지 않았음. 
  
이 에러문구를 찾아보았을 때 commonjs방식을 사용하면 된다고 해서 
commonjs방식과 format을 cjs로 변경하였었음. 

파악하지 못했던 것
1. 현재 esbuild로 번들링 하려고 하는 것이 클라이언트인가 서버인가? 
2. 브라우저 실행 코드 vs 노드 환경 실행 코드

 

 

해결해보기

1. 클라이언트와 서버의 번들을 분리해보기 

- server를 시작했을 때 실행되어야 할 것은 index.js 서버 번들 

   

- '/'로 접속했을 때 제공되는 html파일에서 제공되어야 할 것은 클라이언트에서 사용될 번들

 

2.

1)script 분석해보기 

 

"scripts": {
    "dev": "nodemon --watch src --exec 'yarn build && yarn start'",
    "build": "esbuild src/index.js --bundle --outdir=dist --platform=node --format=esm && cp src/index.html dist/",
    "start": "node dist/index.js"
  },

 

'dev' : nodemon을 활용해 src폴더를 모니터링하고 있다가. yarn build와 yarn start를 실행

(nodemon을 활용한 이유는 src 폴더 내부의 파일이 변경되면 build와 start를 다시 실행하도록)

 

'build' : esbuild를 활용해 src/indx.js를 bundle로 dist폴더에 node플랫폼을이용하여 (서버용 빌드) 빌드를 실행함

 

'start' : node 명령어로 dist폴더의 index.js를 실행 

 

 

2) 클라이언트와 서버 빌드 나누기

build에서 하는 build는 서버용이기 때문에, 클라이언트 용 빌드도 따로 해주어야 한다. 

또한 서버에서 제공하는 번들은 cjs로, 클라이언트용 번들은 esmodule로 설정한다.

"build:server": "esbuild src/index.cjs --bundle --outdir=dist --platform=node --format=cjs",
"build:client" : "esbuild src/app.js --bundle --outdir=dist/public --loader:.html=text --loader:.css=file  --platform=browser --format=esm && cp -r public/ dist/public",

 

 

 

3) type을 특정하지 않는다. 

"type" : module #삭제

각각 format으로 지정해준다.

 

 

 

4)directory나누기 

 

서버에서 제공하는 것과 클라이언트에서 제공하는 정적파일의 분리를 위해 directory를 나눈다.

서버는 dist 폴더의 루트로, 클라이언트용은 public하위로.  

 

서버에서 '/'로 클라이언트가 요청했을 때 제공해주는 파일은 아래와 같이 작성 

 

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

 

 

- 의문점 :

  • html파일과 css파일을 빌드 폴더에 copy하는 것이 과연 빌드인가? 

(다른 방법은 없을까? 고민해보기. )

 

 

 

  • 클라이언트용 빌드를 하려면? (app.js파일 및 다른 폴더들 모두 빌드 해야하나? )
    : src/app.js를 빌드하면 app.js가 entrypoint가 되어 해당 파일에서 import한 모듈들을 모두 하나의 번들로 빌드해줌.
    그러므로 명령어에는 entrypoint만 입력하면 된다. 

 

  • commonjs와 esmodule의 차이점
    : commonjs는 node환경에서,  esmodule은 브라우저환경에서
    (require vs import , module.exports vs export)

 

-현재까지 성공한 것과 해결 해야할 점 

- 성공:

    client와 server의 번들을 따로 만들어서 WAS로 빌드된 정적 파일을 제공하는 데 성공하였다.

 

- 해야할 것 : 

   app.js가 실행되지 않고 있다.?? 

   index.html에서 파싱하고 있는 app.js가 실행되지 않는다. 

 

 

-결론

이번에 만났던 문제의 정의 



index.js에서 실행되는 것은 server이며 

서버 요청에 따른 제공 된 html파일에서 import하는 js파일은 브라우저용 js인데, 

그 두가지를 구분하지 못하고 html파일에서 server파일을 제공하였던 문제.