FrontEnd/프론트엔드 개발환경의 이해와 실습 (webpack, babel, esli

NPM , Webpack

Tony Lim 2024. 8. 26. 13:19
728x90

NPM install을 통해서 CDN, 직접 source download대신에 외부 패키지 의존성을 관리할 수 있다.

 

Webpack

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="src/math.js"></script>
  <script src="src/app.js"></script>
</body>
</html>
function mySum(a,b) {
  return a + b;
}

이 상태에서 index.html이 브라우저에 렌더리되면 전역 scope가 오염이된다. 어디에서든 mySum을 접근을 할 수 있게된다.

var math = math || {};

(function() {
  function mySum(a,b) {
    return a + b
  }

  math.sum = mySum
})()

math를 전역scope로 두고 다른 곳에서는 math.sum으로 접근하도록 변경한다.

 

AMD(Asynchronous Module Definition) 브라우저 같이 javascript 를 로딩해서 사용해야하는 경우 사용해야하는 스펙이다.

UMD(Universial Module Defintion) 은 AMD, CommonJS방식까지 지원하는 통합스펙이다.

최종적으로는 ES2015에서 표준 모듈 시스템을 내놓았고 export하면 import하는 방식이다.

  <body>
    <script type="module" src="src/app.js"></script>
  </body>

크롬의 경우 type="module"을 주게 되면 app에서 제대로 import가 동작을 하게된다. 하지만 이것은 브라우저 별로 다 다르니 문제가된다. 이때 웹팩이 필요한것이다.


webpack

node_modules/.bin/webpack --mode development --entry ./src/app.js --output-path ./dist

강의 와다르게

"webpack": "^5.94.0",
"webpack-cli": "^5.1.4"

webpack5에서 진행을 했다. --output-path에 명시된 argument는 모조리 다 directory로 변경된다는것을 주의하자

https://www.inflearn.com/community/questions/1364339/webpack5-%EC%97%90%EC%84%9C-open-index-html%ED%95%98%EB%8A%94%EB%B2%95-%EC%A7%88%EB%AC%B8#662682

 

webpack5 에서 open index.html하는법 + 질문 - 인프런 | 커뮤니티 질문&답변

누구나 함께하는 인프런 커뮤니티. 모르면 묻고, 해답을 찾아보세요.

www.inflearn.com

 

const path = require("path")

module.exports = {
  mode: "development",
  entry: {
    main: "./src/app.js",
    main2: "./src/app.js"
  },
  output: {
    path: path.resolve("./dist"),
    filename: "[name].js"
  }
}

설정으로는 위와같이 하고 [name]은 entry의 main을 return한다. 동적으로 여러개의 entry가 있을 때 설정하기 위함이다.

dist에 main.js main2.js가 따로 생긴다.

또한 webpack은 node위에서 동작하는것이다.


로더

웹팩은 모든 파일을 모듈로 바라본다. 자바스크립트로 만든 모듈 뿐만 아니라 스타일시트, 이미지, 폰트까지도 전부 모듈로 보기 때문에 import 구문을 사용하면 자바스킯트 코드 안으로 가져올 수 있다.

이것이 가능한 이유는 웹팩의 로더 덕분이다. 로더는 타입스크립트 같은 다른 언어를 자바스크립트 문법으로 변환해주거나 이미지를 data url형식의 문자열로 변환한다. 또한 css 파일을 자바스크립트에서 직접 로딩할 수 있도록 해준다

module.exports = function myWebpackLoader (content) {
  console.log("myWebpackLoader");
  return content;
}

간단한 my-webpack-loader.js 를 만들었다 보통 함수로 만든다.

const path = require("path")

module.exports = {
  mode: "development",
  entry: {
    main: "./src/app.js"
  },
  output: {
    path: path.resolve("./dist"),
    filename: "[name].js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          path.resolve("./my-webpack-loader.js")
        ]
      }
    ]
  }
}
$ npm run build

> sample@1.0.0 build
> webpack

myWebpackLoader
myWebpackLoader
asset main.js 4.07 KiB [emitted] (name: main)
asset main2.js 4.07 KiB [emitted] (name: main2)
runtime modules 1.31 KiB 6 modules
cacheable modules 100 bytes
  ./src/app.js 52 bytes [built] [code generated]
  ./src/math.js 48 bytes [built] [code generated]
webpack 5.94.0 compiled successfully in 212 ms

~/workspace/frontend/sample ⌚ 11:03:40

설정을 하고 빌드를 하면 2번 호출되는것을 확인할 수 있다. 이는 app.js , math.js 2개를 각각 로드했기 때문이다.

 


자주 사용되는 loader

 

css-loader = css파일을 자바스크립트에서 불러와 사용하려면 css를 모듈로 변환하는 작업이 필요하다. 이를 대신해준다.

ERROR in ./src/app.css 1:5
Module parse failed: Unexpected token (1:5)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> body {
|   background-color: green;
| }
 @ ./src/app.js 1:0-19

webpack 5.94.0 compiled with 1 error in 175 ms

~/workspace/frontend/sample ⌚ 11:17:12
$

app.js 에서 import "app.css"를 작성하고 build하려니 webpack이 css를 인지 못하는 상황이다.

  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'css-loader'
        ]
      }
    ]
  }

css loader를 통해 css를 모듈로 로드해서 webpack build에 성공했다. 하지만 만들어진 main.js에 그냥 글자만 들어간 상태이지 CSSOM(css object model) 형태가 아니므로 브라우저에 반영이 되지 않는다.

style-loader가 추가적으로 필요하다.

file-loader 같은 경우 png파일 같은것을 로드할 때 쓰인다.

    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.png$/,
        loader: "file-loader",
        options: {
          publicPath: "./dist/",
          name: "[name].[ext]?[hash]"
        }
      }

publicPath는 파일로더가 처리하는 파일을 모듈로 사용했을  경로앞에 추가되는 문자열이다. 파일을 호출하는 쪽에서는 경로앞에 dist를 붙여서 호출하게 된다.
현재 css를 호출하는 js를 담고 있는 index.html의 위치에서 dist 폴더 안에 들어가야 file들이 있기 때문에 필요한것이다. 아래에서 나오는 HtmlWebpackPlugin을 써서 같이 번들링 하면 같은 위치가 됨으로 지워도 무방하다.

./dist/picture.png?1a3asd9...  처럼 png 파일이름이 생성되고 hash는 매번 빌드될때 캐싱되는 것을 무시하기 위함이다. 매번 새로운 사진을 빌드하게 됨

또한 use의 array의 순서도 중요하다 array의 오른쪽부터 loader가 수행된다. 즉 css loader가 만든 결과물을 style-loader가 처리하는 순서로 동작한다.

 

url-loader 는 작은 img파일 같은 경우에는 <img src="data:image/png;base64, ~~~~"/> 이런식으로 html에 base64로 인코딩된 byte를 넣어줄 수 있다. 그러면 네트워크 통신을 통해 가져오지 않는다는 장점이 있다.

      {
        test: /\.(png|jpg|gif|svg)$/,
        loader: "url-loader",
        options: {
          publicPath: "./dist/",
          name: "[name].[ext]?[hash]",
          limit: 20000, //20kb
        }
      }

2kb 이상이면 file-loader가 실행되고 그 이하의 경우에만 url-loader가 실행된다.

 


플러그인

로더가 파일 단위로 처리하는 반면 플러그인은 번들된 결과물을 처리한다. 번들된 자바스크립트를 난독화 한다거나 특정 텍스틀르 추출하는 용도로 사용한다.

class MyWebpackPlugin {
  apply(compiler) {
    compiler.hooks.emit.tapAsync("MyWebpackPlugin", (compilation, callback) => {
      const source = compilation.assets["main.js"].source();
      const banner = [
        "/**",
        " * This is result of processing by BannerPlugin.",
        " * Build Date: 2023-08-31",
        " */",
      ].join("\n");
      const objectSourceNew =
        new compilation.compiler.webpack.sources.RawSource(
          banner + "\n\n" + source
        );
      compilation.updateAsset("main.js", objectSourceNew);
      callback();
    });
  }
}

module.exports = MyWebpackPlugin;
  plugins: [
    new MyWebpackPlugin(),
  ]

plugin은 번들된 결과에 접근할 수 있다. 위예시는 번들링이된 main.js 첫줄에 bullshit string을 입력하게 해주는 결과이다.

하지만 실행하고 나면

> sample@1.0.0 build
> webpack

(node:56989) [DEP_WEBPACK_COMPILATION_ASSETS] DeprecationWarning: Compilation.assets will be frozen in future, all modifications are deprecated.
BREAKING CHANGE: No more changes should happen to Compilation.assets after sealing the Compilation.
        Do changes to assets earlier, e. g. in Compilation.hooks.processAssets.
        Make sure to select an appropriate stage from Compilation.PROCESS_ASSETS_STAGE_*.
(Use `node --trace-deprecation ...` to show where the warning was created)

compile다 된건 건들지 말라한다.

 


자주 사용하는 플러그인

BannerPlugin = 결과물에 빌드 정보나 커밋 버전 같은것을 추가할 수 있다.

    new webpack.BannerPlugin({
      banner: `
        Build Date: ${new Date().toLocaleDateString()}
        Commit Version: ${childProcess.execSync("git rev-parse --short HEAD")}
        Author : ${childProcess.execSync("git config user.name")}
      `
    })

주로 정적파일들이 잘 갱신이 되었는지 확인하기 위해서 남긴다고 한다.

 

DefinePlugin = 앱은 개발,운영으로 나눠서 운영한다. 가령 환경에 따라 API 서버 주소가 다를 수 있다. 이런 환경정보는 소스가 아닌곳에서 관리하는게 용이하다. 이때 쓸 수 있다. 주로 환경변수로 제공된다.

    new webpack.DefinePlugin({
      TWO: "1+1",
      "api.domain": JSON.stringify("http://dev/api.domain.com")
    })
console.log(process.env.NODE_ENV);
console.log(TWO)
console.log(api.domain)

이런식으로 접근이 가능하다. 빌드할 때마다 다른 서버 도메인 주소를 soruce를 고치지 않고 얻어 낼 수 있다.

 

HtmlTemplatePlugin = HTML 파일을 후처리하는 데 사용된다.

    <title>Document<%= env %></title>
    new HtmlWebpackPlugin({
      template: "./src/index.html",
      templateParameters: {
        env: process.env.NODE_ENV === "development" ? "(개발용)" : ""
      },
      minify: process.env.NODE_ENV === "production" ? {
        collapseWhitespace: true,
        removeComments: true,
      } : false
    })

template은 어떤 html을 bundling 에 포함할것인지 적는것이다.

ejs문법으로 쓰여진 env 변수에 빌드 할때 주어진 환경변수에 따라 정해주고 주석이나 whitespace를 지울지 말지 결정할 수 있다.

 

CleanWebpackPlugin = 빌드 이전 결과물을 제거하는 플러그인이다.

    new CleanWebpackPlugin({
    })

 

 

MiniCssExtractPlugin = 스타일 시트가 점점 많아지면 하나의 자바스크립트 결과물로 만드는 것이 부담일 수 있다.
이때 번들 결과에서 스타일시트 코드만 뽑아서 별도의 css파일로 만들어 역할에 따라 파일을 분리하는것이 좋다.
큰 거 하나를 다운로드 받는것보다 작은 여러개를 병렬로 다운 받는게 빠르다.

https://www.inflearn.com/community/questions/1367557/%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%97%90%EC%84%9C-%EC%A7%88%EB%AC%B8%EC%9D%B4-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4

 

자주 사용하는 플러그인 에서 질문이 있습니다. - 인프런 | 커뮤니티 질문&답변

누구나 함께하는 인프런 커뮤니티. 모르면 묻고, 해답을 찾아보세요.

www.inflearn.com

 

 

 

728x90