DevTips – 자산을 효율적으로 컴파일하는 방법

게시 됨: 2022-08-25

올해의 목표 중 하나는 두 가지 주력 플러그인(Nelio Content 및 Nelio A/B Testing)을 TypeScript 및 React Hooks로 리팩토링하는 것이었습니다. 이제 반년이 지났고 이미 이 목표가 완전히 성공했다고 말할 수 있습니다. 그러나 저는 인정해야 합니다. 길은 예상보다 조금 더 복잡했습니다. 특히 TypeScript를 도입한 후 플러그인 빌드 시간이 몇 초에서 2분 이상으로 단축되었습니다! 뭔가 잘못 되었고 우리는 무엇을 몰랐습니다.

글쎄, 오늘의 게시물에서 나는 그 경험과 그것을 고치기 위해 우리가 한 일에 대해 조금 이야기하고 싶습니다. 결국 TypeScript가 항상 빌드 프로세스를 약간 느리게 한다는 것을 우리 모두 알고 있지만(유형 검사에는 대가가 따릅니다), 그렇게 많이 해서는 안됩니다! 글쎄요, 스포일러 경고: 문제는 TypeScript가 아니었습니다... 제 설정이었습니다. TypeScript는 그것을 "명백하게"만 만들었습니다. 그럼 시작해 볼까요?

장난감 프로젝트

몇 주 전에 발생한 문제와 해결 방법을 이해하는 데 도움을 주기 위해 우리가 할 수 있는 최선은 따라할 수 있는 매우 간단한 예를 만드는 것입니다. TypeScript를 사용하는 간단한 WordPress 플러그인을 만들고 잘못 구성 하면 컴파일 시간이 매우 느려질 수 있습니다. 시작하는 데 도움이 필요하면 WordPress 개발 환경에 대한 이 게시물을 확인하세요.

플러그인 생성

가장 먼저 해야 할 일은 WordPress의 /wp-content/plugins 디렉토리에 플러그인 이름(예: nelio )으로 새 폴더를 만드는 것입니다. 그런 다음 다음 내용이 포함된 기본 파일( nelio.php )을 추가합니다.

 <?php /** * Plugin Name: Nelio * Description: This is a test plugin. * Version: 0.0.1 * * Author: Nelio Software * Author URI: https://neliosoftware.com * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * * Text Domain: nelio */ if ( ! defined( 'ABSPATH' ) ) { exit; }

제대로 했다면 이제 WordPress에서 플러그인을 활성화할 수 있습니다.

플러그인 목록에 있는 장난감 플러그인의 스크린샷
플러그인 목록에 있는 장난감 플러그인의 스크린샷.

물론 플러그인은 아직 아무 작업도 수행하지 않지만... 최소한 표시됩니다.

타입스크립트

TypeScript 코드를 추가해 봅시다! 가장 먼저 할 일은 플러그인 폴더에서 npm을 초기화하는 것입니다. 이것을 실행하십시오:

 npm init

그리고 화면의 지시를 따릅니다. 그런 다음 종속성을 설치합니다.

 npm add -D @wordpress/scripts @wordpress/i18n

package.json 파일을 편집하여 @wordpress/scripts 에 필요한 빌드 스크립트를 추가합니다.

 { ... "scripts": { "build": "wp-scripts build", "start": "wp-scripts start", }, ... }

npm이 준비되면 tsconfig.json 파일을 추가하여 TypeScript를 사용자 정의합니다.

 { "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "node", "outDir": "build", "lib": [ "es7", "dom" ] }, "exclude": [ "node_modules" ] }

마지막으로 TS 코드를 작성해 보겠습니다. 우리는 이것을 Nelio A/B Testing 및 Nelio Content에서 했던 것과 매우 간단하지만 "충분히 가깝기"를 원하므로 index.tsutils/index.ts 내부에 두 개의 TypeScript 파일이 있는 src 폴더를 프로젝트에 만듭니다. utils/index.ts .

한편으로 utils/index.ts 가 유틸리티 패키지라고 가정해 봅시다. 즉, 프로젝트의 다른 파일에 필요할 수 있는 몇 가지 기능이 포함되어 있습니다. 예를 들어 고전적인 minmax 기능을 제공한다고 가정해 보겠습니다.

 export const min = ( a: number, b: number ): number => a < b ? a : b; export const max = ( a: number, b: number ): number => a > b ? a : b;

반면에 앱의 기본 파일인 index.ts 를 살펴보겠습니다. 테스트 목적으로 우리가 원하는 것은 유틸리티 패키지와 WordPress 종속성을 사용하는 간단한 스크립트입니다. 이 같은:

 import { __, sprintf } from '@wordpress/i18n'; import { min } from './utils'; const a = 2; const b = 3; const m = min( a, b ); console.log( sprintf( /* translators: 1 -> num, 2 -> num, 3 -> num */ __( 'Min between %1$s and %2$s is %3$s', 'nelio' ), a, b, m ) );

@wordpress/scripts 기본 설정

지금 당장 npm run build 를 사용하여 프로젝트를 빌드한다면 모든 것이 기본적으로 작동할 것입니다. @wordpress/scripts (즉, 우리가 프로젝트를 빌드하는 데 사용하는 기본 도구)가 우리 예제와 같은 코드베이스와 함께 작동하도록 설계되었기 때문입니다. 즉, src 폴더에 index.ts 파일이 있으면 index.asset.php 종속성 파일과 함께 build 폴더에 index.js 파일이 생성됩니다.

 > ls build index.asset.php index.js

왜 파일이 ​​2개인가요? 하나는 컴파일된 JavaScript 파일(duh)이고 다른 하나는 스크립트에 대한 유용한 정보가 포함된 종속성 파일입니다. 특히 WordPress에 포함된 라이브러리 중에서 어떤 JavaScript 라이브러리에 의존하는지 알려줍니다. 예를 들어, index.ts 는 문자열을 국제화하기 위해 @wordpress/i18n 패키지에 의존하며, 이는 WordPress에 포함된 라이브러리이므로… 네, wp-i18nindex.asset.php 에 표시됩니다.

 build/index.asset.php <?php return array( 'dependencies' => array( 'wp-i18n' ), 'version' => 'c6131c7f24df4fa803b7', );

불행히도 기본 구성은 완벽하지 않습니다. 여기 이유가 있습니다.

코드에 버그가 있는 경우(예: number 대신 arg string 을 사용하여 min 함수를 호출합시다):

 const m = min( `${ a }`, b );

이것은 오류를 유발해야 합니다. 하지만 그렇지 않습니다. 문제 없이 컴파일됩니다.

TypeScript로 컴파일하는 동안 유형 검사

앞서 언급한 "제한"을 해결하려면 자체 webpack 구성 파일을 만들고 TS 코드를 만날 때마다 tsc (TypeScript 컴파일러)를 사용하도록 지시하기만 하면 됩니다. 즉, 다음 webpack.config.json 파일이 필요합니다.

 const defaultConfig = require( '@wordpress/scripts/config/webpack.config' ); const config = { ...defaultConfig, module: { ...defaultConfig.module, rules: [ ...defaultConfig.module.rules, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, }; module.exports = { ...config, entry: './src/index', output: { path: __dirname + '/build', filename: 'index.js', }, };

보시다시피 @wordpress/scripts 패키지에 포함된 기본 웹팩 구성을 로드하는 것으로 시작한 다음 모든 .ts 파일에 ts-loader 를 추가하여 defaultConfig 를 확장합니다. 쉬워요!

그리고 지금, 보라:

 > npm run build ... ERROR in ...src/index.ts TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. webpack 5.74.0 compiled with 1 error in 4470 ms

프로젝트를 컴파일하면 오류가 발생합니다. 만세! 물론, 조금 느리지만 적어도 프로덕션에 무언가를 업로드하기 전에 몇 가지 보안 검사를 받습니다.

스크립트 대기열에 넣기

이제 코드에 문제가 있다는 것을 알았으므로 수정하고 플러그인을 다시 컴파일하십시오. 모든 것이 효과가 있었나요? 시원한! 이제 스크립트와 PHP에 대한 종속성을 대기열에 넣어 브라우저에서 시도할 수 있기 때문입니다.

nelio.php 를 열고 다음 스니펫을 추가합니다.

 add_action( 'admin_enqueue_scripts', function() { $path = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); $asset = require( $path . '/build/index.asset.php' ); wp_enqueue_script( 'nelio', $url . '/build/index.js', $asset['dependencies'], $asset['version'] ); } );

다음으로 WordPress 대시보드로 이동하여(모든 페이지에서 트릭을 수행할 수 있음) 브라우저의 JavaScript 콘솔을 살펴봅니다. 다음 텍스트가 표시되어야 합니다.

 Min between 2 and 3 is 2

멋진!

내 종속성은 어떻습니까?

JavaScript/webpack/WordPress의 종속성 관리에 대해 잠시 이야기해 보겠습니다. @wordpress/scripts 는 기본적으로 프로젝트가 WordPress 코어에 패키지된 종속성을 사용하는 경우 .asset.php 파일에 나열되는 방식으로 구성됩니다. 예를 들어 이것은 @wordpress/i18n 이 스크립트의 종속성 파일에 나열된 이유를 설명합니다.

그러나 "기타" 패키지에 대한 종속성은 어떻습니까? utils 패키지는 어떻게 되었나요? 간단히 말해서: 기본적으로 webpack은 모든 종속성을 컴파일하고 출력 스크립트에 병합합니다. 생성된 JS 파일을 살펴보세요( npm run start 로 컴파일하여 축소를 비활성화)

 ... var __webpack_modules__ = ({ "./src/utils/index.ts": ((...) => ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; }), ...

보다? 우리의 utils 코드는 바로 거기에 있으며 출력 스크립트에 포함되어 있습니다.

@wordpress/i18n 은 어떻습니까? 글쎄, 그것은 전역 변수에 대한 단순한 참조일 뿐입니다.

 ... var __webpack_modules__ = ({ "./src/utils/index.ts": ..., "@wordpress/i18n": ((module)) => { module.exports = window["wp"]["i18n"]; }) ...

내가 말했듯이 @wordpress/scripts 에는 컴파일 프로세스에서 특정 종속성을 "제외"하고 전역 범위에서 사용할 수 있다고 가정하는 코드를 생성하는 Dependency Extraction Webpack Plugin 플러그인이 함께 제공됩니다. 예를 들어 이 예에서 @wordpress/i18nwp.i18n 에 있음을 알 수 있습니다. 그렇기 때문에 스크립트를 대기열에 넣을 때 종속성도 대기열에 넣어야 합니다.

두 개의 개별 스크립트를 생성하는 사용자 지정 구성

이 모든 것을 염두에 두고 utils 패키지로 동일한 것을 달성하고 싶다고 가정해 봅시다. 즉, 해당 콘텐츠가 index.js 에 포함되는 것을 원하지 않지만 자체 .js 파일로 컴파일되어 index.asset.php 에 대한 종속성으로 나타나야 합니다. 어떻게 합니까?

먼저 실제 패키지 처럼 보이도록 index.jsimport 문의 이름을 바꿔야 합니다. 즉, 상대 경로( ./utils )를 사용하여 스크립트를 가져오는 대신 @nelio/utils 와 같은 이름을 사용할 수 있다면 좋을 것입니다. 이렇게 하려면 프로젝트의 package.json 파일을 편집하여 종속성에 새 dependencies 을 추가하기만 하면 됩니다.

 { ... "dependencies": { "@nelio/utils": "./src/utils" }, "devDependencies": { "@wordpress/i18n": ..., "@wordpress/scripts": ... }, ... }

npm install 을 실행하여 이 "새" 패키지를 가리키는 node_modules 에 심볼릭 링크를 만들고 마지막으로 src/utils 에서 npm init 를 실행하여 npm의 관점에서 @nelio/utils 가 유효한 패키지가 되도록 합니다.

그런 다음 @nelio/utils 를 자체 스크립트로 컴파일하려면 webpack.config.js 구성을 편집하고 두 가지 내보내기를 정의해야 합니다.

  • 우리가 이미 가지고 있는 것( ./src/index.ts )
  • ./src/utils 를 다른 파일로 컴파일하기 위한 또 다른 내보내기, 예를 들어 nelio.utils 라는 전역 변수에 내보내기를 노출합니다.

즉, 우리는 이것을 원합니다:

 module.exports = [ { ...config, entry: './src/index', output: { path: __dirname + '/build', filename: 'index.js', }, }, { ...config, entry: './src/utils', output: { path: __dirname + '/build', filename: 'utils.js', library: [ 'nelio', 'utils' ], libraryTarget: 'window', }, }, ];

코드를 다시 컴파일하고 ./build 폴더를 살펴보십시오. 이제 모두 두 개의 스크립트가 있음을 알 수 있습니다. ./build/utils.js 를 살펴보면 예상대로 nelio.utils 를 정의하는 방법을 알 수 있습니다.

 ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; (window.nelio = window.nelio || {}).utils = __webpack_exports__; ... ... var min = function (a, b) { return a < b ? a : b; }; var max = function (a, b) { return a > b ? a : b; }; (window.nelio = window.nelio || {}).utils = __webpack_exports__; ...

불행히도, 우리는 단지 절반을 마쳤습니다. 또한 ./build/index.js 를 살펴보면 src/utils 가 여전히 포함되어 있음을 알 수 있습니다. "외부 종속성"이 되어야 하고 방금 정의한 전역 변수를 사용해야 하지 않습니까?

외부 종속성을 생성하기 위한 사용자 지정 구성

@nelio/utils 를 실제 외부 종속성으로 변환하려면 웹팩을 추가로 사용자 정의하고 앞에서 언급한 종속성 추출 플러그인을 활용해야 합니다. webpack.config.js 파일을 다시 열고 config 변수를 다음과 같이 수정하기만 하면 됩니다.

 const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' ); const config = { ...defaultConfig, module: { ...defaultConfig.module, rules: [ ...defaultConfig.module.rules, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, plugins: [ ...defaultConfig.plugins.filter( ( p ) => p.constructor.name !== 'DependencyExtractionWebpackPlugin' ), new DependencyExtractionWebpackPlugin( { requestToExternal: ( request ) => '@nelio/utils' === request ? [ 'nelio', 'utils' ] : undefined, requestToHandle: ( request ) => '@nelio/utils' === request ? 'nelio-utils' : undefined, outputFormat: 'php', } ), ], };

@nelio/utils 에 대한 모든 참조는 코드에서 nelio.utils 로 변환되고 스크립트 처리기 nelio-utils 에 대한 종속성이 있습니다. 두 스크립트의 종속성을 살펴보면 다음과 같습니다.

 build/index.asset.php <?php return array( 'dependencies' => array('nelio-utils', 'wp-i18n')... ?> build/utils.asset.php <?php return array( 'dependencies' => array()... ?>

./build/index.js 를 보면 실제로 @nelio/utils 종속성이 이제 외부임을 확인합니다.

 ... var __webpack_modules__ = ({ "@nelio/utils": ((module)) => { module.exports = window["nelio"]["utils"]; }), "@wordpress/i18n": ((module)) => { module.exports = window["wp"]["i18n"]; }) ...

하지만 마지막으로 해결해야 할 문제가 하나 있습니다. 브라우저로 이동하여 대시보드 페이지를 새로고침하고 콘솔을 확인합니다. 아무것도 나타나지 않죠? 왜요? 글쎄요, nelio 는 이제 nelio-utils 에 의존하지만 이 스크립트는 WordPress에 등록되지 않았습니다… 이 문제를 해결하려면 nelio.php 를 편집하고 새 스크립트를 등록하십시오.

 add_action( 'admin_enqueue_scripts', function() { $path = untrailingslashit( plugin_dir_path( __FILE__ ) ); $url = untrailingslashit( plugin_dir_url( __FILE__ ) ); $asset = require( $path . '/build/utils.asset.php' ); wp_register_script( 'nelio-utils', $url . '/build/utils.js', $asset['dependencies'], $asset['version'] ); } );

빌드 프로세스 속도를 높이는 방법

빌드 프로세스를 여러 번 실행하고 완료하는 데 걸리는 평균 시간을 계산하면 빌드가 약 10초 안에 실행되는 것을 볼 수 있습니다.

 > yarn run build ... ./src/index.ts + 2 modules ... webpack 5.74.0 compiled successfully in 5703 ms ... ./src/utils/index.ts ... webpack 5.74.0 compiled successfully in 5726 m Done in 10.22s.

별 것 아닌 것 같지만 이것은 간단한 장난감 프로젝트이며, 제가 말했듯이 Nelio Content 또는 Nelio A/B Testing과 같은 실제 프로젝트는 컴파일하는 데 몇 분이 걸립니다.

왜 "너무 느리고" 속도를 높이려면 어떻게 해야 합니까? 내가 말할 수 있는 한, 문제는 웹팩 구성에 있었습니다. wepack의 module.exports 에 내보내기가 많을수록 컴파일 시간이 느려집니다. 그러나 단일 내보내기가 훨씬 더 빠릅니다.

단일 내보내기를 사용하도록 프로젝트를 약간 리팩토링해 보겠습니다. 먼저 src/utils 에 다음 내용으로 export.ts 파일을 만듭니다.

 export * as utils from './index';

그런 다음 webpack.config.js 를 편집하여 두 개의 항목이 있는 단일 내보내기를 갖도록 합니다.

 module.exports = { ...config, entry: { index: './src/index', utils: './src/utils/export', }, output: { path: __dirname + '/dist', filename: 'js/[name].js', library: { name: 'nelio', type: 'assign-properties', }, }, };

마지막으로 프로젝트를 다시 빌드합니다.

 > yarn run build ... built modules 522 bytes [built] ./src/index.ts + 2 modules ... ./src/utils/export.ts + 1 modules ... webpack 5.74.0 compiled successfully in 4339 ms Done in 6.02s.

이전 시간의 거의 절반인 6초만 소요되었습니다! 꽤 깔끔하죠?

요약

TypeScript는 컴파일 시간에 유형이 정확 하고 불일치가 없는지 확인할 수 있기 때문에 코드의 품질 을 개선하는 데 도움이 됩니다. 그러나 삶의 모든 것과 마찬가지로 TypeScript를 사용하는 이점에는 대가가 따릅니다. 코드 컴파일 속도가 약간 느려집니다.

오늘의 게시물에서 우리는 웹팩의 구성에 따라 프로젝트 컴파일이 훨씬 더 빠를 수 있음을 보았습니다(또는 더 느릴 수 있음). 스위트 스폿에는 단일 내보내기가 필요합니다. 이것이 바로 오늘 우리가 이야기한 것입니다.

게시물이 마음에 드셨으면 합니다. 그렇다면 공유하십시오. 웹팩을 최적화하는 다른 방법을 알고 계시다면 아래 댓글로 알려주세요. 좋은 하루 보내세요!

Unsplash에서 Saffu의 추천 이미지.