Webpack 알아보기 (2)

삔아·2021년 11월 6일
0

webpack

목록 보기
2/4
post-thumbnail

3. Webpack 주요 속성


웹팩의 빌드(파일 변환) 과정을 이해하기 위해서는 아래 4가지 주요 속성에 대하여 알고 있어야 합니다.

  • entry
  • output
  • loader
  • plugin

각 주요 속성에 대하여 자세히 살펴보겠습니다.

(1) Entry


💡 entry 속성은 웹팩에서 웹 자원을 변환하기 위해 필요한 최초 진입점이자 자바스크립트 파일 경로입니다.

동적으로 로드되는 모듈은 entry대상이 아닙니다.

간단한 규칙은, HTML 페이지 마다 하나의 포인트를 가진다는 점입니다.
그러니 SPA(Single Page Application)은 하나의 포인트를 가지게 되고, MPA(Multiple Page Application)은 페이지 별로 하나의 포인트를 가지게 됩니다.

entry 에는 여러가지 유형이 들어올 수 있습니다.

Entry 유형

1) 문자열이 들어오는 경우

문자열이 들어오는 경우 시작시 로드되는 모듈로 해석합니다.

// webpack.config.js 
module.exports = { 
	entry: './src/myfolder/file.js' 
};

위 코드는 웹팩을 실행했을 때 src/myfolder 폴더 밑의 file.js 를 대상으로 웹팩이 빌드를 수행하는 코드 입니다.

해당 구문은 다음 내용의 축약된 표현이기도 합니다.

module.exports = {
  entry: {
    main: './path/to/my/entry/file.js',
  },
};

2) 배열이 들어오는 경우

module.exports = { 
	entry: { 
		main: ['./src/myfolder/file_01.js', './src/myfolder/file_02.js'] 
	} 
};

배열을 전달하면 multi-main entry 가 생성 됩니다.

여러개의 의존성 파일을 하나의 chunk 로 모으고 이들의 의존성 그래프를 생성하고 싶을때 유용하게 사용할 수 있지만 라이브러리 같은 하나의 엔트리 포인트를 갖는 애플리케이션 혹은 Webpack 설정을 빠르게 설정하려는 경우에는 좋은 선택이지만 설정을 확장하는데는 그다지 유연성이 없습니다.

3) 객체가 들어오는 경우

module.exports = { 
	entry: {
		app: './src/app.js',
		adminApp: './src/adminApp.js'
	}
};

여러개의 엔트리포인트를 지정할 수 있습니다.

⚠️ 참고
플러그인에 의해 생성된 엔트리 포인트만 있는 경우 빈 객체 {}를 entry에 전달할 수 있습니다.

  • EntryDescription object 객체 속성 지정 엔트리 포인트 설명이 있는 객체입니다. 다음 속성을 지정할 수 있습니다.
    • dependOn: 현재 엔트리 포인트가 의존하는 엔트리 포인트. 이 엔트리 포인트를 로드하기 전에 로드해야 합니다.

    • filename: 디스크에 있는 각 출력 파일의 이름을 지정합니다.

    • import: 시작 시 로드되는 모듈입니다.

    • library: 현재 엔트리에서 라이브러리를 번들링하려면 라이브러리 옵션을 지정합니다.

    • runtime: 런타임 청크의 이름입니다. 설정되면 이 이름의 런타임 청크가 생성되고 그렇지 않으면 기존 엔트리 포인트의 이름이 사용됩니다.

    • publicPath: 브라우저에서 참조할 때 이 엔트리의 출력 파일에 대한 공용 URL 주소를 지정하세요. 또한 output.publicPath도 참고하세요.

      더 자세히 는 해당 링크 를 참고해주세요.

💡 HTML의 문서에 정확히 하나의 엔트리 포인트를 사용하는 것이 좋습니다.

Entry 파일에 들어갈 내용

💡 entry 속성에 지정된 파일에는 웹 애플리케이션의 전반적인 구조와 내용이 담겨져 있어야 합니다.

웹팩이 해당 파일을 가지고 웹 애플리케이션에서 사용되는 모듈들의 연관 관계를 이해하고 분석하기 때문에 애플리케이션을 동작시킬 수 있는 내용들이 담겨져 있어야 합니다.

예를 들어, 블로그 서비스를 웹팩으로 빌드한다고 했을 때 코드의 모양은 아래와 같을 수 있습니다.

// index.js
import LoginView from './LoginView.js';
import HomeView from './HomeView.js';
import PostView from './PostView.js';

function initApp() {
  LoginView.init();
  HomeView.init();
  PostView.init();
}

initApp();

위 코드는 해당 서비스가 싱글 페이지 애플리케이션 이라고 가정하고 작성한 코드입니다.
사용자의 로그인 화면, 로그인 후 진입하는 메인 화면, 그리고 게시글을 작성하는 화면 등 웹 서비스에 필요한 화면들이 모두 index.js 파일에서 불려져 사용되고 있기 때문에 웹팩을 실행하면 해당 파일들의 내용까지 해석하여 파일을 빌드해줄 것입니다.

https://joshua1988.github.io/webpack-guide/assets/img/webpack-entry.90e26197.png

위와 같이 모듈 간의 의존 관계가 생기는 구조를 디펜던시 그래프(Dependency Graph)라고 합니다.

앞에서 살펴본 것처럼 엔트리 포인트는 1개가 될 수도 있지만 아래와 같이 여러 개가 될 수도 있습니다.

entry: {
  login: './src/loginView.js',
  main: './src/mainView.js'
}

위와 같이 엔트리 포인트를 분리하는 경우는 싱글 페이지 애플리케이션이 아닌 특정 페이지로 진입했을 때 서버에서 해당 정보를 내려주는 형태의 멀티 페이지 애플리케이션에 적합합니다.

(2) Output


💡 `output` 속성은 웹팩을 돌리고 난 결과물의 파일 경로 (정보 [이름도 포함] ) 를 의미합니다.
module.exports = {
  output: {
    filename: 'bundle.js'
  }
}

앞에서 배운 entry 속성과는 다르게 객체 형태로 옵션들을 추가해야 합니다.

Output 속성 옵션 형태

최소한 filename은 지정해줘야 하며 일반적으로 아래와 같이 path 속성을 함께 정의합니다.

var path = require('path');

module.exports = {
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, './dist')
  }
}

여기서 filename 속성은 웹팩으로 빌드한 파일의 이름을 의미하고, path 속성은 해당 파일의 경로를 의미합니다. 그리고 path 속성에서 사용된 path.resolve() 코드는 인자로 넘어온 경로들을 조합하여 유효한 파일 경로를 만들어주는 Node.js API입니다.

이 API가 하는 역할을 좀 더 이해하기 쉽게 표현하면 아래와 같습니다.

output: './dist/bundle.js'

위 코드에서 사용한 path 라이브러리의 자세한 사용법은 여기를 참고하세요.

  • (참고) path 간단히 알아보기
    • path.join( path1, path2 ... )

      파라미터로 전달받은 경로를 이어서 하나의 경로로 만듭니다.

      path.join('/foo', 'bar', 'baz/example');
      // Returns: 'foo/bar/baz/example'

      자바스크립트의 내장 API에 있는 join 과 비슷한 역할을 하지만 해당 API가 동작하는 OS의 파일 구분자를 이용하여 파일 위치를 조합하기 때문에 OS별로 결과 값이 다를 수 있습니다.

    • path.resolve([from...], to)

      전달받은 경로의 절대 경로를 리턴합니다.

      path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
      /*
      	만약에 현재 디렉토리가 /home/myself/node 라고 가정한다면
      	리턴되는 값은 다음과 같이 유효하지 않은 값들을 가공하여 나오게 됩니다.
      	'/home/myself/node/wwwroot/static_files/gif/image.gif'
      */

      모듈을 해석하는데에 있어 영향을 미치는 옵션으로 웹팩이 스스로 판단하여 경로나 확장자를 처리할 수 있게 도와주는 옵션 입니다.

      💡 이 resolve는 웹팩 번들링을 할 때 기타 복잡한 다수의 loader가 있을 경우 복잡함으로 인해 유효하지 않은 위치가 있다면 자동으로 제거해주고 검증이 된 폴더 위치를 잡아주는것이 resolve의 특성이기 때문에 좀 더 안전한 번들링을 위해서 사용하는 측면이 있습니다.

  • (참고) Multi Entry & output
    module.exports = { 
      mode : 'development', // 웹팩4부터는 mode(development||production)는 필수 
      entry: { 
        bbinya : './src/js/index.js', 
        'module.chunk' : ['./src/js/module1.js', './src/js/module2.js'] // 배열 사용
    																												//(오른쪽부터 왼쪽으로 읽어감) 
      }, 
      output: { 
        path : __dirname, 
        filename : 'dist/[name].js' // 위에 지정한 entry 키의 이름에 맵핑되어 파일이 생성됨 
      } 
    };
    webpack 을 실행하면 dist 폴더 내에 bbinya.js, module.chunk.js 이 생성되어 있는 것을 확인하실 수 있습니다.

Output 파일 이름 옵션

앞에서 살펴본 filename 속성에 여러 가지 옵션을 넣을 수 있습니다.

  1. 결과 파일 이름에 entry 속성을 포함하는 옵션
module.exports = {
  output: {
		//정적인 절대값으로 이름을 구성
    filename: '[name].bundle.js'
  }
};
  1. 결과 파일 이름에 웹팩 내부적으로 사용하는 모듈 ID를 포함하는 옵션
module.exports = {
  output: {
    filename: '[id].bundle.js'
  }
};
  1. 매 빌드시 마다 고유 해시 값을 붙이는 옵션
module.exports = {
  output: {
		//webpack 을 build 할 때마다 hash 값이 변하면서 output 파일이 계속 변경 생성
    filename: '[name].[hash].bundle.js'
  }
};
  1. 웹팩의 각 모듈 내용을 기준으로 생생된 해시 값을 붙이는 옵션
module.exports = {
  output: {
		// chunk(덩어리) 에 따라서 hash 값이 변하면서 output 파일이 계속 변경 생성
    filename: '[chunkhash].bundle.js'
  }
};

이렇게 생성된 결과 파일의 이름에는 각각 엔트리 이름, 모듈 아이디, 해시 값 등이 포함됩니다.

빌드를 할 때 마다 웹팩에서 고유 값들을 붙여주게 되는데요.
해쉬를 사용하였을때 주의할 점은 항상 bundle.js 로 변환이 되면 내부의 파일 내용과 관계없이 브라우저 캐싱때문에 같은 파일을 화면에 뿌려주기 때문에 강제 새로고침을 해야 됩니다. 즉 캐싱을 비워줘야하는데요.
내용이 변환 되었을 때 에는, chunkhash 고유값으로 구분자를 주어서 이 파일이 변화가 되었다는것을 인식을 주면서 사용자가 강제 새로고침을 하지 않아도 결과물을 잘 확인 할 수 있게끔 합니다.

(3) Loader


💡 로더(Loader)는 웹팩이 웹 애플리케이션을 해석할 때 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, Images, 폰트 등)들을 변환할 수 있도록 도와주는 속성입니다.

Webpack 은 모든 파일을 모듈로 관리 할 수 있다고 언급했었는데요. 이 Webpack은 자바스크립트 파일만 읽어 올 수 있습니다.
때문에 CSS나 이미지 등을 Webpack이 이해 할 수 있는 모듈로 변경해 주는 역할을 바로 Loader 가 해주게 됩니다.

// webpack.config.js
module.exports = {
  module: {
    rules: []
  }
}

엔트리나 아웃풋 속성과는 다르게 module라는 이름을 사용합니다.

Loader가 필요한 이유

var path = require('path');

module.exports = {
  mode: 'none', 
  entry: './index.js', 
  output: {
    filename: 'bundle.js', 
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [ // 하나의 로더 규칙
      {
        test: /\.css$/, // 정규식 이용 => css 확장자를 가진 모든 파일
        use: ['style-loader', 'css-loader'] // 웹팩으로 변환 할 때에 로더들을 추가 할 수 있다.
      }
    ]
  },
}

해당 예시 코드에서 module 부분을 주석처리 한 뒤에 npm run build 를 실행해보면 아래와 같은 에러가 발생합니다.

Webpack 은 자바스크립트 파일만을 읽습니다. 자바스크립트 파일 안에 css 코드가 들어갈 수 없는데요. 해당 p태그 부분 의 파일에 대한 로더가 필요하다 라며 에러가 나옵니다.

그러면 이번엔, CSS-loader 만을 추가해보면 어떤 결과가 나올까요?

var path = require('path');

module.exports = {
  mode: 'none',
  entry: './index.js', 
  output: {
    filename: 'bundle.js', 
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [ 
      {
        test: /\.css$/,
        use: ['css-loader']
      }
    ]
  },
}


먼저 결과물을 보면 css-loader 로 css파일이 웹팩에서 인지 할 수 있는 단계가 왔기 때문에 빌드 에러가 나지는 않지만, 본래 p태그에 blue 라는 색상을 준 부분은 나타나지 않습니다.

또한, 만약에 아래와 같은 코드를 build를 해주면 어떻게 될까요?

module: {
    rules: [ // 하나의 로더 규칙
      {
        test: /\.css$/, // 정규식 이용 => css 확장자를 가진 모든 파일
        use: ['css-loader', 'style-loader']
      }
    ]
  },


이렇게 만약 loader 부분의 순서를 바꾸어주면 빌드 에러가 발생하게 되는 부분을 확인 하실 수 있습니다.

다시 둘의 순서를 바꾸어주면 정상적으로 빌드가 잡히고 결과물이 잘 나오는 것 을 확인 하실 수 있습니다.

즉, css-loader는 css가 웹팩 안으로 들어갈 수 있게 해준것이며, style-loader 는 그 들어간 스타일 코드를 header 안에 inline 스타일로 넣어주는 것 이라고 생각하시면 됩니다.

💡 항상 로더는 오른쪽 에서 왼쪽 순서로 적용이 됩니다.

예를들어, sass-loader 가 필요한 상황이라면

'style-loader', 'css-loader', 'sass-loader' 순으로 넣어주는 것 입니다.

sass를 css 파일로 바꾸어주고, css파일을 웹팩으로 넣어주고, 마지막에 style-loader로 들어오게 해주는 순서 입니다.

또한 위의 module 쪽 코드를 보면 rules 배열에 객체 한 쌍을 추가했습니다. 그리고 그 객체에는 2개의 속성이 들어가 있는데 각각 아래와 같은 역할을 합니다.

  • test : 로더를 적용할 파일 유형 / 변환 할 파일 지정 (일반적으로 정규 표현식 사용)
  • use : 해당 파일에 적용할 로더의 이름

자주 사용되는 Loader 종류

앞에서 살펴본 CSS 로더 이외에도 실제 서비스를 만들 때 자주 사용되는 로더의 종류는 다음과 같습니다.

로더를 여러 개 사용하는 경우에는 아래와 같이 rules 배열에 로더 옵션을 추가해주면 됩니다.

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

(4) Plugin


💡 플러그인(plugin)은 웹팩의 기본적인 동작에 **추가적인 기능을 제공**하는 속성입니다. 로더랑 비교하면 로더는 파일을 해석하고 변환하는 과정에 관여하는 반면, 플러그인은 **해당 결과물의 형태를 바꾸는 역할**을 한다고 보면 됩니다.

Plugin 은 번들된 파일을 난독화, 압축할 수도 있고 핫리로딩, 파일복사, 파일추출, 별칭사용 등의 부가적인 작업을 할 수 있습니다.

즉, Plugin은 파일별 커스텀 기능을 사용하기 위해서 사용하는 것인데요.
Webpack 을 어떻게 사용하느냐에 따라 Plugin 사용법은 여러가지가 될 수 있습니다.

플러그인은 아래와 같이 선언합니다.

// webpack.config.js
module.exports = {
  plugins: []
}

Plugin 사용 방법은 인자와 옵션을 사용할 수 있고, plugins 속성에 new 인스턴스를 전달해야 합니다.

// webpack.config.js
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin(),
    new webpack.ProgressPlugin()
  ]
}

위의 두 플러그인은 각각 아래와 같은 역할을 합니다.

  • HtmlWebpackPlugin : 웹팩으로 빌드한 결과물로 HTML 파일을 생성해주는 플러그인
  • ProgressPlugin : 웹팩의 빌드 진행율을 표시해주는 플러그인

자주 사용하는 플러그인

  • SplitChunksPlugin : 중복되는 Chunk 요소를 줄여서 Bundle Size 최적화하는 플러그인
  • clean-webpack-plugin : 성공적으로 다시 빌드 한 후 webpack의 output.path 디렉토리에있는 모든 파일과 사용하지 않는 모든 웹팩 자산을 제거하는 플러그인
  • webpack-bundle-analyzer-plugin : 번들링 되는 모듈들을 분석해서 보기 좋게 시각적으로 표현해주는 플러그인
  • BannerPlugin : 번들링된 파일의 상단에 배너(텍스트)를 달아주는 플러그인 - 결과물에 빌드 정보나 커밋 버전 등을 추가 할 수 있습니다.
  • DefinePlugin : 모든 javascript에서 사용 가능한 전역 변수를 선언할 수 있게 해주는 플러그인
  • EnvironmentPlugin : process.env의 환경 변수를 모든 javascript에서 사용 가능한 전역 변수로 등록해주는 플러그인
  • MiniCssExtractPlugin : javascript에 포함된 css를 별도의 css파일로 추출하는 플러그인

적용해보기

Loader에서 잠깐 보여드렸던 예시 코드로 CSS 파일을 추출하기 위한 플러그인을 한번 적용해보도록 하겠습니다.

// app.js
import './base.css';
/* base.css */
p {
  color: blue;
}
<!-- index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>CSS & Libraries Code Splitting</title>
  </head>
  <body>
    <header>
      <h3>CSS Code Splitting</h3>
    </header>
    <div>
      <!-- 웹팩 빌드 결과물이 잘 로딩되면 아래 p 태그의 텍스트 색깔이 파란색으로 표시됨 -->
      <p>
        This text should be colored with blue after injecting CSS bundle
      </p>
    </div>
    <!-- 웹팩의 빌드 결과물을 로딩하는 스크립트 -->
    <script src="./dist/bundle.js"></script>
  </body>
</html>
// webpack.config.js
var path = require('path');
var MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  mode: 'none', // production = 배포할 때의 mode , development = 개발 mode , none
  entry: './index.js', // 웹팩을 변환 할 파일의 주소. 경로.
  output: {
    filename: 'bundle.js', //[chunkhash] 같은 경우에는 고유값을 계속 넣어준다.
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [ // 하나의 로더 규칙
      {
        test: /\.css$/, // 정규식 이용 => css 확장자를 가진 모든 파일
        // use: ['style-loader', 'css-loader'] // 웹팩으로 변환 할 때에 로더들을 추가 할 수 있다.
        use: [
            { loader: MiniCssExtractPlugin.loader },
            "css-loader"
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin()
  ],
}

이 webpack 설정파일에 MiniCssExtractPlugin() 을 추가하고, npm run build 를 실행시키면, dist 폴더 아래에 main.css 파일이 생긴 것을 확인 하실 수 있습니다.

여기서 바로 결과물을 확인하면, 스타일이 적용되지 않은 상태이며, index.html 파일에 들어가서

<link *rel*="stylesheet" *href*="./dist/main.css"> 해당 코드를 head부분에 추가하시면, 스타일이 적용 된 모습을 확인하실 수 있습니다.

정리

여태까지 살펴본 웹팩 4가지 주요 속성을 도식으로 나타내보면 다음과 같습니다.

https://joshua1988.github.io/webpack-guide/assets/img/diagram.519da03f.png

위 도식을 보면서 지금까지 배운 내용을 종합해보겠습니다.

  1. Entry 속성은 웹팩을 실행할 대상 파일. 진입점
  2. Output 속성은 웹팩의 결과물에 대한 정보를 입력하는 속성. 일반적으로 filename과 path를 정의
  3. Loader 속성은 CSS, 이미지와 같은 비 자바스크립트 파일을 웹팩이 인식할 수 있게 추가하는 속성. 로더는 오른쪽에서 왼쪽 순으로 적용
  4. Plugin 속성은 웹팩으로 변환한 파일에 추가적인 기능을 더하고 싶을 때 사용하는 속성. 웹팩 변환 과정 전반에 대한 제어권을 갖고 있음
profile
Frontend 개발자 입니다, 피드백은 언제나 환영 입니다

0개의 댓글