웹팩(webpack)의 cache-loader

목이·2019년 12월 30일
8

웹팩

목록 보기
1/1
post-thumbnail

웹팩 Loader 의 역할

웹팩은 정적 모듈 번들러(static module bundler)입니다. 모듈 번들러는 entry 속성에 지정된 *.js 파일을 분석해 require/import 로 의존관계에 있는 리소스(css, image, webfont)와 자바스크립트 모듈을 묶어 웹 브라우저에서 실행 가능한 하나(혹은 여러 개)의 번들 파일을 생성합니다.

웹팩은 자바스크립트와 json 파일밖에 인식하지 못합니다. 번들링 과정에서 *.css, *.png, *.xml 등의 파일확장자를 만나면 이를 처리할 방법을 알지 못합니다. Loader 는 웹팩이 다루지 못하는 파일 형식들을 변환해 어플리케이션에서 사용가능하고 dependency graph 에 추가될 수 있는 모듈형식으로 만들어줍니다.

Vue.js 프로젝트는 *.vue 확장자를 가진 파일들로 구성됩니다. 모듈 번들링을 위해 웹팩이 사용되는데 *.vue 확장자에 대한 처리를 할 수 없기에 vue-loader 의 도움이 필요합니다.

cache-loader 역할

cache-loader는 다른 Loader 의 수행결과를 disk 에 저장합니다. 번들링 과정에서 동일한 파일형식에 대한 처리를 반복하지 않고, 저장된 결과를 이용하도록 하는게 cache-loader 의 역할입니다. 번들링 시간을 비약적으로 줄여주기에 무거운 작업을 수행하는 Loader 의 결과물을 캐쉬하는데 주로 사용됩니다.

babel-loader의 결과물을 캐쉬하려면 그 앞에 cache-loader 를 선언해주면 됩니다.

{
	test: /\.m?jsx?$/,
    use: [
    	/* cache-loader 선언 */
        {
			loader: '/.../node_modules/cache-loader/dist/cjs.js',
			options: {
            	cacheDirectory: '/.../node_modules/.cache/babel-loader',
            	cacheIdentifier: '...'
            }
		},
        /* babel-loader 선언 */
        {
			loader: '/.../node_modules/babel-loader/lib/index.js'
		}
	]
},

특정 파일에 대한 처리를 담당할 실행 Loader 가 여러개 선언되어 있으면, 배열에 담긴 순서의 역순으로 실행됩니다. 역순의 의미를 배열 끝에서 앞으로 이동하며 실행하는 순차적 구조 로 생각하면 잘 납득되지 않습니다. 굳이 그럴 필요가 없잖아요? 사용자 입장에서는 중첩구조로 보는게 이해하기 쉽습니다.

cache-loader( babel-loader() );

babel-loader 실행결과물을 전달받은 cache-loader 는 cacheDirectory 경로에 캐쉬파일을 생성합니다. 저장된 파일을 열어보면 Loader 의 수행결과가 JSON 형식으로 담겨져 있는 것을 확인 할 수 있습니다.

// node_modules/.cache/babel-loader/496e2f16a346a5bcf416a21ddf6ffdce.json
{"remainingRequest":"/[프로젝트 PATH]/node_modules/babel-loader/lib/index.js!/[프로젝트 PATH]/node_modules/@babel/runtime/helpers/esm/classCallCheck.js","dependencies":[{"path":"/[프로젝트 PATH]/node_modules/@babel/runtime/helpers/esm/classCallCheck.js","mtime":499162500000},{"path":"/[프로젝트 PATH]/node_modules/cache-loader/dist/cjs.js","mtime":499162500000},{"path":"/[프로젝트 PATH]/node_modules/babel-loader/lib/index.js","mtime":499162500000}],"contextDependencies":[],"result":[{"type":"Buffer","data":"base64:ZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gX2NsYXNzQ2FsbENoZWNrKGluc3RhbmNlLCBDb25zdHJ1Y3RvcikgewogIGlmICghKGluc3RhbmNlIGluc3RhbmNlb2YgQ29uc3RydWN0b3IpKSB7CiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCJDYW5ub3QgY2FsbCBhIGNsYXNzIGFzIGEgZnVuY3Rpb24iKTsKICB9Cn0="},{"version":3,"sources":["/[프로젝트 PATH]/node_modules/@babel/runtime/helpers/esm/classCallCheck.js"],"names":["_classCallCheck","instance","Constructor","TypeError"],"mappings":"AAAA,eAAe,SAASA,eAAT,CAAyBC,QAAzB,EAAmCC,WAAnC,EAAgD;AAC7D,MAAI,EAAED,QAAQ,YAAYC,WAAtB,CAAJ,EAAwC;AACtC,UAAM,IAAIC,SAAJ,CAAc,mCAAd,CAAN;AACD;AACF","sourcesContent":["export default function _classCallCheck(instance, Constructor) {\n  if (!(instance instanceof Constructor)) {\n    throw new TypeError(\"Cannot call a class as a function\");\n  }\n}"]}]}

저장된 캐쉬파일을 구성하는 중요속성 몇가지를 정리해 봅니다.

  • dependencies 는 Loader 가 분석한 원본 소스의 의존관계 정보를 담고 있습니다.
  • mtime 은 원본 소스의 마지막 수정시간 정보입니다. 원본 파일이 수정되면 캐쉬된 파일보다 높은 mtime 을 가집니다. 웹팩은 실행과정에서 mtime 이 높은 파일을 선별해 번들링을 진행합니다.
  • result 는 원본 소스의 실제 내용을 base64 인코딩 등의 최적화 옵션을 적용해 저장합니다.

cache-loader 사용시 유의사항

모듈 번들러는 특정 파일에 대한 처리를 캐쉬된 결과에서 먼저 찾습니다. 처리해야하는 파일이 캐쉬된 결과와 다르지 않다면 성능상의 이점을 위해 캐쉬파일을 사용합니다. 사용자는 특정 파일을 처리하기위해 등록한 여러 로더가 번들링 과정에서 반드시 실행된다고 가정해서는 안됩니다.

*.js 형식의 파일 처리과정에 ESLint 규칙 적용을 위해 eslint-loader 를 추가한 예를 살펴봅시다.

{
	test: /\.m?jsx?$/,
    use: [
    	/* cache-loader 선언 */
        {
			loader: '/.../node_modules/cache-loader/dist/cjs.js',
		},
        /* babel-loader 선언 */
        {
			loader: '/.../node_modules/babel-loader/lib/index.js'
		}
	]
},
...
{
	test: /\.m?jsx?$/,
    enforce: 'pre',
    use: [
        /* eslint-loader 선언 */
    	{
        	loader: '/Users/beizix/playground/rapid-vuejs/hello-lint/node_modules/eslint-loader/index.js',
        }
    ]
},
...  

babel-loader 에 의해 변형된 파일에 ESLint 준수여부를 판단하는건 무의미하기에 enforce: 'pre' 속성을 주어 eslint-loader 가 먼저 *.js 파일을 처리하도록 선언합니다.

번들링 시점에 잘못된 문법으로 작성된 *.js 파일이 발견되면 warn/error 메세지를 사용자에게 보여줍니다. ESLint 규칙을 위반했어도 번들링 작업은 계속 진행됩니다. js 파일은 babel-loader 에게 전달되고 cache-loader 는 수행결과를 저장합니다.

이때 다시 번들링 작업을 수행하면 어떤 결과가 있을까요?

Lint 룰을 위반한 소스는 여전히 수정되지 않았지만, (전과 다르게) warn/error 경고 없이 웹팩 빌드가 성공했다는 메세지만 보여줍니다. 모듈 번들러 관점에서 ESLint 규칙을 위반한 js 파일과 캐쉬된 파일은 동일합니다. 성능상의 이점을 얻기 위해 웹팩은 캐쉬된 파일을 불러와 번들링을 수행합니다. 캐쉬파일이 선택받는 순간 babel-loader 뿐 아니라 eslint-loader 도 실행기회를 박탈당하는 것입니다.

eslint-loader 는 Lint 룰을 위반한 js 파일이 수정했을 때에만 실행기회를 되찾을 것입니다.

맺음말

캐쉬파일을 생성하고 읽는 작업은 분명 자원소모를 필요로 합니다. 캐쉬의 장점을 얻으려면 비용소모가 큰 연산작업을 선별해 적용해야합니다. *.js 파일이 하위 브라우저에서 동작해야 한다면 babel-loader 와 함께 cache-loader 를 선언해주는게 좋습니다. *.vue 형식을 처리하는 vue-loader 도 매번 처리과정을 거치는 것은 낭비이기에 cache-loader 와 함께 선언되야 합니다. 선언된 로더의 동작여부는 캐쉬된 파일에 의해 결정된다는 점을 인지하면 복잡한 빌드설계 과정에서 예측하지 못한 상황이 발생하는 것을 방지할 수 있습니다.

profile
코딩꾼

3개의 댓글

comment-user-thumbnail
2019년 12월 30일

최고에요!!

답글 달기
comment-user-thumbnail
2020년 1월 5일

Good

답글 달기
comment-user-thumbnail
2021년 4월 14일

감사합니다.

답글 달기