ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Gulp + Webpack 3를 Webpack 4로 마이그레이션 해보자
    프론트엔드 최적화 2023. 10. 14. 16:16

    기존 구조를 변경하게 된 이유

     기존 빌드 시스템은 gulp를 통하여 자동화 task로 자바스크립트 파일을 제외한 정적 자산 파일(html, css, svg, png, jpg font 등)를  생성하며 특히, image sprite를 통하여 여러 장의 이미지를 한 장의 이미지를 생성하는 등의 작업을 한 후에 build하고, webpack을 통하여 자바스크립트 파일을 빌드하는 시스템이었다. 

     gulp는 유닛 테스트, 최적화 등 반복 가능한 작업들을 자동화하는 빌드 시스템이다. pipe를 통하여 동시에 여러 task를 수행하고, 스트림 기반이기 때문에 node.js의 강점을 잘 살려, 실제 빌드 및 작업 속도가 webpack보다 빠른 것으로 알려져 있다.

     webpack은 모든 모듈(html, css, js, 등 구성하는 모든 자원)들의 의존성 관계를 파악하여 최소한의 번들로 병합 및 압축하는 번들러이다. webpack을 사용하면 gulp를 통한 모든 정적 자산 파일들을 자동화 task를 script로 직접 작성하지 않아도 간단한 설정만으로도 해결 할 수 있고, webpack이 제공하는 기능들을 통해 더욱 빠르고 편리하게 작업할 수 있다. 예를 들어, gulp를 통하여 css 파일을 수정할 때, watch를 계속 켜놓고 변경사항이 생길 때 빌드 자동화 task 이후 결과물을 확인하는 번거로움이 있었으나, webpack은 webpack-dev-server를 통하여 변경 사항만 감지하여 즉시 자동 빌드하여 빠르게 결과물을 확인 할 수 있다. 또한 webpack-dev-server는 변경이 필요하면 부분의 DOM을 감지하여 변경하기 때문에 전체 새로고침이 되지 않는다. 큰 개발 효율성을 확보할 수 있다.

     또한, gulp를 통하여 수 천개의 모든 모듈들을 직접 자동화하고 관리하는 것보다 webpack의 설정을 통하여 관리하는 것이 더 용이하기 때문에 노드 버전, deprecated된 babel, loader, 라이브러리들을 업그레이드 함과 동시에 webpack을 업그레이드 하고 gulp task 들을 모두 webpack으로 마이그레이션하는 작업을 수행했다.

    정리하여, gulp를 걷어내고 webpack4로 마이그레이션을 한 이유는 다음과 같이 정리할 수 있겠다.

    1. 비대해진 모듈들과 의존성을 관리하기에 용이 => 추후 확장성에도 유리
    2. webpack에서 제공해주는 편리하고 강력한 기능 덕분에 개발 생산성, 편의성 향상
    3. 간편한 크로스 브라우징 대응 가능
    4. 압축 가능 
    ....

     아마 초기에는 gulp를 통하여 성능 이점을 확보하려는 전략이었으나 점점 소스가 비대해져가고 지난 5년 이상 기술적 시도를 하기 어려운 상황이어서, 기술적으로 향상 시키다는 것이 매우 즐거운 경험이었다.

     

     

    어떻게 gulp를 webpack으로 대체할 수 있었을까?

     

    Webpack의 간단한 사용 방법과 함께 사용된 loader, plugin 등을 소개하며 설정한 경험을 나누고자 한다.

    Entry

    webpack에서 웹 자원을 변환하기 위해 필요한 최초 진입점

     

    * entry에 설정해주는 js파일부터 시작해서 import로 불러 연관되는 다른 모듈들과의 의존 관계가 생기는 구조(Dependency Graph)를 생성한다.

     

    entry: {
    	app: './src/index.js',
    }

    Output

    webpack에서 번들링을 하고 난 뒤 결과물의 파일 경로

     

    *filename 속성으로 빌드한 파일의 이름을 나타내고, path를 통하여 해당 파일의 경로를 나타내어 세부 설정을 줄 수 있다.

    *publicPath: publicPath는 prefix 개념으로 config에서 설정한 주소가 실제 배포되는 주소 앞에 설정 된다.

    output: {
    		path: path.resolve(__dirname, './dist/'),
    		filename: '[name].js',
    		chunkFilename: '[name].[chunkhash].js',
    		publicPath: '/'
    	},

    Resolve

    모듈을 해석하는 방식을 변경

     

    *alias 옵션을 이용할 시에는 특정 모듈의 별칭을 만들 수 있다.

    Loader

    webpack이 웹 애플리케이션을 해석할 때, 자바스크립트 파일이 아닌 웹 자원(HTML, CSS, Images, 폰트 등) 변환하거나 처리하는 역할

     

    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'string-replace-loader',
                options: {
                    search: '[___IMAGE___]', replace: image
                }
            },
            {
                test: /\.(sc|c)ss$/i,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            publicPath: "../",
                        }
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: true
                        }
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            sourceMap: true,
                        }
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true,
                            prependData: `$CDN_URL: "https://jeveloper.tistory.com/";`
                        }
                    }
                ]
            },
            {
                test: /plugins\/.+\.(jsx|js)$/,
                loader: 'imports-loader?jQuery=jquery,$=jquery,this=>window'
            },
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /(node_modules)/,
            }
        ]
    },

     

    - loader 설정 순서는 오르쪽에서 왼쪽 (또는 아래에서 위로) 평가 / 실행

    • css-loader
      • Javascript 모듈에서 직접 CSS 파일을 import 할 수 있게 함
    • postcss-loader
      • ‘Autoprefixer’ 플러그인을 통해서 벤더 접두사(’-webkit-’, ‘-moz-’, ‘-ms-’, ‘-o-’)를 자동으로 추가해주는 역할 (크로스 브라우징 대응)
    • sass-loader
      • scss 파일 형식을 css로 변환해주는 역할
      • option을 통하여 scss 파일에서 전역 변수 사용 가능

    Plugin

    const plugins = [
    	new CleanWebpackPlugin(),
    	new webpack.DefinePlugin({ 'process.env': {
    			MODE : JSON.stringify(mode),
    			DEBUG : JSON.stringify(debug)
    		} }),
    	new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /ko|en/),
    	new CopyPlugin({
    		patterns: [
    			{ from: path.resolve(__dirname, config.googleSearchAuth.src), to: path.resolve(__dirname, config.googleSearchAuth.dest) },
    			{ from: path.resolve(__dirname, config.naverAuth.src), to: path.resolve(__dirname, config.naverAuth.dest) },
    			{ from: path.resolve(__dirname, config.sitemap.src), to: path.resolve(__dirname, config.sitemap.dest) },
    			{ from: path.resolve(__dirname, isProduction ? config.robots.src : config.robotsDev.src), to: path.resolve(__dirname, config.robots.dest) },
    			{ from: path.resolve(__dirname, config.verification.src), to: path.resolve(__dirname, config.verification.dest) },
    			{ from: path.resolve(__dirname, config.download.src), to: path.resolve(__dirname, config.download.dest) },
    			{ from: path.resolve(__dirname, config.font.src), to: path.resolve(__dirname, config.font.dest) },
    		]
    	}),
    	new MiniCssExtractPlugin({
    		filename: "css/[name].css",
    		chunkFilename: "css/[id].css",
    	}),
    	new HtmlWebpackPlugin({
    		template: "./src/index.html",
    		filename: "./index.html",
    		minify: true
    	}),
    	new HtmlWebpackPlugin({
    		template: "./assets/html/instagram.html",
    		filename: "./html/instagram.html",
    		excludeChunks: ["app", "app-event"],
    		inject: false,
    		minify: true
    	}),
    	new HtmlWebpackPlugin({
    		template: "./assets/html/please.html",
    		filename: "./html/please.html",
    		excludeChunks: ["app", "app-event"],
    	}),
    	new HtmlWebpackPlugin({
    		template: "./assets/html/notice/index.html",
    		filename: "./html/notice/index.html",
    		excludeChunks: ["app", "app-event"],
    	}),
    	new HtmlReplaceWebpackPlugin([
    		{pattern: '[___TITLE___]', replacement: replacements.title},
    		{pattern: '[___DESCRIPTION___]', replacement: replacements.description},
    		{pattern: '[___LOGO___]', replacement: replacements.logo},
    		{pattern: '[___CSS___]', replacement: replacements.css},
    		{pattern: '[___UNDER_IE_10___]', replacement: replacements.underIE10},
    	]),
    	new MomentTimezoneDataPlugin({
    		matchZones: 'Asia/Seoul'
    	}),
    	new BundleAnalyzerPlugin({
    		analyzerMode: "static", // 분석결과를 파일로 저장
    		reportFilename: "assets/stats.html", // 분설결과 파일을 저장할 경로와 파일명 지정
    		defaultSizes: "parsed",
    		openAnalyzer: true, // 웹팩 빌드 후 보고서파일을 자동으로 열지 여부
    		generateStatsFile: true, // 웹팩 stats.html 파일 자동생성
    		statsFilename: "assets/stats_dev.json", // stats.json 파일명 rename
    	}),
    ];

     

    • 웹팩의 빌드 과정에 개입하여 번들링된 결과물에 추가적인 처리를 수행한다. 로더가 파일 단위로 처리하는 반면, 플러그인은 번들된 결과물 전체에 대한 작업을 수행한다.
      • ex.) 최적화, 환경 변수 중집, 번들된 파일 생성 작업 등
    • copy-webpack-plugin
      • 정적 파일(asset 폴더)들을 빌드된 결과물로 단순 복사 (중간 변환 x)
    • mini-css-extract-plugin
      • 빌드 시 css 코드가 포함된 js 파일별로 css파일을 별도의 파일로 추출
    • html-replace-webpack-plugin
      • html 변수 치환 plugin

     

     

    출처: https://joshua1988.github.io/webpack-guide/concepts/wrapup.html#concepts-review

     

    이후, 최적화가 목적은 아니었지만 최적화까지 시도했습니다.

    시도 과정 및 결과는 다음 글에 이어서 하겠습니다.

     

     

     

     

    [출처]

    https://joshua1988.github.io/webpack-guide/

    '프론트엔드 최적화' 카테고리의 다른 글

    Webpack4 패키지 최적화  (0) 2023.10.16
Designed by Tistory.