본문 바로가기

Next-step, javascript TDD & cleanCode

Lotto 미션 과제중 css Loading 문제 (webpack)

 오늘의 교훈 - 부끄러울줄 알아야 한다.

 

내게 있었던일 

Next-step에서 Lotto 미션을 진행하고 있었습니다. html, css, js를 이용해서 Lotto를 생성하고 결과를 알려주는 사이트를 만드는 것이었습니다. 실행과정은 다음과 같습니다.

 

npm install
npm run start-step-web

 

 

어떤 구조로 되어 있을까요 package.json에 scripts를 통해서 

    "start-step-web": "webpack serve --open --config step-web.config.js"

다음과 같은 명령어를 수행합니다. 저는 이부분을 잘 이해를 못했던거 같은데요. 그래서 문제의 원인을 재대로파악하지 못하고 시간을 허비했던거 같습니다.  그래서 이부분에 대해서 어떤 의미를 가지고 있는지 찾아봤습니다.

 

Webpack

  • webpack은 모듈 번들러(Module bundler)로, 여러 파일과 의존성을 하나의 파일로 버들링하여 웹 애플리케이션을 최적화힙니다.

serve

  • 'serve'는 webpack dev server를 실행하는 명령어입니다. Webpack Dev Server는 개발 중에 애플리케이션을 로컬 서버에
    호스팅하여 변경 사항을 즉시 반영할 수 있는 환경을 제공합니다. 

--open

  • '--open' 플래그는 개발 서버가 시작될 때 기본 웹 브라우저를 자동으로 열도록 합니다. 이는 개발중에 편리하게 애플리케이션을 바로 확인할수 있게 합니다. 

--confing

  • '--config' 플래그는 Webpack이 특정 구성파일을 사용하도록 지시합니다. 기본적으로 Webpack은 프로젝트 디렉토리에 있는 'webpack.config.js' 파일을 찾지만, '--config' 플래그를 사용하면 다른 경로에 있는 구성 파일을 사용할 수 있습니다. 

결론적으로

'webpack serve --open --config step-web.config.js' 명령어는 Webpack Dev Serve를 시작하고, 'step-web.config.js'라는 구성 파일을 사용하여 서버를 설정하며, 서버가 시작되면 기본 웹 브라우저를 자동으로 엽니다.

 


그렇다면 webpack dev server를 사용해서 로컬 환경에서 작업할 수 있는 공간을 제공한다는 것을 알았다. 그렇다면 css가 재대로 반영하지 않았던 이유는 webpack.config.js에 있다는 것을 알게됬다. 그래서 webpack.config.js 파일을 보게되면, 

 

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  devServer: {
    port: 9000,
  },
  entry: './src/step-web-index.js',
  output: {
    filename: 'web-bundle.js',
    path: path.resolve(__dirname, 'dist'),
  },
  resolve: {
    extensions: ['.js', '.mjs', '.css'],
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
            },
          },
        ],
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: './index.html',
    }),
  ],
  devtool: 'inline-source-map',
};

 

이런식의 구성을 알 수 있는데, 여기서 발생한 나의 2번째 문제 안에 프로퍼티들이 어떤 동작을하는지에 대해 알지 못했다. 사실 한두번쯤은 본적이 있다. 공부했던 기억도 있으나 자주 쓰지 않았거나 공부를 재대로 하지 않았는지 내가 뭘 실수 하는지 파악하지 못했다. 

 

Target

  • 다양한 환경 컴파일을 가능하게 한다.
    • 기본값 : web
    • "es5"를 넣으면 지정된 ECMAScript 5버전으로 컴파일한다.
    • Browser Compatibility 호환성과 연관됨. 

Entry

  • Webpack이 번들링을 시작하는 진입점.
    • entry 파일이 모든 것을 모아서 번들링함. 
    • webpack이 내부의 디펜던시 그래프를 생성하기 위해 사용해야 하는 모듈 
    • 기본값 ./src/index.js

Output

  • 번들 내보낼 위치 path, 번들 파일명 fiename 지정방법을 웹팩에 알려주는 역할 
    • 기본 출력 파일 : ./dist/main.js
    • 생성된 기타 파일 : ./dist 폴더
    • path : 번들링된 결과물이 담길 주소 
    • path 속성 사용 시 path 모듈을 사용해야만 함 
const path = require('path');

module.exports = {
	...
  output: {
    path: path.resolve(__dirname, "docs"), // 절대 경로로 설정 해야 한다. 
    filename: "app.bundle.js",
    clean: true // 번들링할때마다 dist폴더 정리하기
  },
};

 

Loader

  • Javascript, JSON이 아닌 파일을 불러오는 역할 
  • 웹팩은 기본적으로 JS,JSON파일만 이해한다. 그러나 Loaders 사용 시, 웹팩이 다른 유형 파일을 처리하거나, 유효한 모듈로 변환해 앱에 사용하거나 dependency 그래프에 추가할 수 있음.  
    • test(필수) : 변환이 필요한 파일들을 식별하기 위한 속성
    • use(필수) : 변환을 수행하는데 사용되는 로더를 가르키는 속성 
    • exclude : 바벨로 컴파일 안할 파일/폴더를 지정. 
    • (include : 반드시 컴파일해야 할 파일/폴더 지정 가능)
    • module.rules 아래에 정의해야 한다. 그냥 Rule 아래에 정의하면 webpack에서 경고 발생 
module.exports = {
	...
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
        exclude: /node_modules/,
      },
    ],
  },
};

 

 

Plugins

  • 번들 최적화, 에셋 관리, 환경변수 주입 등 광범위한 작업 수행 가능 
    • require()로 플러그인 먼저 요청
    • plugins 배열에 사용하고자 하는 플러그인을 추가
    • 다른 목적으로 플러그인을 재사용하도록 설정할 수 있기 때문에 new 연산자를 사용해 호출하여 플러그인의 인스턴스 생서
    • HtmlWebpackPlugin : 생성된 모든 번들을 자동 삽입하여 애플리케이션용 HTML 파일을 생성
    • CleanWebpackPlugin : 빌드 이전 결과물을 제거하는 플러그인 

Optimizaiton 최적화 

  • 웹팩 버전4부터 선택항목에 따른 최적화 가능
    • mininize : TerserPlugin 또는 optimization.minimize에 명시된 plugins로 bundle 파일을 최소화(=압축) 시키는 작업 여부를 결정 
    • minimizer : default minimizer을 커스텀된 TerserPlugin 인스턴스를 제공해서 재정의 할 수 있다.
module.exports = {
  ...
  optimization: {
    minimizer: [
      new CssMinimizerPlugin(),
    ]
  }
};

 

 


그래서 문제가 무엇이었을까? 

웹팩은 entry 프로퍼티에 기술된 파일을 읽어서 번들링한 뒤 output으로 정의한 폴더로 결과물을 내보낸다. 지금의경우, index.html이라는 정적 파일이 template이고, entry로 정의한 파일들이 index.html 와 "결합" 되는 부분입니다. 따라서 index.htm의 link/script로 css/js 를 import 해도 동작하지 않습니다.

 

 

Webpack의 번들링 과정

Webpack은 다음과 같은 과정을 통해 파일을 번들링합니다:

  1. Entry Point: Webpack은 entry 속성에 정의된 파일을 시작점으로 하여, 모든 의존성 파일을 탐색합니다.
  2. Module Resolution: Webpack은 각 모듈을 해석하고, 필요한 로더를 통해 파일을 변환합니다. 예를 들어, css-loader와 style-loader를 사용하여 CSS 파일을 JavaScript 모듈로 변환합니다.
  3. Bundling: 변환된 모듈들을 하나 또는 여러 개의 번들 파일로 묶습니다.
  4. Output: 최종 번들 파일을 output 속성에 정의된 경로에 저장합니다.

HtmlWebpackPlugin의 역할

HtmlWebpackPlugin은 Webpack 설정에서 자주 사용되는 플러그인으로, 다음과 같은 기능을 합니다:

  1. HTML 템플릿 처리: 지정된 템플릿 파일(예: index.html)을 기반으로 최종 HTML 파일을 생성합니다.
  2. 자동 스크립트 삽입: 번들된 JavaScript 파일을 <script> 태그로 자동으로 삽입합니다.
  3. CSS 삽입: 번들된 CSS 파일을 <link> 태그로 자동으로 삽입합니다.

동작하지 않는 이유

Webpack과 HtmlWebpackPlugin을 사용하면, HTML 파일에 번들된 자원을 수동으로 참조할 필요가 없습니다.

 

해결 방안은? 

css 파일을 번들링 과정에 포함시켜야된다. 

  entry: ['./src/js/index.js', './src/css/index.css'],

 

동작 원리

  1. Entry 설정: entry 속성에 JavaScript 파일(./src/js/index.js)과 CSS 파일(./src/css/index.css)을 모두 포함시킵니다.
    • Webpack은 이 파일들을 읽어 들여 각각의 의존성을 분석합니다.
    • CSS 파일은 css-loader와 style-loader를 통해 처리됩니다.
  2. Module Rules: Webpack은 설정된 로더(babel-loader, css-loader, style-loader)를 사용하여 JavaScript와 CSS 파일을 처리합니다.
    • babel-loader: JavaScript 파일을 트랜스파일합니다.
    • css-loader: CSS 파일을 JavaScript 모듈로 변환합니다.
    • style-loader: 변환된 CSS 모듈을 <style> 태그로 변환하여 HTML 파일에 삽입합니다.
  3. HtmlWebpackPlugin: template으로 지정된 HTML 파일을 기반으로 최종 HTML 파일을 생성하고, 번들된 JavaScript와 CSS 파일을 자동으로 포함합니다.
    • <script src="web-bundle.js"></script>: 번들된 JavaScript 파일을 자동으로 포함합니다.
    • <style> 태그: 번들된 CSS 파일을 자동으로 포함합니다.

 


결론

나는 혼자서 이문제에 대해서 알지 못했다. 그렇기에 부끄러움을 무릅쓰고 리뷰어님께 질문을 했다. 다행히도 리뷰어님은 친절히 설명해주셨고, 나는 문제에 대해서 학습할 수 있었다. 부끄럽다고 머뭇거리는게 아니라 알려고 노력하는 자세를 갖자.