vue.config.js (webpack version 4)
// vue.config.js
module.exports = {
/**
* productionSourceMap
* - sourcemap 기능 on/off 여부 // true면 sourcemap 기능 on
* - true로 설정하면 프로덕션 빌드시 소스맵을 생성한다.
* 이는 디버깅을 용이하게 해주지만, 프로덕션 빌드 크기가 증가하게되므로 로딩 시간에 영향을 준다.
* */
productionSourceMap: process.env.NODE_ENV === 'development',
/**
* publicPath
* - 파일 경로 앞에 붙을 경로
* - vue 프로젝트가 배포될 때 사용되는 기본 url 경로 지정
* - 주의사항: publicPath의 값은 항상 /(슬래시)로 시작하고 끝나야된다.
* 이 설정은 프로젝트의 HTML, CSS, JS 등의 참조 경로에 영향을 준다.
* 따라서 잘못 설정하면 배포 후 리소스를 제대로 로드하지 못하는 문제가 발생할 수 있다.
* 즉, publicPath는 프로젝트가 올바른 위치에서 모든 자원을 찾을 수 있도록 배포 경로를 지정하는 중요한 설정이다.
*/
publicPath: '/my-app/',
/**
* outputDir
* - 번들링 결과를 내보낼 디랙토리 경로 설정
* - 기본적으로 vue cli 프로젝트는 dist라는 이름의 디렉토리에 프로덕션 빌드 결과물을 저장한다.
* 그러나 이 위치를 변경하고 싶은 경우 outputDir 옵션을 사용하여 원하는 디렉토리 경로를 지정할 수 있다.
* 아래 설정을 적용하면 npm run build 명령어를 실행했을 때 생성되는 빌드 파일들은 my-build-folder라는 이름의 디렉토리에 저장된다.
* - 주의사항
* outputDir에 지정된 디렉토리는 빌드시마다 '완전히 삭제'되고 다시 생성된다.
* 따라서 이 디렉토리 내에 수동으로 파일을 추가하거나 변경하는 것은 권장되지 않는다.
* outputDir 설정을 변경할 경우, 배포나 CI/CD 파이프라인 설정 등에서도 해당 변경사항을 반영해야한다.
* 결론적으로, outputDir는 프로덕션 빌드 결과물이 저장될 디렉토리의 위치를 지정하는 설정이다.
* 기본값은 dist이지만, 필요에따라 다른 위치로 변경할 수 있다.
*/
outputDir: 'my-build-folder',
/**
* transpileDependencies
* - babel을 사용하여 특정 node_modules 내의 종속성들을 변환(transpile)할 때 사용한다.
* - 기본적으로 vue cli는 어플리케이션의 소스 코드만 babel을 통해 변환한다.
* 이는 대부분 npm 종속성들이 이미 es5로 변환되어 배포되기 때문이다.
* 그러나 일부 패키지는 es6+로 배포될 수 있으며, 이는 IE와 같은 오래된 브라우저에서 문제를 일으킬 수 있다.
* - transpileDependencies 옵션은 이러한 문제를 해결하기 위해 사용된다.
* 이 옵션을 사용하면, 지정된 종속성들을 babel을 통해 es5로 명시적으로 변환할 수 있다.
* - 아래 설정에서는 some-npm-package와 other-npm-package 2개의 npm package가 babel을 통해 변환된다.
* - 주의사항
* transpileDependencies 옵션에 패키지 이름 또는 정규식을 사용할 수 있다.
* 패키지 이름을 사용하면 특정 패키지를 명시적으로 지정할 수 있으며,
* 정규식을 사용하면 여러 패키지를 패턴 매칭으로 지정할 수 있다.
* 종속성을 변환하는 것은 빌드 성능에 영향을 줄 수 있다.
* 따라서 실제로 필요한 패키지만 변환하는 것이 좋다.
* 결론적으로, transpileDependencies는 오래된 브라우저의 호환성 문제를 해결하기 위해 특정 node_modules 내의 종속성들을 babel을 통해 변환할 때 사용되는 설정이다.
*/
transpileDependencies: ['some-npm-package', /other-npm-package/],
/**
* pages
* - pages 옵션은 vue cli 프로젝트에서 멀티 페이지 어플리케이션을 구성할 때 사용된다.
* - 기본적으로 vue cli는 단일 페이지 어플리케이션(SPA)을 생성한다.
* 그러나 pages 옵션을 사용하면 여러 개의 HTML 페이지를 가진 멀티 페이지 어플리케이션(MPA)을 구성할 수 있다.
* - 아래 예제에서 2개의 페이지, 즉, index와 subpage가 구성되어있다.
* 각 페이지는 해당 페이지의 시작점(entry), 템플릿, 출력될 파일 이름, 타이틀 등을 지정할 수 있다
* - 주요 특징
* - chunks: webpack의 코드 분할을 활용하여 각 페이지별로 필요한 javascript 청크만을 포함시킬 수 있다.
* - templates: 각 페이지는 자체적인 html 탬플릿을 가질 수 있다.
* - flexibility: SPA와는 달리 MPA는 각 페이지가 독립적으로 로드되므로, 빠른 초기 로드 시간을 제공하거나 SEO를 개선하는데 도움이 될 수 있다.
* pages 옵션은 큰 규모의 어플리케이션 또는 기존의 멀티 페이지 웹사이트와 통합이 필요한 경우에 유용하다.
*/
pages: {
index: {
// 페이지 시작점(entry)
entry: 'src/index/main.js',
// 페이지의 템플릿
template: 'public/index.html',
// 출력될 파일 이름
filename: 'index.html',
// 페이지에 주입될 타이틀
title: 'Index Page',
// 해당 페이지의 청크
chunks: ['chunk-vendors', 'chunk-common', 'index'],
},
subpage: {
entry: 'src/subpage/main.js',
template: 'public/subpage.html',
filename: 'subpage.html',
title: 'Sub Page',
},
},
/**
* configureWebpack 옵션은 vue cli 프로젝트의 웹팩 설정을 직접 조정하거나 확장하기 위해 vue.config.js에 사용된다.
* - 이 옵션을 사용하여 기존의 웹팩 설정을 수정하거나 추가적인 웹팩 설정을 제공할 수 있다.
* - configureWebpack는 객체 또는 함수로 지정될 수 있다.
* - 객체 형식
* 직접적인 웹팩 설정 변경을 위해 객체를 제공할 수 있다.
* 이 객체는 webpack-merge를 사용하여 기본 설정과 병합된다.
*
* configureWebpack: {
plugins: [
// 웹팩 플러그인 추가
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src/'),
}
}
}
*
*
* - 함수 형식
* 함수를 제공하여 웹팩 설정을 좀 더 세밀하게 조정하거나 웹팩 설정에 액세스하고 조작할 수 있다.
* configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
// 생산 환경에 대한 웹팩 설정 변경
} else {
// 개발 환경에 대한 웹팩 설정 변경
}
}
*
* - 주의사항
* configureWebpack을 사용할 때 주의해야할 점은 웹팩의 기본 설정을 덮어쓰지 않도록 조심해야한다는 것이다.
* 대신 설정을 확장하거나 조정하는 것이 좋다.
* 만약 웹팩 설정에 더 세밀한 조정이 필요한 경우 chainWebpack 옵션도 사용할 수 있다.
*
* 요약하면, configureWebpack은 Vue CLI 프로젝트의 웹팩 설정을 직접 확장하거나 수정하는데 사용되는 옵션이다.
* 이를 통해 프로젝트의 웹팩 구성에 특정 변경사항이나 추가사항을 적용할 수 있다.
*/
/**
* configureWebpack: {
* optimization: {
* splitChunks: { // 중복 모듈을 제거하거나 특정 조건에 따라 모듈을 별도의 청크로 분할하는 설정입니다.
* // 코드 분할 설정
* chunks: 'all',
* minSize: 20000,
* maxSize: 0,
* minChunks: 1,
* maxAsyncRequests: 5,
* maxInitialRequests: 3,
* automaticNameDelimiter: '~',
* automaticNameMaxLength: 30,
* name: true,
* cacheGroups: {
* // 캐시 그룹 설정
* },
* },
* runtimeChunk: 'single', // 런타임 코드를 별도의 청크로 분리 // 웹팩의 런타임 코드를 별도의 청크로 분리하려면 이 옵션을 사용합니다.
* }
* }
*
* 몇 가지 주요 optimization 옵션
* - minimize 및 minimizer: 코드를 최소화하려면 이 옵션을 사용하십시오. 예를 들어, TerserPlugin을 사용하여 JavaScript 코드를 최소화 할 수 있습니다.
* - usedExports: Tree shaking을 위한 설정입니다. 모듈에서 사용되지 않는 export를 제거합니다.
* */
/**
* html-webpack-plugin
* 1. HTML 파일 생성: 웹팩으로 번들된 자바스크립트, CSS, 그리고 기타 자원들을 포함하는 HTML 파일을 자동으로 생성한다.
* 2. 번들된 자원 연결: 웹팩의 출력으로 생성된 번들 파일들을 HTML 파일에 자동으로 연결해준다.
* 만약 여러개의 청크(chunk)가 있다면, 이 플러그인은 청크마다 해당하는 스크립트 태그나 링크 태그를 HTML에 추가해준다.
* 3. 템플릿 사용
* 사용자가 원하는 형태로 HTML 파일을 구성하기 위해 탬플릿을 사용할 수 있다.
* 4. Minification
* 생성된 HTML을 자동으로 최소화(minify)할 수 있다.
* */
/**
* configureWebpack: {
* plugins: [
* new HtmlWebpackPlugin({
template: 'public/index.html', // 생성하는 HTML 파일의 템플릿으로 public/index.html 파일을 사용한다.
filename: // 출력 HTML 파일의 이름을 결정 // VUE_APP_PROFILE이 local인 경우, 출력 파일의 이름은 index.html // 그렇지 않은 경우 dist/index.html (path.resolve는 절대 경로를 생성)
process.env.VUE_APP_PROFILE === 'local'
? 'index.html'
: `${path.resolve(
__dirname,
'dist',
)}/index.html`,
chunksSortMode: 'dependency', // html-webpack-plugin 3 version에서 사용되는 옵션
// 번들된 파일들이 HTML 내에 어떤 순서로 포함될지를 결정
// dependency는 의존성 순서대로 파일들을 포함시킴
minify: { // 생성된 HTML 파일을 최소화합니다.
removeComments: true, // HTML에서 주석을 제거합니다.
collapseWhitespace: true, // 불필요한 공백을 제거합니다.
removeAttributeQuotes: true // 가능한 경우, HTML 속성의 따옴표를 제거합니다.
},
}),
* ]
* }
* */
/**
* chainWebpack
* - chainWebpack은 Vue CLI의 웹팩 설정을 조정하기 위한 기능이다.
* - vue.config.js에서 chainWebpack을 사용하면 웹팩의 설정을 더욱 세밀하게 조정할 수 있다.
* 이 옵션은 웹팩 체인(webpack-chain)이라는 라이브러리를 활용하여 웹팩 설정을 조작하는데 사용한다.
*
* 기본적인 웹팩 설정을 직접 수정하기보다는, chainWebpack을 통해 웹팩 설정 객체에 접근하고, 이를 기반으로 원하는 변경사항을 적용하는 것이 더 안정적이고 효율적이다.
* */
/**
* chainWebpack: config => {
* // 아래 코드는 Vue CLI에서 기본으로 제공하는 prefetch 플러그인을 제거한다.
* // prefetch는 모든 컴포넌트를 미리 가져오기위한 링크를 생성한다.
* // 이 링크는 중요하지 않은 세번째 파티 리소스보다 낮은 우선 순위로 로드된다.
* // 따라서 초기 페이지 로드에는 영향을 미치지 않지만, 네트워크 활동이 있을 때, 중요한 리소스의 로딩을 지연시킬 수 있다.
* // 이 플러그인을 제거하면 해당 기능을 비활성화하게된다.
* config.plugins.delete('prefetch');
* config.plugins.delete('prefetch-[appname]'); // appname이란 pages 옵션에서 설정한 각 앱 이름을 뜻한다.
* }
* */
/**
* chainWebpack: config => {
* // preload 플러그인을 삭제한다.
* // preload 플러그인은 <link rel="preload"> 태그를 생성하여 초기 로딩 성능을 향상시키기 위해 특정 리소스를 미리 로드하도록 브라우저에 지시하는 역할을 한다.
* // preload 플러그인을 삭제하면 해당 태그가 생성되지 않아 해당 리소스들의 미리 로딩 기능이 비활성화된다.
* config.plugins.delete('preload');
* config.plugins.delete('preload-[appname]'); // appname이란 pages 옵션에서 정의한 앱 이름을 뜻한다.
* }
* */
/**
* chainWebpack: config => {
* config.resolve.alias.set('[별칭]', '[별칭이가리키는경로]');
* }
* */
/**
* 빌드 환경을 브라우저 전용으로 세팅
* chainWebpack: config => {
* // config.plugin('processBrowser') // 웹팩 설정에서 processBrowser라는 이름의 플러그인을 타겟팅하거나 생성한다.
* // 만약 해당 이름의 플러그인이 존재하지 않으면 새로운 플러그인 인스턴스를 생성한다.
* // .use(require('webpack').DefinePlugin, [{'process.browser': true,}]) // DefinePlugin은 웹팩에서 제공하는 내장 플러그인으로, 소스 코드 내에서 특정 키워드를 대체하는데 사용된다.
* // 여기서는 process.browser라는 키워드를 true값으로 대체하도록 설정되어있다.
* // 즉, 소스코드 내에서 process.browser를 사용하면 빌드시에 이 부분이 true로 치환된다.
* // 결론적으로 아래 코드는 Vue 프로젝트 웹팩 설정에 process.browser라는 전역 변수를 주입하는 역할을 한다.
* // 이 변수는 주로 서버 사이드 렌더링(SSR)과 같은 환경에서 브라우저 환경을 구분하기 위해 사용되며, 여기서는 브라우저 환경임을 나타내기 위해 true로 설정되어있다.
* config.plugin('processBrowser').use(require('webpack').DefinePlugin, [
{
'process.browser': true,
},
]);
* }
*/
/**
* 웹팩 글로벌 이름 변경
* chainWebpack: config => {
* // 웹팩의 설정에서 config.output.jsonPFunction은 웹팩에서 비동기 코드를 로드할 때 사용하는 jsonp 함수의 이름을 설정하는 옵션이다.
* // jsonp는 json with padding의 약자로 크로스 도메인 문제를 해결하기위해 사용되는 기법중 하나이다.
* // 웹팩에서는 비동기 모듈(예: 코드 스플리팅 결과)을 로드하기위해 jsonp를 사용한다.
* // config.output.jsonpFunction 옵션을 설정하면
* // - 여러 웹팩 앱들이 같은 페이지에서 동작할 때나,
* // - 여러 웹팩 빌드가 하나의 페이지에 포함될 때,
* // 서로 다른 jsonp 함수 이름을 가지도록 할 수 있다.
* // 이렇게 함으로써 각 웹팩 빌드간 충돌을 피할 수 있다.
* // 예를 들어, config.output.jsonpFunction('xxxx') 코드는 웹팩이 생성하는 비동기 로드 코드에서 사용하는 jsonp 함수의 이름을 xxxx로 설정한다.
* // 이 설정 없이 여러 웹팩 앱이나 빌드들이 동일한 페이지에서 실행될 경우, 기본적으로 모든 웹팩 빌드가 같은 jsonp 콜백 함수 이름을 사용하게 되므로 충돌이 발생할 가능성이 있다.
* config.output.jsonpFunction('xxxx');
* }
*/
/**
* chainWebpack: config => {
* // config.module.rule('xxxx') // 웹팩의 설정에서 모듈의 처리 규칙을 정의하는 부분이다.
* // 여기서 xxxx는 이 규칙의 이름을 뜻한다.
* // 이 이름은 임의로 지정할 수 있으며, 특별한 기능이나 의미를 가진 것은 아니다.
* // 단순히 이 규칙을 참조하거나 수정할 때 사용하기 위해 지정된다.
* // .test(...) // 이 규칙이 적용될 파일의 형식을 정규 표현식으로 정의하는 부분이다.
* // 아래 코드를 보면 .png, .jpg, .jpeg, .gif, .webp, .ico 확장자를 가진 파일을 의미한다.
* // jpe?g에서의 ?는 e 문자가 있을 수도 있고, 없을 수도 있다는 것을 의미한다.
* // 따라서 .jpg와 .jpeg 둘 다 해당된다.
* // (\?.*)?$는 이러한 이미지 파일 뒤에 쿼리 파라미터가 올 수 있다는 것을 의미한다.
* // 쿼리 파라미터는 옵션이다.
* // 결론적으로, 이 코드는 .png, .jpg, .jpeg, .gif, .webp, .ico 확장자를 가진 이미지 파일과 그 뒤에 올 수 있는 선택적인 쿼리 파라미터를 대상으로하는 웹팩 규칙을 정의한다.
* // 이 규칙에는 어떻게 이러한 이미지 파일들을 처리할지에 대한 로더나 플러그인 설정이 추가로 이어질 수 있다.
* config.module.rule('images').test(/\.(png|jpe?g|gif|webp|ico)(\?.*)?$/);
* }
* */
/**
* chainWebpack: config => {
* // 해당 코드는 웹팩 설정에서 특정 모듈 처리 규칙을 정의하는 부분이다. 구체적으로 다음을 수행한다.:
// config.module.rule('xxxx'); // 웹팩 설정의 모듈 섹션에서 새로운 규칙을 정의하며, 이 규칙의 이름을 'xxxx'로 지정한다.
// .test(/\.xxxx/); // 이 규칙이 적용될 파일의 형식을 정규 표현식으로 지정한다. 여기서는 .xxxx 확장자를 가진 파일을 의미한다.
// .use('xxxx-loader'); // 이 규칙에 대한 로더를 지정한다. 이 로더의 이름은 'xxxx-loader'로 지정되며, 이 이름은 후에 로더의 설정을 수정하거나 참조할 때 사용될 수 있다.
// .loader('xxxx-loader'); // 실제로 사용될 로더를 지정한다. 여기서는 'xxxx-loader' 로더를 사용하도록 설정한다.
// 결론적으로, 이 코드는 .xxxxx 확장자를 가진 파일들을 처리하기 위해 xxxx-loader를 사용하도록 웹팩에 지시한다.
* config.module
.rule('xxxx')
.test(/\.xxxx/)
.use('xxxx-loader')
.loader('xxxx-loader');
// 해당 코드는 웹팩 설정에서 특정 모듈 처리 규칙을 정의하는 부분이다. 구체적으로는 다음과 같은 동작을 수행하게 된다.
// config.module.rule('xxxx'); // 웹팩 설정의 모듈 섹션에서 새로운 규칙을 정의하며, 이 규칙의 이름을 'xxxx'로 지정한다.
// .test(/Xxxx\.js$/); // 이 규칙이 적용될 파일의 형식을 정규 표현식으로 지정한다. 여기서는 파일 이름이 Xxxx.js로 끝나는 JavaScript 파일을 대상으로 한다.
// .use('xxxx-loader'); // 이 규칙에 대한 로더를 지정한다. 이 로더의 이름은 'xxxx-loader'로 지정되며, 이 이름은 후에 로더의 설정을 수정하거나 참조할 때 사용된다.
// .loader('xxxx-loader'); // 실제로 사용될 첫 번째 로더를 지정한다. 여기서는 'xxxx-loader' 로더를 사용하도록 설정된다.
// .options({ inline: true }); // 'xxxx-loader'에 대한 옵션을 설정한다. 여기서는 inline 옵션을 true로 설정했다. 해당 옵션의 역할은 xxxx-loader의 문서나 설정에서 확인할 수 있다.
// .end(); // 현재 로더의 설정을 마치고, 규칙 설정으로 돌아간다.
// .use('babel-loader'); // 동일한 규칙 내에서 다른 로더를 지정한다. 여기서는 'babel-loader'의 이름으로 추가 로더를 설정한다.
// .loader('babel-loader'); // 실제로 사용될 두 번째 로더를 지정한다. 여기서는 'babel-loader'를 사용하도록 설정된다.
// 결론적으로, 이 코드는 Xxxx.js로 끝나는 파일들에 대해 두 개의 로더를 순서대로 적용하도록 웹팩에 지시한다. 먼저 'xxxx-loader'를 적용하고, 그 다음으로 'babel-loader'를 적용하게 된다.
config.module
.rule('xxxx')
.test(/Xxxx\.js$/)
.use('xxxx-loader')
.loader('xxxx-loader')
.options({ inline: true })
.end()
.use('babel-loader')
.loader('babel-loader');
* }
* */
/**
* chainWebpack: config => {
* // 해당 코드는 웹팩 설정에서 특정 모듈 처리 규칙을 정의하면서 특정 파일을 제외하는 설정을 하는 부분이다.
// 자세히 살펴보면
// config.module.rule('js'); // 웹팩 설정의 모듈 섹션에서 'js'라는 이름으로 정의된 규칙을 참조한다. 이 규칙은 보통 JavaScript 파일들을 처리하기 위해 사용된다.
// .exclude.add(/Xxxxx\.js$/); // 'js' 규칙에 대해서 제외할 파일 패턴을 추가한다.
// 이 설정에 의해 파일 이름이 Xxxxx.js로 끝나는 JavaScript 파일은 해당 'js' 규칙에서 제외되게 된다. 즉, 'js' 규칙에 설정된 로더나 다른 처리들이 Xxxxx.js 파일에는 적용되지 않는다.
// 결론적으로, 이 코드는 웹팩에 Xxxxx.js 파일을 'js' 규칙에서 제외하라고 지시한다.
* config.module.rule('js').exclude.add(/Xxxxx\.js$/);
* }
* */
/**
* chainWebpack: config => {
* // 이 코드는 웹팩 설정에서 특정 플러그인을 추가하거나 설정하는 역할을 한다.
// 자세히 분석하면,
// config.plugin('Xxxx'); // 웹팩 설정에서 'Xxxx'라는 이름의 플러그인을 참조하거나 생성한다.
// 만약 해당 이름의 플러그인이 이미 존재한다면 그것을 참조하고, 아니라면 새로 생성하게 된다.
// .use(Xxxx); // 'Xxxx' 이름의 플러그인에 대해 사용할 플러그인의 생성자 함수나 클래스를 설정한다.
// 여기서 Xxxx는 실제 웹팩 플러그인의 생성자 함수나 클래스가 되어야 한다.
// 이 함수나 클래스는 새 플러그인 인스턴스를 생성할 때 호출되며, 이를 통해 웹팩 빌드 프로세스에 개입하거나 추가 작업을 수행하는 등의 기능을 구현할 수 있다.
// 예를 들면, 만약 Xxxx가 DefinePlugin이라는 웹팩 내장 플러그인일 경우, 이 코드는 웹팩 빌드 과정에서 전역 변수를 정의하는 역할을 수행하게 된다.
// 결론적으로, 위 코드는 웹팩 설정에 Xxxx라는 이름의 플러그인을 추가하거나 설정하며, 이 플러그인의 기능을 Xxxx 생성자 함수나 클래스에 정의되어 있다는 것을 의미한다.
* config.plugin('Xxxx').use(Xxxx);
* }
* */
}
CustomWebpackPlugin.js
// CustomWebpackPlugin.js
// webpack에서 플러그인을 작성하는 것은 상당히 강력한 기능이다.
// 플러그인은 webpack의 빌드 프로세스에 개입하여, 사용자 지정 로직을 추가하거나 기존 로직을 변경할 수 있다.
// 기본적인 webpack 플러그인의 구조는 다음과 같다.
// 1. 플러그인 클래스 정의
// - 대부분의 플러그인은 클래스 형태로 작성된다.
// 2. apply 메서드
// - 모든 webpack 플러그인은 apply 메서드를 가지며, 이 메서드는 webpack에서 해당 플러그인을 적용할 때 호출된다.
// 3. event listener
// - apply 메서드 내에서 webpack의 빌드 프로세스에서 발생하는 이벤트에 리스너를 연결한다.
// 예시:
class CustomWebpackPlugin {
// 생성자에서 옵션을 받을 수 있습니다.
constructor(options) {
this.options = options;
}
// apply 메서드는 webpack에 의해 호출됩니다.
apply(compiler) {
// `emit` 이벤트에 연결하여, 파일을 출력하기 전에 로직을 수행합니다.
compiler.hooks.emit.tapAsync('CustomWebpackPlugin', (compilation, callback) => {
// 여기서 custom 로직을 수행합니다.
console.log('This is an example CustomWebpackPlugin!');
// 모든 작업이 완료되면 callback을 호출하여 webpack에게 플러그인 로직이 완료됐음을 알립니다.
callback();
});
}
}
module.exports = CustomWebpackPlugin;
webpack.config.js
// webpack.config.js
const CustomWebpackPlugin = require('./CustomWebpackPlugin');
module.exports = {
// ... other webpack config
plugins: [
new CustomWebpackPlugin({ /* your options here */ })
]
};
// const RawSource = require('webpack-sources/lib/RawSource');
// webpack-sources는 Webpack의 내부 동작에 사용되는 여러 유형의 source (즉, 생성된 빌드의 소스 코드 또는 애셋)를 제공하는 패키지이다.
// 이 패키지에는 원시 코드, 맵 파일, 바이너리 등을 처리하는 여러 클래스가 포함되어 있다.
// RawSource는 이러한 클래스 중 하나로, 원시 문자열의 내용을 가진 소스 코드 객체를 나타낸다.
// 이 클래스는 Webpack 플러그인 작성시에 특히 유용하게 사용될 수 있다.
// 플러그인이 특정 단계에서 자체적으로 코드 또는 애셋을 생성하거나 변경할 때 RawSource를 사용하여 해당 내용을 Webpack의 컴파일 과정에 추가하거나 반영할 수 있다.
// 예시:
const RawSource = require('webpack-sources/lib/RawSource');
// 원시 문자열 내용을 표현하는 소스 객체 생성
const source = new RawSource('console.log("This is my raw content.");');
// 이제 `source`는 원시 내용을 포함하는 소스 객체이며,
// 이를 Webpack의 컴파일러나 컴파일 과정에 추가하는 데 사용할 수 있다.
// --------------------------------------------------------------------------------
// 플러그인에서 Webpack의 출력 애셋으로 이 source를 추가하려면, compilation.assets에 추가하면된다.
compilation.assets['my-output-file.js'] = source;
// 이렇게하면 종 빌드 출력에 my-output-file.js라는 파일이 포함되며, 그 내용은 위에서 정의한 원시 문자열이된다.
// custom webpack plugin
class MyWebpackPlugin {
apply(compiler) {
compiler.hooks.emit.tap(this.constructor.name, compilation => {
// ...
});
}
}
// 위의 코드는 Webpack을 위한 커스텀 플러그인의 기본 구조를 나타냅니다.
// 코드를 구성하는 주요 부분에 대해 설명하겠습니다.
// 1. class MyWebpackPlugin:
// 이 클래스는 Webpack 플러그인을 정의합니다. Webpack 플러그인은 대체로 클래스 기반으로 작성됩니다. 플러그인의 이름은 MyWebpackPlugin입니다.
// 2. apply(compiler):
// apply는 모든 Webpack 플러그인에 필요한 메서드입니다. Webpack은 플러그인을 초기화할 때 이 메서드를 호출합니다.
// compiler 객체는 Webpack의 핵심 객체로, 플러그인이 Webpack 라이프사이클의 여러 단계에 훅을 추가할 수 있도록 합니다.
// 3. compiler.hooks.emit.tap(...):
// compiler.hooks는 Webpack 빌드 프로세스 중 다양한 시점에서 실행할 수 있는 훅의 집합입니다.
// emit 훅은 Webpack이 파일을 출력하기 직전에 호출됩니다. 다시 말해, 빌드 과정에서 모든 모듈과 청크가 처리되고 난 후에 이 훅이 실행됩니다.
// tap 메서드를 사용하여 해당 훅에 함수를 등록합니다. 첫 번째 인자는 플러그인의 이름 (여기서는 this.constructor.name으로 MyWebpackPlugin을 참조)이며, 두 번째 인자는 실제로 실행될 콜백 함수입니다.
// 4. compilation:
// compilation 객체는 현재 실행 중인 빌드 프로세스에 관한 모든 정보를 포함합니다. 이 객체를 통해 생성된 에셋, 모듈, 청크 등의 정보에 접근할 수 있습니다. 또한 에셋을 추가하거나 수정하는 등의 작업을 수행할 수도 있습니다.
// 코드의 주요 내용은 이렇습니다. 실제 로직이 생략되어 있기 때문에 // ... 부분에 원하는 작업 (예: 특정 에셋 추가/수정)을 추가하여 플러그인의 기능을 확장할 수 있습니다.
// compiler.hooks.emit.tap 메서드와 compiler.hooks.emit.tapAsync 메서드의 차이
// compiler.hooks.emit.tap와 compiler.hooks.emit.tapAsync는 둘 다 Webpack의 emit 훅에 함수를 연결하는 방법입니다. 그러나 그들이 처리하는 작업의 비동기 방식에 차이가 있습니다.
// 1. compiler.hooks.emit.tap:
// 이 메서드는 동기적으로 훅에 함수를 연결합니다.
// 콜백 함수는 동기적으로 실행되므로 비동기 작업을 포함할 수 없습니다.
// tap을 사용할 때, 다음 단계로 넘어가기 전에 현재 연결된 함수가 완전히 실행되어야 합니다.
// compiler.hooks.emit.tap('MyPlugin', compilation => {
// // do some synchronous work
// });
// 2. compiler.hooks.emit.tapAsync:
// 이 메서드는 비동기적으로 훅에 함수를 연결합니다.
// 콜백 함수는 추가적인 인자로 callback 함수를 받습니다. 비동기 작업이 완료되면 이 callback을 호출하여 Webpack에게 알려줘야 합니다.
// 이 방식은 파일 작성, 네트워크 요청과 같은 비동기 작업을 처리할 때 유용합니다.
// compiler.hooks.emit.tapAsync('MyPlugin', (compilation, callback) => {
// // do some asynchronous work
// // ...
// // when done, call the callback
// callback();
// });
// 결론적으로, 동기 작업을 수행할 때는 tap을, 비동기 작업을 처리할 때는 tapAsync를 사용합니다.
// 비동기 작업을 수행하는 경우 반드시 tapAsync의 콜백 함수를 호출하여 작업이 완료되었음을 Webpack에 알려야 합니다.
// custom webpack plugin
// 아래 코드는 웹팩 플러그인의 구조로 되어 있으며, 특정 번들들에 대한 자바스크립트와 CSS 파일 목록을 JSON 형태로 출력하는 기능을 수행한다.
// 1. TestWebpackPlugin
// - 이는 웹팩 플러그인의 주요 클래스이다.
// 웹팩 플러그인은 기본적으로 apply 메서드를 포함해야한다.
// 이 메서드는 웹팩에 의해 호출되며, 웹팩의 컴파일러 객체(compiler)를 인자로 받는다.
// 2. compiler.hooks.emit.tap
// - 이는 웹팩의 빌드 프로세스 중에 "emit" 단계에서 특정 로직을 실행하도록 설정합니다. "emit" 단계는 웹팩이 파일을 출력하기 직전의 단계입니다.
// 3. customWebpackFunc
// - 이 함수는 주어진 bundleName에 대한 자바스크립트와 CSS 파일들의 목록을 가져와 JSON 형태로 변환하고, 이를 출력합니다. 함수는 아래의 로직을 수행합니다:
// - entrypoints.get(bundleName).chunks를 사용해 주어진 번들 이름에 대한 청크들의 목록을 가져옵니다.
// - 각 청크에 있는 파일들 중에서 .js 또는 .css 확장자를 가진 파일들만 선택합니다.
// - "hot-update"를 포함하지 않는 파일들만 필터링합니다. 이는 HMR(Hot Module Replacement)와 관련된 임시 업데이트 파일들을 제외하기 위함입니다.
// - 파일의 경로에서 마지막 부분만 가져와 files 배열에 추가합니다.
// - 최종적으로 files 배열을 JSON 형태로 변환하고, 웹팩의 출력 결과물에 추가합니다.
// 4. RawSource
// - 이는 웹팩 플러그인에서 제공하는 유틸리티로, 원시 문자열을 웹팩의 출력 소스 형식으로 변환하는데 사용됩니다.
// 5. compilation.emitAsset
// - 이는 웹팩의 compilation 객체에서 제공하는 메서드로, 결과물로 출력할 파일을 추가하는데 사용됩니다.
// 6. process.env.VUE_APP_TEST
// - 이는 환경 변수에서 특정 값을 체크하는 로직입니다. 이 값이 "local"일 경우와 아닐 경우에 따라 출력할 파일의 경로가 달라집니다.
// 결론적으로, 이 플러그인은 "test1", "test2", "test3"라는 이름의 세 개의 번들에 대한 .js 및 .css 파일들의 목록을 JSON 형태로 출력합니다. 출력 파일의 이름은 각각 "test1.json", "test2.json", "test3.json"입니다.
class TestWebpackPlugin {
apply(compiler) {
compiler.hooks.emit.tap(this.constructor.name, compilation => {
customWebpackFunc({bundleName: 'test1', fileName: 'test1.json', compilation});
customWebpackFunc({bundleName: 'test2', fileName: 'test2.json', compilation});
customWebpackFunc({bundleName: 'test3', fileName: 'test3.json', compilation});
});
}
}
function customWebpackFunc({bundleName, fileName, compilation}) {
const files = compilation.entrypoints.get(bundleName).chunks
.flatMap(chunk =>
chunk.files
.filter(file => file.match(/\.(js|css)$/))
.filter(file => file.indexOf('hot-update') === -1),
)
.map(file => {
const s = file.split('/');
return s[s.length - 1];
});
const rawSource = new RawSource(JSON.stringify(files));
if (process.env.VUE_APP_TEST === 'local') {
compilation.emitAsset(fileName, rawSource);
} else {
compilation.emitAsset(`dist/${fileName}`, rawSource);
}
}
// ---------------------------------------------------------------
// symbolic link란?
// - 간단히 말해서 윈도우의 바로가기 파일 같은 것이다.
// - 프로젝트 코드 뿐만아니라 외부에서 불러온 패키지 모듈도 같이 살펴봐야될 때가 있는 경우
// - 그 외부에서 불러온 패키지 모듈도 내가 개발하는 경우 (그런데 아직 개발 완료를 못한 경우)
// ---------------------------------------------------------------
// 폴더구조 예시
// my-project
// ㄴ node_modules
// ㄴ my-some-package <- 내가 개발한 패키지 모듈, my-project에서 외부 모듈로 설치함
// my-some-package <- 내가 개발하고 있는 패키지 모듈
// 테스트 프로세스
// - my-project/node_modules/my-some-package에서 직접 수정하기 좀 그럼
// - 내가 개발하고있는 패키지 모듈 경로에서 수정한다면.. 테스트 프로세스는 다음과 같아질 것이다.
// my-some-package 모듈을 수정 후 npm 배포 후, my-project에 버전업 설치 후 테스트
// 이러한 상황에서 간단하게 수정 및 테스트하는 방법이 있다.
// npm link 명령어를 사용하면된다.
// 사용법
// npm link는 서로 다른 두 개의 패키지를 로컬 컴퓨터 내에서 연결한다.
// 연결 과정은 다음과 같다.
// my-project <- 2. 여기서 npm link my-some-package 명령어를 실행하면 my-some-package는 방금 생성한 global symlink를 바라보게된다.
// ㄴ node_modules
// ㄴ my-some-package
// my-some-package <- 1. 여기서 npm link 명령어 실행하면 global symlink가 생성된다.
// global symlink는 해당 PC의 global folder에 생성된다.
// 이러한 원리로(global symlink를 통해) 서로 다른 위치에 있는 2개의 패키지가 연결된다.
// my-some-package 패키지 모듈을 수정하면 my-project/node_modules/my-some-package 이 모듈도 같이 수정되기 때문에
// 모듈 테스트가 훨씬 더 수월해진다.
// ---------------------------------------------------------------
// symlink 삭제
// my-project <- 여기서 npm uninstall my-some-package && npm install 실행 (global symlink 연결 끊기)
// ㄴ node_modules
// ㄴ my-some-package
// my-some-package <- 여기서 npm uninstall 실행 (gloabal symlink 제거)
// vue.config.js
module.exports = {
chainWebpack: config => {
config.resolve.symlinks(false);
// 웹팩의 resolve.symlinks 설정을 웹팩이 모듈 경로를 해석할 때 심볼릭 링크(symbolic link)를 어떻게 처리할지를 지정한다.
// 심볼릭 링크는 파일이나 디렉토리에대한 참조로, 주로 패키지 링크나 로컬 개발을 도와주기 위해 사용된다.
// 예를 들면, npm link 또는 yarn link는 로컬 패키지를 전역 패키지처럼 사용하게해주는 명령어로, 내부적으로 심볼릭 링크를 사용한다.
// config.resolve.symlinks 설정의 기본값은 true이다.
// - true로 설정되면 웹팩은 모듈 경로가 심볼릭 링크일 경우 실제 경로(real path)로 해석하게된다. sym link 사용
// - false로 설정되면, 웹팩은 심볼릭 링크로 지정된 원래의 경로를 그대로 사용하게된다. sym link 사용 X
// config.resolve.symlinks(false) 코드는 웹팩에게 모듈 경로가 심볼릭 링크일 때,
// 이를 실제 경로로 바꾸지 않고 원래의 경로를 그대로 사용하도록 지시하는 것이다.
// 이 설정은 로컬 개발환경에서 특정 패키지를 심볼릭 링크로 연결했을 때 유용할 수 있다.
// 특히 HMR(Hot Module Replacement)와 같은 기능을 제대로 동작하게 하기 위해 필요한 경우가 있다.
}
}
// vue.config.js
// vue.config.js에서 pluginOptions은 Vue CLI 프로젝트에 사용된 플러그인들을 위한 사용자 지정 설정 옵션을 제공한다.
// 즉, 개발자가 설치한 Vue CLI 플러그인에 추가 설정이나 오버라이드가 필요할 때 pluginOptions을 사용하여 해당 설정을 제공할 수 있다.
// 예를 들어, 특정 Vue CLI 플러그인이 프로젝트에 추가 기능을 제공하면서도 추가적인 구성이나 옵션을 요구할 수 있다.
// 이 때 pluginOptions은 그러한 플러그인에 대한 구성 값을 지정하는 데 사용될 수 있다.
module.exports = {
pluginOptions: {
// ...
}
}
// webpack dev server
module.exports = {
// ...
// 원격 PC에 webpack dev server를 실행하고, 내 PC에서 원격 PC의 webpack dev server로 접속할 때 발생하는 에러(Invalid Host header)를 해결하기 위해 사용하는 옵션같다.
devServer: {
// webpack dev server 는 기본적으로 개발중인 웹 애플리케이션에 대한 엑세스를 위해 사용되는 호스트 헤더를 검사한다.
// 이는 DNS 리바인딩 공격을 방지하기위한 보안 기능이다.
// DNS 리바인딩 공격은 공격자가 유해한 스크립트를 실행하여 사용자의 웹브라우저를 조작하여 특정 호스트명으로 악의적인 IP 주소로 요청을 전송하도록 만드는 방법이다.
// disableHostCheck 옵션을 true로 설정하면, webpack dev server 는 호스트 헤더의 유효성 검사를 비활성화하게 된다.
// 이는 웹 어플리케이션에 대한 요청이 어떤 호스트명을 사용하여도 허용되어야할 경우 유용하다.
// 예를 들어, 여러 서브도메인에서 작업 중인 웹 애플리케이션에 엑세스해야할 때 사용할 수 있다.
// 그러나 보안 상의 이유로 일반적으로 disableHostCheck 옵션을 사용하는 것은 권장되지 않는다.
// 이 옵션은 개발 환경에서만 사용되어야하며, 프로덕션 환경에서는 절대 사용되어서는 안된다.
// 간단히 요약하면, disableHostCheck 는 webpack dev server 가 호스트 헤더 검사를 건너뛰게해주는 옵션이다.
// 이를 사용하면 보안 위험이 있으므로 주의해야한다.
disableHostCheck: true,
// public 옵션을 사용하여 웹브라우저가 연결하는 호스트와 포트를 지정할 수 있다.
// 단, 아래와 같이 설정할 경우, local-test.my-app.net 호스트가 127.0.0.1를 바라보도록 hosts 파일을 수정할 필요가 있다.(로컬 DNS, 맥은 gas Mask 사용하면 편함)
public: 'local-test.my-app.net:8080',
// host 옵션은 서버가 바인딜될 IP 주소를 지정한다.
// 이 설정은 특정 네트워크 인터페이스를 통해 서버를 접근하려는 경우 유용하다.
// 기본 값은 'localhost'이다.
// host 옵션의 몇가지 사용 예는 다음과 같다.
// host: 'localhost' // 서버는 오직 현재의 기기, 즉 로컬 기기에서만 접근 가능하다.
// host: '0.0.0.0' // 서버는 현재 기기의 모든 네티워크 인터페이스를 통해 접근 가능하다.
// // 이 설정은 외부 네트워크(예: 모바일 기기)에서 개발 서버에 접근할 때 유용하다.
// // 주의: 0.0.0.0 을 사용하면 서버가 공용 네트워크에서 접근 가능하므로 보안 상의 이유로 실제 운영 환경이나 공개 네트워크에서는 사용하지 않아야 한다.
// host: '192.xxㅌ.x.x' // 특정 IP 주소: 만약 여러개의 네트워크 인터페이스가 있거나 특정 IP 주소에서만 서버에 접근하길 원한다면, 이렇게 IP를 지정할 수 있다.
host: '0.0.0.0', // host 옵션을 설정하면 해당 주소로 바인딩되고, 해당 주소로 서버에 접근할 수 있게된다.
port: 8080,
https: {
ca: '파일경로',
cert: '파일경로',
key: '파일경로',
},
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods':
'GET, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers':
'X-Requested-With, content-type, Authorization',
},
historyApiFallback: {
rewrites: [
{from: 'regex or string', to: 'string'},
{
from: 'regex or string',
to: 'string',
},
{ from: 'regex or string', to: 'string' },
],
},
proxy: {
// /xxxx/yyy/ 경로가 아닐 경우, zzzz 경로로 요청보냄 (proxy)
'^(?!/xxxx/yyy/).*': {
target: 'zzzzzz',
},
},
}
}