Webpack 설치과정(ft. Babel)

이종현·2023년 3월 25일

ModuleBundler

목록 보기
1/1
post-thumbnail

1. 계기

이번에 같이 공부하는 F-Lab 팀원이랑 협업으로 프로젝트를 진행하던 중에 앞으로 협업을 하면서 Webpack을 미리 설정하고 계속 맞춰나가는 것이 좋다고 판단하여, 프로젝트 진행을 잠시 몇 일만 중단하고 Webpack을 설치한 다음에 진행하기로 결정했다.

내가 나름 시도했던 과정을 남기기 위해서 블로그에 글을 남기지만 다른 분들의 시간은 소중하니.. 
결론부터 이야기하면 해당과정은 결국 css를 분리하지 못해, 
실패한 과정이기에 webpack을 제대로 설치 및 설정해보고 싶은 분들은 아래 링크를 참고하시면 좋을 것 같다. 

https://www.youtube.com/watch?v=Cpx_KeZH9D8&list=PL-qMANrofLysX7hRV9BQymJFLD7Fasiv6&index=2

2. 설치과정

기존에는 항상 npm 관련해서그냥 무턱대고 설치했었다면 이번에는 하나하나 기록해보기로 결정했기 때문에, 일단 내 설치환경부터 확실하게 하고나서 설치하는 걸로 진행해보려고 했다.

2.1 내 폴더구조 파악하기

일단 두 가지의 경우를 생각했다.

프로그래밍 공부라는 가장 상위 폴더 안에 프로젝트라는 폴더가 있고, 그 안에 팀원이랑 같이 진행하는 깃허브에 리퍼지토리를 원격으로 연결해놓은 로컬 폴더가 있는 상황이다. 즉, 프로그래밍 공부/프로젝트/SPA_Router 로 구성되어 있는 상황이다.

그렇다면 앞으로 Webpack을 계속 사용해야 하는 상황인데, 하나의 SPA_Router 프로젝트에다 Webpack을 설치해서 사용하고 되면 나중에 TypeScript 프로젝트를 하게 되면 다시 그 프로젝트 폴더에다 설치를 해야하는 상황이다.

그럼 그것또한 시간을 허비?(계속 Webpack을 설치하고 숙지한다는 점에서는 시간 낭비가 아닐수도..)한다고 생각이 들어서 설치과정 자체를 제대로 잘 기록해 놓고, 상위 폴더에 설치해도 문제가 없다면 상위 폴더에 설치해서 진행하는 것이 더 효율적이라는 생각이 들었다.

그래서 두 가지의 경우, 프로그래밍 공부/프로젝트/SPA_Router에서 프로그래밍 공부에 Webpack을 설치할지, 아니면 SPA_Router에 Webpack을 설치할지를 정해야 하는 상황이다.

2.2 조사해보기

일단 알아본바로는, Webpack은 폴더마다 영향을 끼치게 되는데, webpack.config.js 파일을 통해 최종적으로 Webpack을 적용할 수 있는 것 같다.

그리고 만약 프로그래밍 공부 폴더에서 Webpack을 전역으로 설치하면 모든 프로젝트에서 Webpack을 사용할 수 있지만, 각 프로젝트마다 Webpack 설정 파일을 별도로 작성하여 각각 다르게 설정할 수 있는 것 같다.

예를 들어, 프로그래밍 공부/Project1과 프로그래밍 공부/Project2 각각의 루트 디렉토리에서 Webpack 설정 파일을 작성하고 구성하면, 각각의 프로젝트에서 Webpack이 해당 설정 파일을 기반으로 빌드를 수행하고 이를 통해 각각의 프로젝트에 맞는 빌드 및 번들링 작업을 수행할 수 있는 것이다.

그러면서 내 폴더구조를 유심히 살펴보았다. 프로그래밍 공부 폴더에는 실제 프로젝트와 관련없는 TIL 폴더라든지 그런 폴더들도 존재했기에, 굳이 번들링해서 작업할 필요가 있을까? 하는 파일들이 더욱 많았다. 그래서 차라리 프로젝트 폴더에 Webpack을 전역으로 설치하고, 각각의 개별 프로젝트 파일에 webpack.config.js 파일을 프로젝트마다 설정하면 될 것 같다.

Babel

그러던 와중에 예전에 모던 자바스크립트 Deep Dive에서 공부했던 Babel이 떠올랐다. Webpack에서 Babel-Loader 같은 것도 사용했었기에, 이번에 설치하는 김에 Babel도 같이 해놓으면 좋을 것 같았다. 그래서 모던 자바스크립트를 정리해놓은 걸 다시 확인해보았고, 처음에 Webpack을 설치하는 과정이랑 거의 흡사했다.

2.3 설치하기

Babel과 Webpack은 둘다 npm init -y 를 통해 package.json 파일을 생성한다음 설치하는 점이 동일했다.

그리고 모던 자바스크립트 Deep Dive에서는 Babel을 먼저 설치 후, Webpack을 설치했다. 즉, npm init -y
로 Babel과 Webpack을 하나로 관리할 package.json 파일을 생성후, 추가적으로 Babel 설치와 Webpack 설치를 진행하면 될 것 같다.

2.3.1 Babel 설치하기

프로젝트 폴더로 이동했다.

npm init -y
// 그냥 npm init 을 하게 되면, 질문이 하나씩 나오게 되고, 하나씩 대답하는 식으로 init을 완료해야 한다.

해당 명령어를 실행했다. 그랬더니 아래의 오류 발생.

npm ERR! Invalid name: "프로젝트"

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/dataliteracy/.npm/_logs/2023-03-24T19_46_33_438Z-debug-0.log

알아보니, npm에서 유효햐지 않은 패키지 이름을 사용했단다. 그러니까 한글로 만든 폴더 이름이 문제고, npm은 ASCII 문자만 허용하기 때문에 영어로 바꾸라는 것이다.;;
그래서 일단 ‘프로젝트’ → ‘Projects’로 변경해서 npm init -y를 실행했다. 그랬더니, package.json 파일 생성

{
  "name": "projects",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

json 파일 안에 있는 이 내용이 기본값인 것 같다.

그리고 개인적으로 궁금하니까, 폴더명을 다시 한글파일로 변경한 다음에 babel이랑 webpack을 설치해보려고 한다.

상위 폴더에서 전역적으로 적용할 수 있게 하기로 했으니까, -g 옵션으로 설치

npm install -g babel webpack

에러 발생..

npm ERR! code EACCES
npm ERR! syscall mkdir
npm ERR! path /usr/local/lib/node_modules/babel
npm ERR! errno -13
npm ERR! Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/babel'
npm ERR!  [Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/babel'] {
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'mkdir',
npm ERR!   path: '/usr/local/lib/node_modules/babel'
npm ERR! }
npm ERR! 
npm ERR! The operation was rejected by your operating system.
npm ERR! It is likely you do not have the permissions to access this file as the current user
npm ERR! 
npm ERR! If you believe this might be a permissions issue, please double-check the
npm ERR! permissions of the file and its containing directories, or try running
npm ERR! the command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/dataliteracy/.npm/_logs/2023-03-24T22_26_11_720Z-debug-0.log

권한 문제.. sudo로 다시 진행

sudo npm install -g babel webpack

이후 성공했다.

npm WARN deprecated babel@6.23.0: In 6.x, the babel package has been deprecated in favor of babel-cli. Check https://opencollective.com/babel to support the Babel maintainers

added 78 packages, and audited 79 packages in 685ms

9 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

package.json 파일이 변경되지 않는 것 같아, babel 부분만 다시 설치..(파일 자체를 종료했다가 다시 켜야 업로드된 정보를 볼 수 있다;; 이때는 몰랐음..)

npm install --save-dev @babel/core @babel/cli

이후 babel 정보가 등록되었다.

{
  "name": "projects",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.21.0",
    "@babel/core": "^7.21.3"
  }
}

그리고 Babel을 사용하려면 @babel/preset-env를 설치해야 한다. @babel/preset-env는 함께 사용되어야 하는 Babel 플러그인을 모아 둔 것으로 Babel 프리셋이라고 부른다. Babel이 제공하는 공식 Babel 프리셋은 다음과 같다.

  • @babel/preset-env
  • @babel/preset-flow
  • @babel/preset-react
  • @babel/preset-typescript

이 시점에서 폴더명을 다시 영어로 바꿨다. 과연 오류가 발생할까?

npm install --save-dev @babel/preset-env
npm install --save-dev @babel/preset-flow
npm install --save-dev @babel/preset-react
npm install --save-dev @babel/preset-typescript

해당 명령어를 모두 입력했다. typescript랑 react는 어차피 나중에 진행할 거니까, 일단 그냥 설치했다.

폴더명을 다시 바꿨다고 오류가 발생하지는 않았다. 그냥 앞으로 작업할 소스들이 해당 폴더를 참고하고 있었으면 문제가 될 수 있지만, 당장 npm init을 할때 빼고는 폴더를 한글로 바꾼다고 해도 오류가 생기는 건 아니고, 중간에 바꾼다고 해도 문제가 생기는 건 아닌 것 같다.

{
  "name": "projects",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.21.0",
    "@babel/core": "^7.21.3"
  },
  "devDependencies": {
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-flow": "^7.18.6",
    "@babel/preset-react": "^7.18.6",
    "@babel/preset-typescript": "^7.21.0"
  }
}

package.json 파일에 정상적으로 추가되었다.

이후 bebel.config.json 파일을 직접 생성한다. 직접.. 생성

그리고 preset을 사용하겠다는 의미로 해당 코드를 입력한다.

{
  "presets": [
    "@babel/preset-env", 
    "@babel/preset-flow", 
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

그리고 트랜스파일링은 당장 하지 않을 것이고, 플러그인 설치도 당장은 하지 않을테니, Babel은 여기서 마무리를 지어보자.
babel-polyfill 같은 경우에도 나중에 babel을 사용해야할 때 설치하면 될 것 같으니, 당장은 설치를 안하는 걸로 하겠다. 지금 당장 중요한 건 Webpack 이니까..

2.3.2 Webpack 설치하기

Webpack은 일단 전역으로 설치하기는 했지만, package.json에 정보가 생성되지 않았으니, 명령어를 한 번더 입력해준다.

npm install --save-dev webpack webpack-cli

그런데 -g 옵션을 이용해서 전역으로 설치하는 경우에도 package.json 파일에 정보는 생겨야 하는거 아닌가?;;
왜 그런지는 모르겠지만 일단 설치 진행.. 정상적으로 정보가 등록되었다.

{
  "name": "projects",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.21.0",
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-flow": "^7.18.6",
    "@babel/preset-react": "^7.18.6",
    "@babel/preset-typescript": "^7.21.0",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1"
  }
}

기존에 의존성이 나눠져있는 부분을 하나로 합쳤다. 그래도 상관없는듯 하여..

그리고 Webpack이 모듈을 번들링할 때 Babel을 사용하여 ES6+/ES.NEXT 사양의 소스코드를 ES5 사양의 소스코드로 트랜스파일링하도록 babel-loader를 설치한다.

npm install --save-dev babel-loader

설치가 완료되면 package.json에 "babel-loader": "^9.1.2" 해당 코드가 등록된다.

그리고 이 쯤에서 webpack.config.js 파일을 각 프로젝트 마다 설정하기 위해서, SPA_Router 폴더에 들어가서 설정하려고 한다.

Babel은 JavaScript 코드를 변환하기 위한 도구이며, webpack과 함께 사용될 때 트랜스파일러로 작동한다.
따라서 Babel 설정 파일인 babel.config.json은 모든 프로젝트에서 공통적으로 사용되는 구성 파일로 보통 
프로젝트 루트 디렉토리에 위치시키는 것이 일반적이다. 

그러나 webpack의 설정 파일인 webpack.config.js는 각 프로젝트마다 서로 다른 설정을 가질 수 있으므로,
각 프로젝트 디렉토리에 따로 생성하는 것이 좋다.

이렇게 하면 각 프로젝트가 자체적으로 webpack 설정을 관리하고 필요에 따라 구성을 조정할 수 있다.

따라서, babel.config.json은 프로젝트 폴더에 위치시키지 않고 Project 폴더에 위치시키고, 
webpack.config.js는 각 프로젝트 폴더에 위치시키는 것이 좋을 것 같다. 

그럼 SPA_Router 폴더에 webpack.config.js 파일 생성

내 경우에 주의할 만한 점이 기존에 작업하던 프로젝트라.. 현재 checkout 한 branch가 main이 아니였기 때문에, 일단 main branch로 check out 했다.

그리고 로컬 폴더에 webpack.config.js 파일을 생성했다.

이제 webpack.config.js을 설정하기 전에 Webpack의 핵심 4가지를 이해해보자.

Entry, Output, Loader, Plugin 설정

이제 하나씩 설정을 하려고 하는 와중에 블로그 글들을 쭉 보고, 모던 자바스크립트 책도 보면서 webpack.config.js 파일을 비교해봤다. 그런데 일단 찾아본 것 중에는 두 가지 방식이 있는 듯 했다.

CommonJS 모듈 시스템
동기적으로 모듈을 로드합니다. 즉, 모듈이 로드되어야 실행이 가능합니다.
require() 함수를 사용하여 모듈을 로드합니다. require() 함수는 모듈 객체를 반환합니다.
module.exports 객체나 exports 객체를 통해 모듈을 내보냅니다. 
module.exports는 객체나 함수 등을 직접 할당할 수 있으며, 
exports는 module.exports에 대한 참조로서 사용됩니다.
ES6 모듈 시스템
비동기적으로 모듈을 로드합니다. 즉, 모듈이 로드되지 않아도 실행이 가능합니다.
import 문을 사용하여 모듈을 로드합니다. import 문은 모듈 객체를 반환합니다.
export 문을 통해 모듈을 내보냅니다. export는 객체나 함수 등을 직접 할당할 수 있습니다. 

단, export는 module.exports와 달리 참조 관계를 가지지 않습니다.

그러니까, 일반적으로 계속 작성해왔던 ES6 스타일의 js파일로 정의하면 ES6 모듈 시스템, module.exports로 정의하면 CommonJS 모듈 시스템이다.

참고로, 브라우저에서는 ES6 모듈 시스템이 CommonJS보다 더 효율적이며, 대부분의 최신 브라우저에서는 
ES6 모듈 시스템을 지원합니다. 하지만, Node.js에서는 아직까지 CommonJS 모듈 시스템을 주로 사용합니다.

웹팩은 현재 대부분의 브라우저에서 지원되는 ES6 모듈 시스템을 지원하므로, 
브라우저에서 로드할 HTML, CSS, JS 파일을 가지고 있는 프로젝트에서도 ES6 모듈 시스템으로 번들링하는 것이
일반적으로 더 좋습니다.

ES6 모듈 시스템은 CommonJS와는 달리 정적인 의존성 트리를 가지며, 
이를 통해 웹팩이 더 효율적으로 모듈을 번들링할 수 있습니다. 
또한, 웹팩에서 ES6 모듈 시스템을 사용하면, 번들링된 결과물에서 사용하지 않는 코드를 제거하거나 최적화하는 
등의 추가적인 기능을 사용할 수 있습니다.

그러나 모듈 시스템을 결정하는 것은 프로젝트의 특성에 따라 다를 수 있습니다. 
예를 들어, 브라우저에서 실행되는 스크립트들을 번들링할 때는 ES6 모듈 시스템을 사용하는 것이 일반적입니다. 
반면, Node.js에서 실행되는 스크립트를 번들링할 때는 CommonJS 모듈 시스템을 사용하는 것이 일반적입니다.

위와 같은 이유로 ES6 모듈 시스템 방식으로 webpack.config.js 파일을 작성해보려고 한다.

  1. Entry 설정
    Entry 설정에서는 웹팩이 어디서부터 디펜던시 그래프를 그려나갈지 명시한다.
    Entry 설정은 웹팩이 애플리케이션을 시작할 때, 어디에서부터 파일을 읽어 들일지 설정한다. 일반적으로 애플리케이션의 시작점(entry point)이 되는 자바스크립트 파일을 지정한다. 이 파일에서 필요한 의존성(dependency)을 찾아서 묶어나가게 된다.
  2. Output 설정
    Output 설정은 웹팩이 묶은 결과물을 어디에 내보낼지 지정한다. 일반적으로 dist 디렉토리에 main.js와 같은 번들 파일을 생성한다.
  3. Loader 설정
    Loader 설정은 자바스크립트나 JSON 파일 이외의 파일을 번들링하기 위해 Loader를 설정한다. 이는 해당 파일이 웹팩에서 인식할 수 있는 모듈로 변환해준다. 예를 들어, CSS 파일을 번들링하기 위해서는 css-loaderstyle-loader를 설정하면 된다. Loader를 설정하려면 'test'와 'use' 두 가지 필수 속성을 적어주어야 한다.
    이때 test는 정규표현식으로 어떤 파일을 변환할지 지정해주는 속성이고, use는 어떤 로더를 사용할지 명시하는 속성이다.
Loader로 인해 번들링이 가능하게 되면서 코드 관리와 유지보수가 보다 용이해집니다.

모듈 로딩이란, 웹 개발에서 필요한 여러 자원들(HTML, CSS, JavaScript, 이미지 등)을 필요한 곳에서 
불러와 사용할 수 있게 하는 것을 의미합니다.

이를 위해, 예를 들어 이전에는 HTML 파일에서 CSS 파일을 불러와야 했기 때문에, JavaScript 파일과 
CSS 파일이 서로 연결되어 있지 않아서 코드 관리가 어렵고 유지보수가 어려웠습니다. 
그러나 웹팩을 이용하면 JavaScript 파일에서 필요한 CSS 파일을 불러와 함께 번들링할 수 있기 때문에, 
JavaScript 파일과 CSS 파일이 서로 연결되어 있어 코드 관리와 유지보수가 보다 용이해집니다. 또한 
HTML, CSS, JavaScript 파일을 하나로 번들링하여 하나의 HTTP 요청으로 로딩할 수 있습니다. 
이렇게 번들링된 파일은 로딩 시간이 짧아져 웹 페이지 로딩 속도가 빨라지고, 성능이 향상됩니다.
그리고 웹팩을 이용하면 파일을 압축하고 최적화하여 파일 크기를 줄일 수 있습니다. 
이렇게 파일 크기가 작아지면 로딩 시간이 더욱 짧아져 성능이 더욱 향상됩니다.

이 기능은 웹팩에서 제공하는 것으로, 다른 번들러에서는 지원되지 않을 수도 있습니다. 
이를 가능하게 하는 방법 중 하나는, 웹팩에서 제공하는 로더(Loader)를 이용하는 것입니다. 
예를 들어, CSS 파일을 불러오기 위해서는 css-loader와 style-loader를 사용할 수 있습니다.

이렇게 Loader를 설정해주면, 웹팩은 JavaScript 파일에서 필요한 모든 자원들을 로딩하고 번들링하여 하나의 
파일로 만들어줍니다. 
이를 통해, 포맷에 얽매이지 않은 자유로운 import가 가능해지고, 개발자는 보다 편리하게 웹 개발을 할 수 있게
됩니다.
  1. Plugin 설정
    Plugin 설정은 웹팩의 번들링 과정에 추가적인 기능을 제공합니다. 예를 들어, 번들 파일에서 CSS 파일을 별도로 추출하거나, 번들 결과물을 최적화하는 기능 등을 제공합니다. 대표적인 플러그인으로는 uglifyjs-webpack-plugin, html-webpack-plugin, clean-webpack-plugin 등이 있습니다.

html-webpack-plugin

html-webpack-plugin은 웹팩으로 번들링한 자바스크립트 파일을 포함한 HTML 문서를 생성해준다. 이 플러그인을 사용하지 않으면 번들링된 자바스크립트 파일을 실행할 수 있는 HTML 문서를 수동으로 생성해야 한다. html-webpack-plugin은 이러한 수고를 덜어줘서, 플러그인 설정에 따라 dist 디렉토리 내에 자동으로 HTML 문서를 생성해준다.

특히 template 속성을 사용하여, HTML 문서의 원본 파일 경로를 지정할 수 있다. 예를 들어, src 폴더 내에 있는 index.html 파일을 원본 파일로 사용하고 싶다면, 아래와 같이 플러그인 설정을 지정할 수 있다.

plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })

이렇게 설정하면, 웹팩으로 번들링한 JS 파일을 포함한 index.html 파일이 dist 폴더 내에 자동으로 생성된다.

mini-css-extract-plugin

mini-css-extract-plugin은 웹팩으로 번들링한 CSS 파일을 따로 분리해주는 플러그인이다. 이 플러그인을 사용하지 않으면, 웹팩으로 번들링한 자바스크립트 파일 안에 CSS 코드가 포함되어 있다. 그러나 이렇게 하나의 JS 파일에 모든 스타일 코드가 포함되면, 스타일 코드가 변경될 때마다 JS 파일 전체를 다시 다운로드해야 하는 문제가 있다. 이는 불필요한 대역폭 낭비를 초래할 수 있다.

mini-css-extract-plugin을 사용하면, CSS 코드를 별도의 파일로 분리해 다운로드 시간을 최적화할 수 있습니다. 예를 들어, 아래와 같이 웹팩 설정 파일에서 mini-css-extract-plugin을 사용할 수 있다.

plugins: new MiniCssExtractPlugin()

그럼 이제 명령어를 입력해서 plugin을 설치해보자. 이때 주의할 점은 내 경우처럼 Projects 폴더 안에서 프로젝트를 하나하나 관리하는 상황이라면, 반드시 작업할 프로젝트 안으로 들어가서 설치해야 한다.

npm i -D html-webpack-plugin mini-css-extract-plugin

npm install --save-dev html-webpack-plugin

설치는 진행했지만, ESLint가 아직 해결되지 않는다고 오류를 잡아주었다.

'html-webpack-plugin' should be listed in the project's dependencies. 
Run 'npm i -S html-webpack-plugin' to add it

npm i -S html-webpack-plugin 명령어 다시 입력.. 하지만 아직도 ESLint에 걸린다.

이후에 package.lock.json 파일을 삭제하고 다시 npm install하고 import path from ‘path’ 로 변경하고 의존성도 추가하고 해보면서 저 부분은 해결했는데, 정확히 어떤 부분 때문에 해결했는지를 놓쳤다. (길어지는 설정 탓에 집중력이..;;)

하지만 아직 한 가지 ESLint 오류가 남아있다.

Casing of mini-css-extract-plugin does not match the underlying filesystem.

이 오류는 결국 일단 해결하지 못한채로.. npx webpack 실행, 하지만 웹팩에서 ES 모듈을 지원하지 않아서 오류가 다시 발생..

ES6 모듈을 지원하는 Node.js 버전 13.2.0 이상에서 실행할 경우, --experimental-modules
플래그를 사용하여 실행할 수 있다고 해서 아래 명령어 실행

node --experimental-modules webpack.config.js

package.json 파일에 type: module 설정하지 않아서, 다시 설정하고 명령어 실행! 일단 완료된 것 같다. 다시 webpack 실행

npx webpack

이번에는 정상적으로 동작했다. 하지만 경고 메세지가 출력된다.

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' 
for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for 
each environment.
You can also set it to 'none' to disable any default behavior. 
Learn more: https://webpack.js.org/configuration/mode/

이 경고 메세지는 mode 옵션이 설정되어 있지 않아, production 모드가 대신 사용되고 있음을 나타낸다

webpack.config.js 파일에 해당 항목 추가..

mode: 'development'

다시 실행..

만약 빌드하고 종료하지 않고 파일 변경에 대한 감지와 빌드를 계속 수행하려면 아래 코드를 입력하면 된다.

webpack-dev-server

일단 webpack이 실행은 되고 dist 폴더를 만들어서 index.html 파일과 main.js 파일을 생성하는 것 까지는 완료했다. 하지만 css파일을 로드하지 못하고 있다..즉, css 플러그인이 제대로 동작하지 않고 있다.

결국 결론을 이야기하자면 여기까지 삽질하고,
https://www.youtube.com/watch?v=Cpx_KeZH9D8&list=PL-qMANrofLysX7hRV9BQymJFLD7Fasiv6&index=2
여기 유튜브 영상보고 해결했다;; 결국 ES모듈로는 해결못하고 CommonJS모듈로 해결했다.
자세한 건 유튜브를 직접 보고 각자 개발환경에 맞춰서 세팅하시는 것이 가장 좋을 것 같다.

아래에 시도했지만 실패했던 전체코드들과 성공한 코드를 남겨놓는다.


3. 전체 코드

ES 모듈 스타일 실패 전체 코드

// new MiniCSS 안에 filename 지정안해줬음.. 하지만 그 문제를 해결해도 여전히 오류 발생
import path from './src/js/index.js';
import { fileURLToPath } from 'url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';

const dirname = path.dirname(fileURLToPath(import.meta.url));

const config = {
  entry: './src/js/index.js',
  output: {
    filename: 'main.js',
		// main.js의 이름으로 dist 폴더에 번들파일 생성
    path: path.resolve(dirname, 'dist'),
		// dist 폴더 안에 사용하지 않는 파일들 정리
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|pages)/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          { loader: MiniCssExtractPlugin.loader },
          {
            loader: 'css-loader',
            options: { import: true },
          },
        ],
      },
      {
        test: /\.png$/,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [new HtmlWebpackPlugin({ template: './src/index.html' }), new MiniCssExtractPlugin()],
  devtool: 'eval-cheap-module-source-map',
  target: 'web',
  devServer: {
    contentBase: path.resolve(dirname, 'dist'),
    compress: true,
    hot: false,
    historyApiFallback: true,
    liveReload: true,
    open: true,
    port: 5500,
    watchContentBase: true,
    watchOptions: {
      poll: 1000,
      ignored: /node_modules/,
    },
  },
};

export default config;
// new MiniCSS 안에 filename 지정안해줬음.. 하지만 그 문제를 해결해도 여전히 오류 발생
import path from 'path';
import { fileURLToPath } from 'url';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';

const dirname = path.dirname(fileURLToPath(import.meta.url));

const config = {
  mode: 'development',
  entry: './src/js/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(dirname, 'dist'),
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|pages)/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          { loader: MiniCssExtractPlugin.loader },
          {
            loader: 'css-loader',
            options: { import: true },
          },
          {
            loader: 'style-loader',
          },
        ],
      },
      {
        /* png 또는 jpeg, jpg (e? 는 e 문자가 있을 수도 있고 없을 수도 있다는 의미)
        또는 gif 그리고 마지막 i는 대소문자를 구분하지 않도록 하는 플래그 */
        test: /\.(png|jpe?g|gif)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './index.html',
      caseSensitive: false,
      filename: 'index.html',
      inject: 'body',
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      ignoreOrder: true,
    })],
  devtool: 'eval-cheap-module-source-map',
  target: 'web',
  devServer: {
    contentBase: path.resolve(dirname, 'dist'),
    compress: true,
    hot: false,
    historyApiFallback: true,
    liveReload: true,
    open: true,
    port: 5500,
    watchContentBase: true,
    watchOptions: {
      poll: 1000,
      ignored: /node_modules/,
    },
  },
};

export default config;
// Projects/SPA_Router_Practice 폴더 안의 package.json
{
  "name": "spa_router_practice",
  "version": "1.0.0",
  "type": "module",
  "description": "라우터 기능을 이용해 SPA 방식으로 동작하는 사이트를 구현한다. toss.tech 사이트를 벤치마킹해서 만들었습니다.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.7.3",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.7.5",
    "style-loader": "^3.3.2",
    "webpack-bundle-analyzer": "^4.8.0",
    "webpack-cli": "^5.0.1"
  }
}
// Projects 폴더 안의 package.json
{
  "name": "projects",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.21.0",
    "@babel/core": "^7.21.3",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-flow": "^7.18.6",
    "@babel/preset-react": "^7.18.6",
    "@babel/preset-typescript": "^7.21.0",
    "babel-loader": "^9.1.2",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.13.1"
  }
}

CommonJS 모듈 스타일 성공 코드

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
  mode: 'development', // development 개발 버전
  // mode: 'production', // production (최종)), none
  // entry: './src/index.js', // 모든 스크립트를 작성할 곳, 입구
  entry: {
    // index: './src/index.js', // index 속성 생성
    index: path.join(__dirname, 'src/js', 'index.js'),
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: './js/[name].js', // js/index.js 생성
  },
  plugins: [
    new CleanWebpackPlugin(), // 웹팩 실행(build) 할때마다, dist 청소
    new HtmlWebpackPlugin({
      template: './index.html', // 번들전 html
      filename: './index.html', // 번들후 html
      hash: true, // 모든 스크립트, css 캐시 무효화
      showErrors: true, // 오류 html에 출력
      chunks: ['index'],
    }),
    new MiniCssExtractPlugin({
      filename: './css/style.css',
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: 'images',
          to: 'images',
        },
      ],
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/i, // 확장자가 css로 끝나는 파일 선택
        // use: ['style-loader', 'css-loader'], // 로딩 순서는 오른쪽부터, <style></style> 생성
        use: [MiniCssExtractPlugin.loader, 'css-loader'], // 로딩 순서는 오른쪽부터, 별도의 css 생성
        exclude: /node_modules/,
      },
      {
        test: /\.(png|svg|jpe?g|gif)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'images/[name][ext]',
        },
        exclude: /node_modules/,
      },
    ],
  },
  devServer: {
    static: './dist',
  },
};
{
  "name": "webpack_basic",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "start": "webpack serve --open"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "clean-webpack-plugin": "^4.0.0",
    "copy-webpack-plugin": "^11.0.0",
    "css-loader": "^6.7.3",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.7.5",
    "webpack": "^5.76.3",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.13.1"
  }
}

Reference

https://365kim.tistory.com/35
https://www.youtube.com/watch?v=Cpx_KeZH9D8&list=PL-qMANrofLysX7hRV9BQymJFLD7Fasiv6&index=2

profile
데이터리터러시를 중요하게 생각하는 프론트엔드 개발자

0개의 댓글