Gulp: 사이트 속도 극대화를 위한 웹 개발자의 비밀 무기

게시 됨: 2022-03-11

우리 중 많은 사람들은 대중에게 다양한 서비스를 제공하는 프로덕션에서 사용되는 웹 기반 프로젝트를 처리해야 합니다. 이러한 프로젝트를 처리할 때 코드를 빠르게 빌드하고 배포할 수 있는 것이 중요합니다. 무언가를 빠르게 수행하면 특히 프로세스가 반복적인 경우 오류가 발생하므로 가능한 한 이러한 프로세스를 자동화하는 것이 좋습니다.

Gulp: 사이트 속도 극대화를 위한 웹 개발자의 비밀 무기

동료 개발자: 브라우저에 정크를 제공하는 데에는 변명의 여지가 없습니다.
트위터

이 게시물에서 우리는 그러한 자동화를 달성할 수 있도록 하는 일부가 될 수 있는 도구를 살펴볼 것입니다. 이 도구는 Gulp.js라는 npm 패키지입니다. 이 포스트에서 사용하는 Gulp.js의 기본 용어에 익숙해지려면 이전에 Toptal 동료 개발자 중 한 명인 Antonios Minas가 블로그에 게시한 "Gulp를 사용한 JavaScript 자동화 소개"를 참조하십시오. 이 게시물 전체에서 패키지를 설치하는 데 광범위하게 사용되는 npm 환경에 대한 기본적인 지식이 있다고 가정합니다.

프런트 엔드 자산 제공

계속하기 전에 Gulp.js가 우리를 위해 해결할 수 있는 문제에 대한 개요를 보기 위해 몇 단계 뒤로 물러나겠습니다. 많은 웹 기반 프로젝트에는 웹 페이지에 다양한 기능을 제공하기 위해 클라이언트에 제공되는 프런트 엔드 JavaScript 파일이 있습니다. 일반적으로 클라이언트에도 제공되는 CSS 스타일시트 세트도 있습니다. 때때로 웹사이트나 웹 애플리케이션의 소스 코드를 볼 때 다음과 같은 코드를 볼 수 있습니다.

 <link href="css/main.css" rel="stylesheet"> <link href="css/custom.css" rel="stylesheet"> <script src="js/jquery.min.js"></script> <script src="js/site.js"></script> <script src="js/module1.js"></script> <script src="js/module2.js"></script>

이 코드에는 몇 가지 문제가 있습니다. 두 개의 개별 CSS 스타일시트와 네 개의 개별 JavaScript 파일에 대한 참조가 있습니다. 즉, 서버는 서버에 총 6번의 요청을 해야 하며 각 요청은 페이지가 준비되기 전에 개별적으로 리소스를 로드해야 합니다. 이것은 HTTP/2가 병렬 처리와 헤더 압축을 도입하기 때문에 HTTP/2에서는 문제가 되지 않지만 여전히 문제입니다. 이 페이지를 로드하는 데 필요한 트래픽의 총량을 늘리고 파일을 로드하는 데 더 오래 걸리기 때문에 사용자 경험의 품질을 저하시킵니다. HTTP 1.1의 경우에도 네트워크를 혹사하고 사용 가능한 요청 채널 수를 줄입니다. CSS 및 JavaScript 파일을 각각에 대한 단일 번들로 결합하는 것이 훨씬 더 나았을 것입니다. 그렇게 하면 총 두 개의 요청만 있을 것입니다. 일반적으로 원본보다 훨씬 작은 이러한 파일의 축소 버전을 제공하는 것도 좋았을 것입니다. 자산이 캐시되면 웹 애플리케이션이 중단될 수 있으며 클라이언트는 오래된 버전을 받게 됩니다.

초과 적재

이러한 문제 중 일부를 해결하기 위한 한 가지 기본 접근 방식은 텍스트 편집기를 사용하여 각 유형의 자산을 번들로 수동으로 결합한 다음 http://jscompress.com/과 같은 축소 서비스를 통해 결과를 실행하는 것입니다. 이것은 개발 과정에서 지속적으로 수행하는 것이 매우 지루한 것으로 판명되었습니다. 미미하지만 의심스러운 개선 사항은 GitHub에서 사용 가능한 패키지 중 하나를 사용하여 자체 축소 서버를 호스팅하는 것입니다. 그러면 다음과 다소 유사해 보이는 작업을 수행할 수 있습니다.

 <script src="min/f=js/site.js,js/module1.js"></script>

이렇게 하면 축소된 파일이 클라이언트에 제공되지만 캐싱 문제는 해결되지 않습니다. 그것은 또한 우리 서버가 모든 요청에 ​​대해 반복적으로 모든 소스 파일을 연결하고 축소해야 하기 때문에 서버에 추가 부하를 일으킬 것입니다.

Gulp.js로 자동화하기

확실히 우리는 이 두 가지 접근 방식보다 더 잘 할 수 있습니다. 우리가 정말로 원하는 것은 번들링을 자동화하고 프로젝트의 빌드 단계에 포함시키는 것입니다. 우리는 이미 축소되어 제공할 준비가 된 사전 구축된 자산 번들로 끝내기를 원합니다. 우리는 또한 모든 요청에 ​​대해 클라이언트가 번들 자산의 최신 버전을 받도록 하고 싶지만 가능하다면 여전히 캐싱을 활용하기를 원합니다. 운 좋게도 Gulp.js가 이를 처리할 수 있습니다. 이 기사의 나머지 부분에서는 Gulp.js의 기능을 활용하여 파일을 연결하고 축소하는 솔루션을 구축할 것입니다. 또한 업데이트가 있을 때 플러그인을 사용하여 캐시를 파괴할 것입니다.

이 예에서 다음 디렉토리 및 파일 구조를 생성합니다.

 public/ |- build/ |- js/ |- bundle-{hash}.js |- css/ |- stylesheet-{hash}.css assets/ |- js/ |- vendor/ |- jquery.js |- site.js |- module1.js |- module2.js |- css/ |- main.css |- custom.css gulpfile.js package.json
npm은 Node.js 프로젝트의 패키지 관리를 더없이 행복하게 만듭니다. Gulp는 npm의 간단한 패키징 접근 방식을 활용하여 모듈식의 강력한 플러그인을 제공함으로써 엄청난 확장성을 제공합니다.

gulpfile.js 파일은 Gulp가 우리를 위해 수행할 작업을 정의하는 곳입니다. package.json 은 npm에서 애플리케이션 패키지를 정의하고 설치할 종속성을 추적하는 데 사용됩니다. 공개 디렉토리는 웹을 향하도록 구성되어야 하는 것입니다. 자산 디렉토리는 소스 파일을 저장할 위치입니다. 프로젝트에서 Gulp를 사용하려면 npm을 통해 설치하고 프로젝트에 대한 개발자 종속성으로 저장해야 합니다. 또한 Gulp용 concat 플러그인으로 시작하여 여러 파일을 하나로 연결할 수 있습니다.

이 두 항목을 설치하려면 다음 명령을 실행합니다.

 npm install --save-dev gulp gulp-concat

다음으로 gulpfile.js의 내용을 작성하기 시작합니다.

 var gulp = require('gulp'); var concat = require('gulp-concat'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(gulp.dest('public/build/js')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(gulp.dest('public/build/css')); }); gulp.task('default', ['pack-js', 'pack-css']);

여기에서 gulp 라이브러리와 concat 플러그인을 로드합니다. 그런 다음 세 가지 작업을 정의합니다.

gulp 라이브러리 및 해당 concat 플러그인 로드

첫 번째 작업( pack-js )은 여러 JavaScript 소스 파일을 하나의 번들로 압축하는 절차를 정의합니다. 지정된 순서대로 globbed하고 읽고 연결할 소스 파일을 나열합니다. 우리는 그것을 concat 플러그인으로 파이프하여 bundle.js 라는 하나의 최종 파일을 얻습니다. 마지막으로 gulp에게 파일을 public/build/js 에 쓰도록 지시합니다.

두 번째 작업( pack-css )은 위와 동일하지만 CSS 스타일시트에 대해 수행합니다. 연결된 출력을 public/build/cssstylesheet.css 로 저장하도록 Gulp에 지시합니다.

세 번째 작업( default )은 Gulp가 인수 없이 호출할 때 실행되는 작업입니다. 두 번째 매개변수에서는 기본 작업이 실행될 때 실행할 다른 작업 목록을 전달합니다.

우리가 일반적으로 사용하는 소스 코드 편집기를 사용하여 이 코드를 gulpfile.js에 붙여넣고 파일을 애플리케이션 루트에 저장합시다.

다음으로 명령줄을 열고 다음을 실행합니다.

 gulp

이 명령을 실행한 후 파일을 보면 public/build/js/bundle.jspublic/build/css/stylesheet.css 라는 두 개의 새 파일을 찾을 수 있습니다. 그것들은 원본 문제의 일부를 해결하는 소스 파일의 연결입니다. 그러나 축소되지 않았으며 아직 캐시 버스팅이 없습니다. 자동화된 축소를 추가해 보겠습니다.

빌드 자산 최적화

두 개의 새로운 플러그인이 필요합니다. 추가하려면 다음 명령을 실행합니다.

 npm install --save-dev gulp-clean-css gulp-minify

첫 번째 플러그인은 CSS 축소용이고 두 번째 플러그인은 JavaScript 축소용입니다. 첫 번째는 clean-css 패키지를 사용하고 두 번째는 UglifyJS2 패키지를 사용합니다. 먼저 gulpfile.js에 다음 두 패키지를 로드합니다.

 var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css');

그런 다음 출력을 디스크에 쓰기 직전에 작업에서 사용해야 합니다.

 .pipe(minify()) .pipe(cleanCss())

gulpfile.js는 이제 다음과 같아야 합니다.

 var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify()) .pipe(gulp.dest('public/build/js')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(gulp.dest('public/build/css')); }); gulp.task('default', ['pack-js', 'pack-css']);

꿀꺽꿀꺽 다시 실행해보자. stylesheet.css 파일이 축소된 형식으로 저장되어 있고 bundle.js 파일이 그대로 저장되어 있는 것을 볼 수 있습니다. 이제 축소된 bundle-min.js도 있음을 알 수 있습니다. 우리는 축소된 파일만 원하고 그것이 bundle.js 로 저장되기를 원하므로 추가 매개변수로 코드를 수정할 것입니다.

 .pipe(minify({ ext:{ min:'.js' }, noSource: true }))

gulp-minify 플러그인 문서(https://www.npmjs.com/package/gulp-minify)에 따라 축소 버전에 대해 원하는 이름을 설정하고 플러그인에 원래 소스가 포함된 버전을 생성하지 않도록 지시합니다. 빌드 디렉토리의 내용을 삭제하고 명령줄에서 다시 gulp를 실행하면 축소된 파일이 두 개만 남게 됩니다. 우리는 빌드 프로세스의 축소 단계 구현을 막 마쳤습니다.

캐시 무효화

다음으로 캐시 무효화를 추가하고 이를 위한 플러그인을 설치해야 합니다.

 npm install --save-dev gulp-rev

꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀꺽 꿀팁 저장하세요

 var rev = require('gulp-rev');

플러그인을 사용하는 것은 약간 까다롭습니다. 먼저 플러그인을 통해 축소된 출력을 파이프해야 합니다. 그런 다음 결과를 디스크에 쓴 후 플러그인을 다시 호출해야 합니다. 플러그인은 고유한 해시로 태그가 지정되도록 파일 이름을 변경하고 매니페스트 파일도 생성합니다. 매니페스트 파일은 HTML 코드에서 참조해야 하는 최신 파일 이름을 결정하기 위해 애플리케이션에서 사용할 수 있는 맵입니다. gulp 파일을 수정한 후에는 다음과 같이 표시되어야 합니다.

 var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); var rev = require('gulp-rev'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify({ ext:{ min:'.js' }, noSource: true })) .pipe(rev()) .pipe(gulp.dest('public/build/js')) .pipe(rev.manifest()) .pipe(gulp.dest('public/build')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(rev()) .pipe(gulp.dest('public/build/css')) .pipe(rev.manifest()) .pipe(gulp.dest('public/build')); }); gulp.task('default', ['pack-js', 'pack-css']);
적절한 캐시 무효화를 사용하면 JS 및 CSS 파일의 만료 시간이 길어 질 수 있으며 필요할 때마다 최신 버전으로 안정적으로 교체할 수 있습니다.

빌드 디렉토리의 내용을 삭제하고 gulp를 다시 실행해 보겠습니다. 이제 각 파일 이름에 해시태그가 붙은 두 개의 파일과 public/build 에 manifest.json이 저장되었음을 알 수 있습니다. 매니페스트 파일을 열면 축소되고 태그가 지정된 파일 중 하나에 대한 참조만 있음을 알 수 있습니다. 일어나고 있는 일은 각 작업이 별도의 매니페스트 파일을 작성하고 그 중 하나가 다른 하나를 덮어쓰게 된다는 것입니다. 기존 매니페스트 파일을 찾고 새 데이터가 있으면 병합하도록 지시하는 추가 매개변수로 작업을 수정해야 합니다. 이에 대한 구문은 약간 복잡하므로 코드가 어떻게 생겼는지 살펴본 다음 살펴보겠습니다.

 var gulp = require('gulp'); var concat = require('gulp-concat'); var minify = require('gulp-minify'); var cleanCss = require('gulp-clean-css'); var rev = require('gulp-rev'); gulp.task('pack-js', function () { return gulp.src(['assets/js/vendor/*.js', 'assets/js/main.js', 'assets/js/module*.js']) .pipe(concat('bundle.js')) .pipe(minify({ ext:{ min:'.js' }, noSource: true })) .pipe(rev()) .pipe(gulp.dest('public/build/js')) .pipe(rev.manifest('public/build/rev-manifest.json', { merge: true })) .pipe(gulp.dest('')); }); gulp.task('pack-css', function () { return gulp.src(['assets/css/main.css', 'assets/css/custom.css']) .pipe(concat('stylesheet.css')) .pipe(cleanCss()) .pipe(rev()) .pipe(gulp.dest('public/build/css')) .pipe(rev.manifest('public/build/rev-manifest.json', { merge: true })) .pipe(gulp.dest('')); }); gulp.task('default', ['pack-js', 'pack-css']);

먼저 출력을 rev.manifest() 로 연결합니다. 이것은 우리가 이전에 가지고 있던 파일 대신에 태그가 있는 파일을 생성합니다. rev-manifest.json 의 원하는 경로를 제공하고 rev.manifest() 에게 기존 파일이 있는 경우 병합하도록 지시합니다. 그런 다음 gulp에게 현재 디렉토리에 매니페스트를 작성하도록 지시합니다. 이 디렉토리는 그 시점에서 public/build가 될 것입니다. 경로 문제는 GitHub에서 더 자세히 논의되는 버그로 인한 것입니다.

이제 자동화된 축소, 태그가 지정된 파일 및 매니페스트 파일이 있습니다. 이 모든 것을 통해 우리는 파일을 사용자에게 더 빨리 전달할 수 있고 수정할 때마다 캐시를 ​​파괴할 수 있습니다. 하지만 남은 문제는 딱 두 가지입니다.

첫 번째 문제는 소스 파일을 수정하면 새로 태그가 지정된 파일을 가져오지만 이전 파일도 그대로 유지된다는 것입니다. 오래된 축소 파일을 자동으로 삭제하는 방법이 필요합니다. 파일을 삭제할 수 있는 플러그인을 사용하여 이 문제를 해결해 보겠습니다.

 npm install --save-dev del

코드에서 이를 요구하고 각 소스 파일 유형에 대해 하나씩 두 개의 새 작업을 정의합니다.

 var del = require('del'); gulp.task('clean-js', function () { return del([ 'public/build/js/*.js' ]); }); gulp.task('clean-css', function () { return del([ 'public/build/css/*.css' ]); });

그런 다음 두 가지 주요 작업이 실행되기 전에 새 작업의 실행이 완료되는지 확인합니다.

 gulp.task('pack-js', ['clean-js'], function () { gulp.task('pack-css', ['clean-css'], function () {

이 수정 후에 다시 gulp 를 실행하면 가장 최근에 축소된 파일만 남게 됩니다.

두 번째 문제는 변경할 때마다 계속 꿀꺽 꿀꺽 꿀꺽 마시고 싶지 않다는 것입니다. 이 문제를 해결하려면 감시자 작업을 정의해야 합니다.

 gulp.task('watch', function() { gulp.watch('assets/js/**/*.js', ['pack-js']); gulp.watch('assets/css/**/*.css', ['pack-css']); });

또한 기본 작업의 정의를 변경합니다.

 gulp.task('default', ['watch']);

이제 명령줄에서 gulp를 실행하면 호출 시 더 이상 아무것도 빌드하지 않는다는 것을 알 수 있습니다. 이는 이제 소스 파일의 변경 사항을 감시하고 변경 사항을 감지한 경우에만 빌드하는 감시자 작업을 호출하기 때문입니다. 소스 파일을 변경하려고 시도한 다음 콘솔을 다시 보면 pack-jspack-css 작업이 종속성과 함께 자동으로 실행되는 것을 볼 수 있습니다.

이제 애플리케이션에서 manifest.json 파일을 로드하고 여기에서 태그가 지정된 파일 이름을 가져오기만 하면 됩니다. 이를 수행하는 방법은 특정 백엔드 언어 및 기술 스택에 따라 다르며 구현하기가 매우 간단하므로 자세히 다루지 않겠습니다. 그러나 일반적인 아이디어는 매니페스트를 배열이나 개체에 로드한 다음 다음과 유사한 방식으로 템플릿에서 버전이 지정된 자산을 호출할 수 있는 도우미 함수를 정의할 수 있다는 것입니다.

 gulp('bundle.js')

그렇게 하면 파일 이름에서 변경된 태그에 대해 다시는 걱정할 필요가 없으며 고품질 코드 작성에 집중할 수 있습니다.

몇 가지 샘플 자산과 함께 이 기사의 최종 소스 코드는 이 GitHub 리포지토리에서 찾을 수 있습니다.

결론

이 기사에서는 빌드 프로세스에 Gulp 기반 자동화를 구현하는 방법을 살펴보았습니다. 이것이 귀하에게 도움이 되고 귀하의 애플리케이션에서 보다 정교한 빌드 프로세스를 개발할 수 있기를 바랍니다.

Gulp는 이러한 목적으로 사용할 수 있는 도구 중 하나일 뿐이며 Grunt, Browserify 및 Webpack과 같은 다른 도구도 많다는 점을 명심하십시오. 그것들은 목적과 해결할 수 있는 문제의 범위가 다릅니다. 일부는 요청 시 로드할 수 있는 종속성이 있는 JavaScript 모듈 번들과 같이 Gulp가 할 수 없는 문제를 해결할 수 있습니다. 이것을 "코드 분할"이라고 하며 모든 페이지에서 프로그램의 모든 부분과 함께 하나의 큰 파일을 제공하는 아이디어를 개선한 것입니다. 이러한 도구는 매우 정교하지만 나중에 다룰 수도 있습니다. 다음 포스트에서는 애플리케이션 배포를 자동화하는 방법에 대해 설명합니다.

관련 항목: Gulp Under the Hood: 스트림 기반 작업 자동화 도구 빌드