{"id":19002536,"url":"https://github.com/doonguk/webpack-boilerplate","last_synced_at":"2025-04-16T22:31:45.201Z","repository":{"id":39052237,"uuid":"279613649","full_name":"doonguk/webpack-boilerplate","owner":"doonguk","description":"Webpack, Babel 를 이용한 프론트엔드 개발환경 셋팅","archived":false,"fork":false,"pushed_at":"2022-12-13T08:15:21.000Z","size":2441,"stargazers_count":35,"open_issues_count":22,"forks_count":7,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-08T18:15:29.851Z","etag":null,"topics":["babel","webpack-js"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doonguk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-14T14:49:52.000Z","updated_at":"2024-06-21T14:59:21.000Z","dependencies_parsed_at":"2022-08-31T10:52:22.138Z","dependency_job_id":null,"html_url":"https://github.com/doonguk/webpack-boilerplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doonguk%2Fwebpack-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doonguk%2Fwebpack-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doonguk%2Fwebpack-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doonguk%2Fwebpack-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doonguk","download_url":"https://codeload.github.com/doonguk/webpack-boilerplate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249283018,"owners_count":21243661,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babel","webpack-js"],"created_at":"2024-11-08T18:15:25.989Z","updated_at":"2025-04-16T22:31:44.820Z","avatar_url":"https://github.com/doonguk.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"## 📒 목차\n\n1. \u003ca href=\"#babel\"\u003ebabel?\u003c/a\u003e\n2. \u003ca href=\"#webpack\"\u003ewebpack?\u003c/a\u003e\n3. \u003ca href=\"#step1\"\u003ebabel 적용하기\u003c/a\u003e\n4. \u003ca href=\"#step2\"\u003ewebpack 적용하기\u003c/a\u003e\n5. \u003ca href=\"#step3\"\u003estyle 관련 loader 적용하기 (feat. sass-loader)\u003c/a\u003e\n6. \u003ca href=\"#step4\"\u003efile-loader, url-loader 적용하기\u003c/a\u003e\n7. \u003ca href=\"#step5\"\u003e모듈을 절대경로로 불러오기\u003c/a\u003e\n8. \u003ca href=\"#step6\"\u003eindex.html 생성하기\u003c/a\u003e\n9. \u003ca href=\"#step7\"\u003ewebpack으로 개발서버 띄우기\u003c/a\u003e\n10. \u003ca href=\"#step8\"\u003e개발서버, 실서버 환경 분리하기\u003c/a\u003e\n11. \u003ca href=\"#step9\"\u003eReact 사용하기(옵션) \u003c/a\u003e\n12. \u003ca href=\"#step9\"\u003e후기\u003c/a\u003e\n\n\n\n\u003ch2 id=\"babel\"\u003e🤔 babel\u003c/h3\u003e\n\n\n### babel?\n\n공식문서에서는 babel을 다음과 같이 표현한다.\n\n\u003e Babel is a JavaScript compiler. Use next generation JavaScript\n\nbabal은 최신 버전의 자바스크립트 문법(보통 ES6+)을 이전 버전의 자바스크립트 문법으로 변환시켜주는 트랜스 컴파일러다. \n\n예를들어, 개발자가 아래와 같이 코딩했다면\n\n```javascript\nconst sayHello = (name) =\u003e console.log('hello ${name}')\n```\n\n이렇게 바뀐다.\n\n```javascript\nvar sayHello = function sayHello(name) {\n  return console.log(\"hello \".concat(name));\n};\n```\n\n하지만 babel을 사용한다고 해서 모든 자바스크립트 최신 문법을 사용할 수 있는건 아니다. `Array.from` 이나 `Object.assign()` 등 기존에 문법에 없던 문법은 **pollyfill(코드 조각)**을 추가하여 지원하게 해야한다.\n\n\u003e **pollyfill**\n\u003e\n\u003e 자바스크립트의 최신기술을 구버전 브라우저에서 사용하기 위해서는 변화과 함께 폴리필도 사용해야 한다. 폴리필은 **런타임 기능 주입**을 말한다. 런타임 기능 주입이 뭐야? 한다면, **브라우저에서 코드가 실행될 때 기능이 있는지 없는지 확인한 뒤 없을 경우에만 코드 변환을 하는 기능을 말한다**\n\n### babel의 동작원리\n\n\u003e Source Code =\u003e AST =\u003e modified AST =\u003e  Source Code\n\n**1. 파싱**\n\n소스코드를 읽어서 파싱 후 추상구문트리(AST)를 생성한다. AST의 각 노드는 소스코드의 구조를 의미한다. 추상구문트리가 궁금하면 [여기](https://astexplorer.net/#/gist/90bc74fbaaee4050afadca33d2899470/latest)를 참고한다.\n\n**2. 변환**\n\n1단계에서 작성한 추상구문트리를 가져와서 각 브라우저에 맞게 변환한다. 여기서 바벨 설정에 추가한 plugin들이 적용된다.\n\n**3. 코드 생성**\n\n2단계에서 변형된 AST를 실제 브라우저 환경에 맞는 소스코드로 변환한다.\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"webpack\"\u003e🤗 Webpack\u003c/h2\u003e\n\n### Webpack?\n\nWebpack은 모듈번들러다. 즉 여러개의 파일을 하나로 합쳐주는 도구이다. \n\n![image](https://user-images.githubusercontent.com/39187116/87876681-ce72ad00-ca14-11ea-804d-f65b58aa1ce1.png)\n\n\u003cdiv style=\"text-align: center\"\u003ewebpack 공식문서 메인사진\u003c/div\u003e\n\n### Webpack에서 Loader?\n\nWebpack은 모든 파일을 모듈로 관리한다. Js 파일 뿐만 아니라 이미지, 폰트, 스타일시트도 전부 모듈로 관리한다. 그러나 webpack은 js밖에 모른다. 비 js 파일을 웹팩이 이해하게끔  변경해야하는데 로더가 이 역할을 담당한다.\n\n로더는 `test`와 `use`키로 구성된 객체로 설정할 수 있다. `test`에 로딩할 파일을 지정하고 `use`에 적용할 로더를 설정한다. 대표적인 로더는 `babel-loader`, `css-loader`, `sass-loader`등이 있다.\n\n### Webpack을 사용하는 이유?\n\n**1. 모듈간의 의존성 문제를 해결**\n\nWebpack이 모듈간의 의존성을 계산해서 번들링 해줌\n\n**2. 네트워크 병목을 줄여줌**\n\n`\u003cscript\u003e` 태그를 여러개 사용하여 웹 페이지를 로드하면 네트워크 병목현상이 생길 수 있다. 이런 문제를 해결하려면, 하나의 JS파일로 로드하면 되지만 하나의 JS파일로 개발할 경우 코드의 가독성이나 전역공간의 오염 같은 문제가 발생하게 된다. Webpack은 여러개의 파일을 하나로 묶어주기 때문에 이와 같은 문제를 해결할 수 있고 네트워크 병목현상을 최소화할 수 있다.\n\n**3. 모듈단위의 개발이 가능하다!**\n\n모듈단위의 개발이 가능하기 때문에, 코드의 가독성도 높이고? 유지보수 효율도 높이고? 스코프에 신경쓰지 않고 개발해도 된다. 또 라이브러리간 종속 문제를 고민할 필요가 없다. (Webpack이 entry point 부터 종속성을 계산하기 때문)\n\n**4.코드를 압축, 최적화할 수 있다.**\n\n하나의 파일로 압축시켜주고, plugin을 이용해서 최적화도 가능하다.\n\n**5. ES6+ 스크립트를 지원한다.**\n\nwebpack은 babel-loader를 사용하여 babel을 지원하기 때문에 컴파일 과정에서(코드를 번들링하는 과정에서) 구형 브라우저가 사용 가능한 문법으로 코드를 변환해 준다.\n\n등등......\n\n### Webpack의 동작원리?\n\n1. entry file의 의존성을 분석한다.\n2. 다음 파일의 의존성을 분석한다. \n3. 모든 파일의 의존성을 분석할 때 까지 2번 과정을 반복한다.\n4. 3번 까지의 과정을 토대로 종속성 그래프를 만들고 이 그래프를 사용하여 모든 모듈을 하나(또는 여러개)의 bundle 파일로 합친다.\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step1\"\u003e😇 Babel 적용하기\u003c/h2\u003e\n\n**1. 기본환경 설정**\n\n```bash\nnpm init -y\nmkdir src\ncd src\nvi index.js\n```\n\n**2. index.js**\n\n```javascript\nconst foo = [1, 2, 3, [4, 5]];\nconsole.log(foo.flat());\n```\n\n**3. 필요한 module install**\n\n```bash\nnpm i @babel/core @babel/cli @babel/preset-env -D\n```\n\n- `@babel/core`: babel을 사용하기 위한 필수 패키지\n- `@babel/cli`: babel을 터미널에서 커맨드를 입력해서 사용하기 위한 패키지 ( 선택사항 )\n- `@babel/preset-env`: babel의 공식 preset중 하나이며 필요한 플러그인 들을 프로젝트 진행환경에 맞춰서 동적으로 결정해 준다. 형식이 `browserlist` 형식이기 때문에 `.browserlistrc` 파일을 만들어 상세하게 설정 가능하다. 설정하지 않으면 기본값으로 설정된다.\n\n모듈을 설치할 때 `-D` 옵션으로 설치 했는데 이는 `package.json` 파일의 `devDependencies` 항목에 추가하겠다는 의도이다. `devDependencies`는 개발이나 테스트 환경에서 필요한 패키지를 의미한다. \n\n\u003e 추가적으로, `dependencies` 옵션은 프로덕션 환경(실제 서비스환경)에서 필요한 패키지를 의미한다!\n\n**4. babel config 설정**\n\nbabel 설정을 위해 root 디렉토리에 `babel.config.js` 파일을 생성한다.\n\n```javascript\nmodule.exports = function (api) {\n  api.cache(true);\n\n  const presets = [\n    [\n      \"@babel/preset-env\",\n      {\n        targets: \"\u003e 0.25%, not dead\",\n      },\n    ],\n  ];\n\n  return {\n    presets,\n  };\n};\n\n```\n\n- `api.cache(true)` : babel 설정 파일을 한번만 실행하겠다는 뜻. 다른 옵션은 [여기](https://babeljs.io/docs/en/config-files#apicache)를 참고한다.\n- `targets` 속성은 지원하는 브라우저의 범위를 정한다.\n\n**5. pollyfill 추가**\n\n`index.js` 의 `flat`함수는 많은 브라우저에서 지원하지 않는다.([참고](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/flat))  따라서 pollyfill을 추가해 줘야한다. 대표적으로 많이 쓰는 pollyfill은 corejs@3 이다. 예전에는 @babel/pollyfill도 많이 사용했는데 성능상의 이슈가 있어서 7.4버전 이후로 deprecated 되었다.\n\n```bash\nnpm i core-js@3 -D\n```\n\n**babel.config.js**\n\n```javascript\nmodule.exports = function (api) {\n  api.cache(true);\n\n  const presets = [\n    [\n      \"@babel/preset-env\",\n      {\n        targets: \"\u003e 0.25%, not dead\",\n        useBuiltIns: \"usage\",\n        corejs: 3,\n        modules: false,\n      },\n    ],\n  ];\n\n  return {\n    presets,\n  };\n};\n\n```\n\n- `useBuiltIns`: pollyfill을 필요한것만 import 할지?말지? 설정 `usage`는 필요한 것만, `entry`는 전부 다 추가해줌. 추가적으로 `usage`로 설정하면 JS코드를 읽는 브라우저를 검사하여 해당 브라우저에 없는! syntax만 pollyfill로 추가해줌!\n- `modules`: ES6 module syntax를 사용할지?말지? 설정. `true`로 설정하면 `import, export` 를 `require, module.exports`로 대체하게 된다. 위에서 `false`로 설정한 이유는 후에 사용할 Webpack의 Tree Shaking이 `import, export`로 동작하기 때문에 `false`로 설정했다. `Tree Shaking`에 관한 내용은 [여기]([https://medium.com/naver-fe-platform/webpack%EC%97%90%EC%84%9C-tree-shaking-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-1748e0e0c365](https://medium.com/naver-fe-platform/webpack에서-tree-shaking-적용하기-1748e0e0c365)) 를 참고하자.\n\n**6. babel 실행**\n\n```bash\nnpx babel src/index.js\n```\n\n**확인**\n\n```javascript\nimport \"core-js/modules/es.array.flat\";\nimport \"core-js/modules/es.array.unscopables.flat\";\nvar foo = [1, 2, 3, [4, 5]];\nconsole.log(foo.flat());\n```\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step2\"\u003e😊 Webpack 적용하기\u003c/h2\u003e\n\n**1. 필요 module install**\n\n```bash\nnpm i webpack webpack-cli -D\n```\n\n**2. webpack.config.js 작성**\n\n프로젝트 root 디렉토리에 webpack.config.js 파일을 생성한다.\n\n```javascript\nconst path = require(\"path\");\n\nconst config = {\n  entry: \"./src/index.js\",\n  output: {\n    path: path.resolve(__dirname, \"./dist\"),\n    filename: \"build.js\",\n  },\n};\n\nmodule.exports = config;\n```\n\n- `entry` : webpack이 의존성 분석하는 시작점이다.\n- `output` 빌드한 결과물을 저장할 `path`와 `filename`을 설정한다. 위 설정은 root 디렉토리의 dist 폴더에 번들된 파일을 저장하고 파일이름을 build.js로 하겠다는 뜻\n\n**3 webpack 실행**\n\n`package.json` 파일의 `scripts` 부분에 build 커맨드 추가.\n\n```\n  \"scripts\": {\n    \"build\": \"webpack\",\n  },\n```\n\n 터미널에 `npm run build`\n\n**4. babel loader 적용**\n\n위에서 단순히 Webpack 으로만 빌드하면 적절히 트랜스파일링된 코드를 얻을 수 없다. babel을 webpack에 적용해서 코드를 트랜스파일링 해보자\n\nbabel을 webpack에 적용하기 위해선 `babel-loader` 모듈 설치가 필요하다.\n\n```bash\nnpm i babel-loader -D\n```\n\n**webpack.config.js**\n\n```javascript\nconst path = require(\"path\");\n\nconst config = {\n  entry: \"./src/index.js\",\n  output: {\n    path: path.resolve(__dirname, \"./dist\"),\n    filename: \"build.js\",\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: \"babel-loader\",\n        exclude: /node_modules/,\n      },\n    ],\n  },\n};\n\nmodule.exports = config;\n\n```\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step3\"\u003e😎 style 관련 loader 적용하기\u003c/h2\u003e\n\n### 1. css-loader\n\n```bash\nnpm i css-loader -D\n```\n\n**1-1. 왜 씀?**\n\nwebpack은 모든 파일을 모듈로 관리한다. 하지만 webpack은 js밖에 모른다. 따라서 css 파일을 js파일로 변환해 로딩해야한다. `css-loader`는 \n\n```javascript\nimport './index.css'\n```\n\ncss 파일을 `import`할 수 있게 해주고,\n\n```javascript\n// build.js\n....\n...........push([n.i,\"body {\\n  background-color: green;\\n}\\n\",\"\"])......\n```\n\nbundle하는 과정에서 위와같이, css 코드를 js코드로 변환해준다.\n\n**1-2. 적용하기**\n\n**webpack.config.js**\n\n```javascript\n...생략\nmodule: {\n  rules: [\n    {\n      test: /\\.js$/,\n      use: \"babel-loader\",\n      exclude: /node_modules/,\n    },\n    {\n      test: /\\.css$/,\n      use: [\"css-loader\"],\n    },\n  ],\n },\n```\n\n### 2. style-loader\n\n```bash\nnpm i style-loader -D\n```\n\n**1-1. 왜 씀?**\n\n`css-loader` 에 의해 모듈로 변경된 스타일 시트는 돔에 추가되어야만 브라우져가 해석할수 있다. `style-loader`는 자바스크립트로 변경된 스타일시트를 동적으로 `head`태그에 추가하는 로더이다. 보통 CSS를 번들링하기 위해서는 css-loader, style-loader를 함께 사용한다.\n\n**1-2. 적용하기**\n\n`css-loader`를 추가했던 부분 앞에 `style-loader`를 추가해준다.\n\n**webpack.config.js**\n\n```javascript\n...생략\n{\n  test: /\\.css$/,\n  use: [\"style-loader\", \"css-loader\"],\n},\n        \n```\n\n`use`는 끝에서부터 왼쪽으로 해석한다. 즉 `css-loader`가 적용된 후 `style-loader`가 적용된다.\n\n**1-3. 고민해볼 부분**\n\n`style-loader`를 적용하면 스타일을 적용할 수 있는데 `head`태그에 파일 수 만큼 `\u003cstyle\u003e` 태그가 생기게 된다.\n\n```html\n\u003chead\u003e\n  \u003cstyle\u003e\n  \t... 샬라샬라\n  \u003c/style\u003e\n  \u003cstyle\u003e\n  \t... 샬라샬라\n  \u003c/style\u003e\n\u003c/head\u003e\n```\n\n이 `style` 코드를 따로 파일로 관리하면 좋지않을까?\n\n### 3. Mini-css-extract-plugin\n\n`style-loader`는 파일의 수 만큼 `head` 태그에 `style`태그가 생긴다. 이게 마음에 들지 않아서 찾아보니 css파일을 따로 빼서 파일로 관리해주는 플러그인이 있었다.\n\n```\nnpm i mini-css-extract-plugin -D\n```\n\n**webpack.config.js**\n\n```javascript\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\nconst path = require(\"path\");\n\nconst config = {\n  entry: \"./src/index.js\",\n  output: {\n    path: path.resolve(__dirname, \"./dist\"),\n    filename: \"build.js\",\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: \"babel-loader\",\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.css$/,\n        use: [MiniCssExtractPlugin.loader, \"css-loader\"],\n      },\n    ],\n  },\n  plugins: [\n    new MiniCssExtractPlugin({\n      filename: \"style.css\", // 원하는 filename\n    }),\n  ],\n};\n\nmodule.exports = config;\n```\n\n위와같이, 모듈을 `require`해서 `style-loader` 부분에 적용하면 된다. 이와같이 적용하면 style.css 파일에 따로 스타일시트가 모이게 된다.\n\n`MiniCssExtractPlugin`을 적용하면 `style-loader`는 더이상 필요가 없다. 지워주자\n\n```bash\nnpm uninstall style-loader\n```\n\n### 4. sass-loader\n\n`sass-loader`는 `.scss`, `.sass` 파일을 `import`할 수 있게 하고, 이를 읽어서 `css` 파일로 컴파일 변환시킨다. 따라서 이를 적용하면 `.scss` 작성하여 스타일을 추가할 수 있다.\n\n```\nnpm i sass-loader -D\n```\n\n`sass-loader`는 .scss 파일을 읽어서 .css파일로 변환 시켜줘야 하기 때문에 `css-loader`보다 우선순위가 높다.\n\n**webpack.config.js**\n\n```javascript\n{\n test: /\\.(sa|sc|c)ss$/,\n use: [MiniCssExtractPlugin.loader, \"css-loader\", \"sass-loader\"],\n},\n```\n\n- `test`하는 파일을 `.sass`, `.sass`도 추가했고,\n- 로더의 맨 앞에 `sass-loader`를 추가했다.\n\n**node-sass**\n\n```bash\nnpm i node-sass\n```\n\n추가적으로 `node-sass` 모듈도 설치하자. 공식 문서에 따르면\n\n\u003e node-sass allows you to natively compile .scss files to css at incredible speed and automatically via a connect middleware.\n\n아주 빠르게 css 파일로 컴파일 시켜준다는데 원리는 조금 더 알아봐야할 것 같다.\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step4\"\u003e😋 file-loader, url-loader 적용하기\u003c/h2\u003e\n\n### file-loader\n\nwebpack은 모든 파일을 모듈로 관리하는데, 이는 file도 예외가 아니다. 따라서 파일을 관리하기 위해서는 `file-loader`를 추가해줘야 한다. 추가적으로 `file-loader`는 실제로 사용되는 파일만 **복사**해서 적용한다.\n\n```bash\nnpm i file-loader -D\n```\n\n**webpack.config.js**\n\n```javascript\n...생략 \n{\n   test: /\\.js$/,\n   use: \"babel-loader\",\n   exclude: /node_modules/,\n },\n {\n  test: /\\.(sa|sc|c)ss$/,\n  use: [MiniCssExtractPlugin.loader, \"css-loader\", \"sass-loader\"],\n },\n {\n  test: /\\.(png|jpe?g|gif|svg|ico)$/,\n  use: [\n      {\n        loader: \"file-loader\",\n        options: {\n          name: \"images/[name].[ext]?[hash]\",\n        }\n      },\n ],\n},\n```\n\n`file-loader`를 적용하면 file을 `import` 해서 사용할 수 있다. 예시는 아래와 같다.\n\n```javascript\nimport woowa from './woowabros.png'\n\nconst $img = document.createElement('img')\n$img.src = woowa\n$img.alt = 'this is woowabros png'\ndocument.body.appendChild($img)\n```\n\n 추가적으로 options의 `name`값에 hash를 설정하면, rebundle시 기존의 hash된 파일을 사용해서 불필요한 복사를 막는다.\n\n### url-loader\n\n`file-loader`는 파일을 복사해서 해싱한 후 사용한다. `url-loader`는 `file-loader`의 기능을 base로 작은 파일이나, 글꼴은 파일로 복사하지 않고 `toString('base64')` 로 문자열로 변환하여 bundle 파일에 넣는다. (bunlde 파일이 더 가벼워 지겠군)\n\n**webpack.config.js**\n\n```javascript\n// 생략\n{\n  loader: \"url-loader\",\n  options: {\n    name: \"images/[name].[ext]?[hash]\",\n    limit: 10000\n\t}\n},\n```\n\n- `file-loader`로 설정한 부분을 `url-loader`로 바꿔주기만 하면된다. \n\n- `limit`값의 의미는 10kb(10000) 미만의 파일을 `url-loader`로 처리하겠다는 뜻\n- `url-loader`는 `file-loader`가 있어야 동작한다. 따라서 `file-loader`를 지우면(모듈을 uninstall) 안된다.\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step5\"\u003e🎃 모듈을 절대경로로 불러오기\u003c/h2\u003e\n\n개발을 하다보면 상대경로로 모듈을 불러오는게 너무 보기싫다ㅜㅜ babel plugin을 이용하면 이를 절대경로로 불러올 수 있다.\n\n`module-resolver`라는 plugin을 설치하자.\n\n```bash\nnpm i babel-plugin-module-resolver -D\n```\n\n**babel.config.js**\n\n```javascript\nmodule.exports = function (api) {\n  api.cache(true);\n\n  const presets = [\n    [\n      \"@babel/preset-env\",\n      {\n        targets: \"\u003e 0.25%, not dead\",\n        useBuiltIns: \"usage\",\n        corejs: 3,\n        modules: false,\n      },\n    ],\n  ];\n\n \nconst plugins = [\n    [\n      \"module-resolver\",\n      {\n        root: [\"./src\"],\n        alias: {\n          imgs: \"./public/images\",\n        },\n      },\n    ]\n  ];\n\n  return {\n    presets,\n    plugins,\n  };\n};\n```\n\n- `root`는 사용자 정의 프로젝트 루트를 지정한다.\n\n- 위와같이 설정하면 `./src/path/subPath` 경로의 무언가를 가져올 때 `path/subPath` 로 가져올 수 있다.\n\n- `alias`는 말그대로 별명인데 여러개의 경로에 별칭을 줘서 파일을 가져올 때 쉽게 가져올 수 있다. 위에서는 `imgs`라는 별칭으로 `/public/images` 경로를 설정했다. 만약에 예시로, `./public/images` 라는 경로에 `woowabros.png`라는 이미지가 있다면 아래와 같이 사용할 수 있다.\n\n  ```javascript\n  import woowa from 'imgs/woowabros.png'\n  ```\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step6\"\u003e 🤡 index.html 생성하기\u003c/h2\u003e\n\n요즘 SPA 프로젝트를 보면 `index.html`을 아래와 같은 형식으로 생성한다.\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003ctitle\u003e하이\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"App\"\u003e\u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n\n```\n\nstatic 한 파일을 번들링해서 하나의 js 파일로 만들고 이를 `index.html` 파일에서 불러오는 형식으로 많이 개발하는데, 이는 `HtmlWebpackPlugin`  과 함께라면 가능하다ㅎㅎ,,\n\n```bash\nnpm i html-webpack-plugin -D\n```\n\n설치 후 `webpack.config.js` 를 수정하자\n\n```javascript\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n... 생략\n\n  plugins: [\n    new MiniCssExtractPlugin({\n      filename: \"style.css\", // 원하는 filename\n    }),\n    new HtmlWebpackPlugin({\n      template: path.resolve(__dirname, './public/index.html'),\n      inject: true,\n      filename: path.resolve(__dirname, './dist/index.html'),\n    }),\n  ],\n};\n\nmodule.exports = config;\n```\n\n`plugins` 부분에 코드를 추가했다.\n\n- `template` 은 `index.html`을 생성할 때 어떤 경로의 `html` 파일을 참조할 건지 적는다.\n- `inject`는 번들링한 파일을 자동으로 불러올지 말지 여부이다.\n- `filename`에 지정된 경로에 `index.html`이 생성된다. \n- `html` 파일 이름이 꼭 `index.html` 이지 않아도 된다. `template`, `filename`에 지정한 이름으로 참조하고 생성한다.\n\n`template` 에  `./public` 경로의 `index.html`을 참조하기로 했으니 만들어주자.\n\n**(프로젝트 root경로)/public/index.html**\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" /\u003e\n    \u003ctitle\u003e하이\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"App\"\u003e\u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step7\"\u003e 🥳 Webpack으로 개발서버 띄우기\u003c/h2\u003e\n\nwebpack으로 빌드한 후 서버를 띄우기 위해서는 `webpack-dev-server` plugin이 필요하다.\n\n```bash\nnpm i webpack-dev-server -D\n```\n\n**webpack.config.js**\n\n```javascript\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\nconst path = require(\"path\");\n\nconst config = {\n  entry: \"./src/index.js\",\n  output: {\n    path: path.resolve(__dirname, \"./dist\"),\n    filename: \"build.js\",\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: \"babel-loader\",\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.(sa|sc|c)ss$/,\n        use: [MiniCssExtractPlugin.loader, \"css-loader\", \"sass-loader\"],\n      },\n      {\n        test: /\\.(png|jpe?g|gif|svg|ico)$/,\n        use: [\n          {\n            loader: \"file-loader\",\n            options: {\n              name: \"images/[name].[ext]?[hash]\",\n              limit: 10000,\n            },\n          },\n        ],\n      },\n    ],\n  },\n  /* webpack-dev-server */\n  mode: \"development\", // webpack-dev-server에만 쓰는 것은 아님\n  devtool: \"inline-source-map\",// webpack-dev-server에만 쓰는 것은 아님\n  devServer: {\n    contentBase: \"./dist\",\n    port: 9000,\n    hot: true,\n  },\n  /* webpack-dev-server */\n  \n  plugins: [\n    new MiniCssExtractPlugin({\n      filename: \"style.css\",\n    }),\n  ],\n};\n\nmodule.exports = config;\n\n```\n\n- `mode`: `webpack-dev-server`에서만 쓰이는 옵션은 아니지만 넣어야할 때가 된것같다. 이 옵션은 `development`와 `production`, `none` 3개 옵션이 있는데 각 옵션마다 기본적으로 설정되는 플러그인, NODE 환경변수(process.env)가 다르다. 자세한 사항은 [여기](https://ibrahimovic.tistory.com/51) 를 참고하자\n- `devtool` : debug tool을 정해준다. source-map은 원본 소스와 난독화된 소스를 맵핑 시켜주는 하나의 방법이다. 자세한 사항은 [여기](https://perfectacle.github.io/2016/11/14/Webpack-devtool-option-Performance/) 를 참고하자\n- `contentBase` : 서버를 띄울 때 서빙할 폴더 설정\n- `port`: 서버를 띄울 port\n- `hot`: 코드가 수정되면 자동으로 re-build 한다.\n- webpack-dev-server의 빌드 결과물은 사실, 실제 파일로 빌드되진 않고 메모리에 저장된다.\n\n**package.json**\n\nscripts 부분에 추가해준다.\n\n```json\n...생략\n\"build\": \"webpack-dev-server --open --config webpack.config.js\",\n```\n\n추가한 뒤 `npm run build` 커맨드로 9000 포트에 개발 서버를 띄울 수 있다.\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step8\"\u003e 😸 개발서버, 실서버 환경 분리하기\u003c/h2\u003e\n\n`webpack-dev-server`는 개발 환경에만 종속되는 plugin이기도 하고 개발서버, 실서버 환경을 분리해서 작성해주는게 좋기 때문에 나눠보자. 이때 공통된 속성을 정의해서 불러오기 위해 `webpack-merge` 모듈이 필요하다\n\n```bash\nnpm i webpack-merge -D\n```\n\n**webpack.common.js**\n\n```javascript\nconst MiniCssExtractPlugin = require(\"mini-css-extract-plugin\");\nconst { CleanWebpackPlugin } = require(\"clean-webpack-plugin\");\nconst path = require(\"path\");\n\nconst config = {\n  entry: \"./src/index.js\",\n  output: {\n    path: path.resolve(__dirname, \"./dist\"),\n    filename: \"build.js\",\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.js$/,\n        use: \"babel-loader\",\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.(sa|sc|c)ss$/,\n        use: [MiniCssExtractPlugin.loader, \"css-loader\", \"sass-loader\"],\n      },\n      {\n        test: /\\.(png|jpe?g|gif|svg|ico)$/,\n        use: [\n          {\n            loader: \"url-loader\",\n            options: {\n              // useRelativePath: true,\n              name: \"images/[name].[ext]?[hash]\",\n              // publicPath: \"./dist/\",\n              limit: 10000,\n            },\n          },\n        ],\n      },\n    ],\n  },\n  plugins: [\n    new MiniCssExtractPlugin({\n      filename: \"style.css\",\n    }),\n    new CleanWebpackPlugin({ filename: \"build.js\" }),\n  ],\n};\n\nmodule.exports = config;\n```\n\n`webpack-dev-server` 관련 코드를 제거하고 `CleanWebpackPlugin` 설치하여 추가적으로 적용했다. `CleanWebpackPlugin`은 re-build될 때 기존의 결과물을 제거해준다.\n\n**webpack.development.js**\n\n```javascript\nconst commonConfig = require(\"./webpack.common\");\nconst { merge } = require(\"webpack-merge\");\n\nmodule.exports = merge(commonConfig, {\n  mode: \"development\",\n  devtool: \"inline-source-map\",\n  devServer: {\n    contentBase: \"./dist\",\n    port: 9000,\n    hot: true,\n  },\n});\n\n```\n\n공통으로 설정한 config 모듈을 불러와서 `merge` 시켰다.\n\n**webpack.prod.js**\n\n```javascript\nconst commonConfig = require(\"./webpack.common\");\nconst { merge } = require(\"webpack-merge\");\n\nmodule.exports = merge(commonConfig, {\n  mode: \"production\",\n});\n\n```\n\n이것은 production 환경.\n\nconfig 파일을 분리했으니 `package.json`의 scripts도 수정해야한다.\n\n**package.json**\n\n```json\n...생략\n\"build\": \"webpack-dev-server --open --config webpack.dev.js\",\n\"build:production\": \"webpack --config webpack.prod.js\",\n```\n\n- `npm run build`는 webpack-dev-server로 개발서버를 띄우고 webpack.dev.js를 참조한다.\n- `nom run build:production`은 개발서버를 띄우지 않고 번들링만 한다. 그리고 webpack.prod.js를 참조한다.\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step9\"\u003e😚 React 사용하기\u003c/h2\u003e\n\nReact를 사용하기 위해서는 `@babel/preset-react`를 설치해야한다.\n\n```bash\nnpm i @babel/preset-react -D\n```\n\n`@babel/preset-react`에는 3가지 plugin이 포함되는데, React 문법인 `jsx`를 사용하게 해주고 번들링하는 과정에르 `jsx`를 `createElement`를 통한 VanilaJS로 변환해준다. 자세한 사항은 공식문서인 [여기](https://babeljs.io/docs/en/babel-preset-react) 를 참고하자.\n\n설치한 `preset`을 `babel.config.js`에 추가해주자.\n\n**babel.config.js**\n\n```javascript\nmodule.exports = function (api) {\n  api.cache(true); // plugin \u0026 preset을 캐싱하여 다시 실행하지 않음\n\n  const presets = [\n    [\n      \"@babel/preset-env\", // 필수적인 플러그인이 모여있음\n      {\n        targets: \"\u003e 1%, not dead\",\n        useBuiltIns: \"usage\"\n        corejs: 3,\n        modules: false, \n      },\n    ],\n    [\"@babel/preset-react\"], // 요기\n  ];\n\n  const plugins = [\n    [\n      \"module-resolver\",\n      {\n        root: [\"./src\"],\n        alias: {\n          imgs: \"./public/images\",\n        },\n      },\n    ],\n  ];\n\n  return {\n    presets,\n    plugins,\n  };\n};\n\n```\n\n잘 동작하는지 테스트할 겸 코드도 작성해보자. 그럼 React를 설치해야겠지?\n\n```bash\nnpm i react react-dom \n```\n\n**App.js**\n\n```javascript\nimport React from 'react'\n\nexport default function App() {\n  return ( \u003cdiv\u003ehello world\u003c/div\u003e)\n}\n```\n\n**index.js**\n\n```javascript\nimport React from \"react\";\nimport ReactDOM from \"react-dom\";\nimport App from \"./components/App.js\";\n\nReactDOM.render(\u003cApp /\u003e, document.querySelector(\"#App\"));\n```\n\n결과..\n\n![image](https://user-images.githubusercontent.com/39187116/89734750-fcd62c00-da98-11ea-87af-4a4c21605467.png)\n\n잘 뜬다. 끗!!\n\n\u003cbr/\u003e\n\n\u003ch2 id=\"step10\"\u003e👿 후기\u003c/h2\u003e\n\n누가 webpack은 학문을 공부하는 것이라고 했던 것 같은데, 막상 해보니까 진짜인 것 같다. 해도 해도 끝이 없이 나온다 (옵션들이..). 하지만 이번 경험을 토대로 혼자 프론트엔드 개발 환경 셋팅을 어느정도 할 줄 알게 된 것 같다. babel \u0026 wepack 설정 이라는 두려움도 없어지고 ㅎㅎ,, 개발을 하면서 그떄 그때 필요한 플러그인들을 적용하며 알아가고 일단은 여기까지만 해야겠다..\n\n\n\n## Reference\n\n1. https://babeljs.io/docs/en/\n2. https://moonformeli.tistory.com/28\n3. https://beomy.tistory.com/41\n4. [https://github.com/parkjihwanjay/boilerplate/wiki/webpack,-babel-%EC%84%A4%EC%A0%95](https://github.com/parkjihwanjay/boilerplate/wiki/webpack,-babel-설정)\n5. https://github.com/hg-pyun/minipack-kr/blob/master/src/minipack.js\n6. [https://github.com/jungcome7/5.Study/blob/master/dependencies%20vs.%20devDependencies.md](https://github.com/jungcome7/5.Study/blob/master/dependencies vs. devDependencies.md)\n7. https://poiemaweb.com/es6-babel-webpack-1\n8. [https://medium.com/naver-fe-platform/webpack%EC%97%90%EC%84%9C-tree-shaking-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-1748e0e0c365](https://medium.com/naver-fe-platform/webpack에서-tree-shaking-적용하기-1748e0e0c365)\n9. http://jeonghwan-kim.github.io/js/2017/05/15/webpack.html\n\n10. https://wonism.github.io/react-native-alias/\n11. https://ibrahimovic.tistory.com/51\n12. https://perfectacle.github.io/2016/11/14/Webpack-devtool-option-Performance/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoonguk%2Fwebpack-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoonguk%2Fwebpack-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoonguk%2Fwebpack-boilerplate/lists"}