webpack의 기본 옵션
- entry: 앱이 번들처리를 시작할 지점을 뜻하며 index.js부터 시작해 import문을 따라 빌드를 진행한다.
- output: 번들링된 결과물의 위치 및 파일 이름을 지정해줍니다. path나 filename이라는 옵션으로 위치 및 파일이름을 지정할 수 있다.
이미지나 파일 같은 외부 리소스를 로드할때는 publicPath가 필요한데 이는 prefix의 개념으로 사용자가 webpack.config.js에 설정한 주소가 실제 배포되는 주소 앞에 설정이 된다.
src="picture.jpg" -> /assets/picture.jpg
-
resolve: 모듈을 해석하는 방식을 변경할 수 있다. 예를 들어 alias 옵션을 이용할때 특정 모듈의 별칭을 만들 수 있으며 일반적으로는 사용되는 src 폴더의 별칭을 지정할 수 있다.
-
loader: 기본적으로 webpack은 javascript 및 json파일만 해석 가능한데 로더를 사용하면 webpack이 다른 포맷의 파일을 처리하고 앱에서 사용 가능한 모듈로 변환이 가능하다.
-
plugin: 로더가 파일 단위로 처리한다면 플러그인은 주로 번들된 결과물을 처리한다.
-
externals: 우리도 코드가 의존하고 있지만 빌드 과정에서 포함하지 않아도 되는 axios, jQuery같은 써드파티 라이브러리들을 여기에 명시하면 빌드 프로세스에 포함되지 않는다.
-
performance: 성능과 관련된 옵션으로 파일 크기가 250kb를 넘어갈 경우 경고를 띄워주는 옵션 등이 있다.
webpack의 최적화

- 번들 사이즈 줄이기
- webpack mode
- source-map
- 코드 스플리팅
- 트리 쉐이킹
- 빌드 속도 올리기
webpack mode
webpack 모드의 종류
- Development: 강력한 소스 매핑, localhost 서버에서는 라이브러리 로딩이나 hot module replacement 기능을 목적으로 사용한다.
- production: 로드 시간을 줄이기 위해 번들 최소화 가벼운 소스맵 및 에셋 최적화에 초점을 맞춘다.
기본적으로 tersurPlugin을 이용하여 코드를 압축, 난독화를 한다.
Source-map
- production으로 빌드한 파일과 원본 파일을 서로 연결시켜주는 기능이다.
- 소스맵은 원본 코드를 특정 알고리즘으로 인코딩하여 특정 키워드로 매핑을 시켜 놓으면 나중에 브라우저에서
난독화된 코드를 그대로 디코딩하여 복원시킬 수 있다.
{
version : 3,
file: "bundle.js"
sourceRoot: "",
sources: ["a.js", "b.js"]
names: ["src", "maps", "are", "fun"],
mapping: "AAgBC, SAAQ, CAAEA"
}
- 소스맵의 구성
- file : 연결된 파일의 이름
- sources : bundle.js를 만드는데 활용되는 소스 코드 파일 목록
- names : 소스코드의 모든 변수와 함수 이름이 기록된다.
- mappings : 실제 코드와 매핑할 수 있도록 하는 데이터이며 Base64 VLQ로 인코딩되어 기록된다.
소스 레벨 종류
- None
- 소스맵을 생성하지 않는다.
- 최대 성능의 Production build시 추천하는 방식
- Source-map:
- 고품질 소스맵을 포함한 Production build시 추천하는 옵션
- 기본적으로 none과 source-map이 있고 대부분의 소스맵은 (option)- source-map 형식으로 진행된다.
option의 종류
-
eval
- eval이 붙는 소스맵 옵션은 자바스크립트 함수인 eval을 사용해서 소스맵을 만듭니다
- eval은 mode: development에서devtool 옵션을 주지 않으면 기본적으로 들어가는 소스맵이므로 실제로 소스맵을 생성하지 않으려면 devtool: false를 준 상태로 빌드를 진행해야한다.
- eval 함수는 각 모듈을 따로 실행시켜 수정된 모듈만 재빌드하여 빠르지만 정확한 소스 코드 위치를 맵핑 하지 못한 경우가 종종 있습니다
-
inline
- inline 옵션은 map 파일을 만들지 않고 주석에 파일을 data URL로 작성한다.
- 따라서, source map이 독립된 파일로 존재하지 않고 bundle.js 파일 내에 포함되게 됩니다

- hidden
- 소스맵 옵션과 거의 동일하지만 소스맵에 대한 참조가 추가 되지 않는다.
- 주로 사용자에게 개발자 도구에서 소스맵을 보여주고 싶지 않을 때 사용하는 옵션이다.

- nosource
- 주로 사용자에게 내부 소스 코드를 보여 주고 싶지 않을 때 사용하는 옵션이다.


- cheap
- 라인 넘버만 매핑하고 라인에서 몇 번째 글자인지는 매핑하지 않는 옵션입니다
- 그래서 에러가 발생하면 몇 번째 라인에서 발생했는지 알 수 있지만 실제로 몇 번째 글자에서 발생했는지 알 수 없습니다.
- 빌드 속도와 리빌드 속도가 약간 향상된다.
요약

모드별로 추천하는 소스맵
Code Splitting
- 자바스크립트로 애플리케이션을 개발하게 되면 기본적으로 하나의 파일에 모든 로직이 들어가게 된다.
- 따라서, 프로젝트의 규모가 커질수록 자바스크립트 파일 용량도 커지는데 용량이 커지면 로딩 속도가 저하된다.
- 코드 스플리팅은 지금 당장 필요한 코드가 아니라면 따로 분리시켜 필요할 때 불러와 사용한다.
SPA로 구현된 react project에서 코드 스플리팅 하는법
- React에서는 컴포넌트들을 동적으로 불러오기 위해서
React.lazy를 사용하는데 이를 위해선 React.Suspense를 함께 이용해야 한다.
- 예시 코드를 보면 Suspense가 Routes를 감싼 걸 볼 수 있는데 이렇게 감싼 상태이면 컴포넌트를 동적으로 불러올 때 로딩 상태를 React.Suspense가 처리하게 된다.
const Home = lazy(() => import("@/pages/Home"));
<Suspense fallback={<div>로딩중..</div>}/>
<Routes>
<Routes path="|" element={<Home />} />
...
</Routes>
</Suspense>
- 다음은 React.lazy를 사용해서 코드 스플리팅한 번들 결과물로 큰 하나의 파일을 여러 개의 작은 모듈로 나눠진 걸 볼수 있다.

- 여기서 추가적으로 최적화할 수 있는 옵션으로 중복된 모듈을 처리하는 optimization.splitChunks 옵션이다.
- splitChunks옵션을 주면 중복된 모듈을 공통 모듈로 분리한다.
- default 설정으로 chunks가 node_modules에 존재하고 20kb보다 크고 로드할 때 최대 병렬의 요청 수가 30개 이하여야지만 splitChunks를 진행한다.
Tree Shaking
- 일반적으로 사용하지 않는 코드를 번들링 결과에 포함하지 않는 일이다.
- 함수 단위가 아닌 모듈 단위에서 필요하지 않거나 사용하지 않는 코드를 번들링 결과에 포함하지 않는 것을 의미한다.

- 이런 경우 tree shaking 이루어지면 c는 번들 결과에 포함되지 않게 된다.
💡 안 쓰는 코드는 그냥 지워버리면 되는거 아닌가요?
우리가 작성한 코드라면 폴더에서 파일을 삭제하면 되지만
- node처럼 서로간의 의존성이 높고
- 대규모 프로젝트라 코드를 지웠을 때 서로에게 어떤 영향이 있을지 모를 때는 함부로 코드를 지우는 것은 지양해야 한다.
- Library
라이브러리를 만드는 경우에는 tree shaking 개념을 알고 있어야 소비자들의 자원을 낭비하지 않게 만들 수 있습니다.
또 소비자들은 webpack을 쓸지 rollup을 쓸지, 또 사용하더라도 webpack의 최신 버전을 안쓸지도 모르는 일이기 때문입니다
- CommonJS
두 번째로는 CommonJs 방식으로 개발된 lodash같은 라이브러리를 사용할 때 더 조심할 수 있습니다
tree shaking 개념을 모르고 이런 옛날에 만들어진 라이브러리를 사용한다면 사용하지 않는 코드들도 다 같이 불러와질 수 있기 때문입니다
그래서 tree shaking은 어떻게 하는데..?
- tree shaking은 그냥 production mode를 켜면 알아서 잘 이루어진다.
persistent cache
- webpack 5에서 persistent cache라는 기능이 추가 되었는데 말 그대로 하드디스크에 빌드된 결과물을 저장해 놓는다는 것입니다
- 그리고 다시 re-build가 일어났을 때 파일이 변경되지 않았다면 하드디스크에 저장돼 있는 캐시 데이터를 재활용하고 만약에 변경이 되었다면 하드디스크에 있는 캐시 데이터를 무효화하는 일을 한다.

persistent cache 적용법
moduel.exports = merge(common, {
mode: "production"
devtools: "source-map",
plugins: [],
optimization: {},
cache: {
type: "filesystem",
buildDependencies: {
config: [__filename],
},
},
}),
- cache 옵션에 type을 "filesystem"으로 지정해주면 하드디스크를 사용한다는 의미이다.
💡 근데 이 좋은걸 왜 default로 안 씀?
캐시의 무효화 정책이 상황에 따라 다르게 적용돼야 하기 때문이다.