본문으로 건너뛰기

MSW 번들 사이즈 2배 이벤트

· 약 8분
arch-spatula

MSW 설정하면서 번들사이즈다 2배가 된 문제가 발생했습니다. 원래 보통 300kb 정도되는 번들 사이즈가 600정도로 커졌습니다.

원래 MSW는 개발하면서 백엔드를 mocking하는 것입니다. 그래서 개발하는 동안에만 있어야 하고 build에는 포함되면 안됩니다. 이 문제를 해결하는 글입니다.

참고로 일반적으로 권장하는 번들 사이즈는 500kb 미만입니다.

문제: 번들사이즈 2배 이벤트

MSW를 설정하면 번들사이즈가 2배가 됩니다.

main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { ThemeProvider } from '@emotion/react';
import theme from './styles/theme.ts';
import queryClient from './libs/queryClient.ts';
import { worker } from './mocks/worker.ts';

if (process.env.NODE_ENV === 'development') {
/**
* 실제 DB랑 통신을 확인하고 싶으면 아래 worker를 주석처리
* api가 안정화 되면 아래 설정 {onUnhandledRequest: 'bypass'}은 해제
* @see https://stackoverflow.com/questions/68024935/msw-logging-warnings-for-unhandled-supertest-requests
*/
worker.start({ onUnhandledRequest: 'bypass' });
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
<ReactQueryDevtools />
</QueryClientProvider>
</React.StrictMode>
);

개발하는 동안 MSW를 활성화하는 설정입니다.

문제
vite v4.3.9 building for production...
✓ 633 modules transformed.
dist/index.html 0.72 kB │ gzip: 0.39 kB
dist/assets/index-103ff5dd.js 0.08 kB │ gzip: 0.09 kB
dist/assets/PageHeading-858f23b9.js 0.13 kB │ gzip: 0.13 kB
dist/assets/index-8b0137af.js 0.32 kB │ gzip: 0.29 kB
dist/assets/index-87bbd3d1.js 0.65 kB │ gzip: 0.46 kB
dist/assets/index-32e984bf.js 0.91 kB │ gzip: 0.51 kB
dist/assets/index-d8a3b847.js 1.25 kB │ gzip: 0.67 kB
dist/assets/index-e552a5ba.js 1.29 kB │ gzip: 0.75 kB
dist/assets/index-7c7fe7b8.js 1.57 kB │ gzip: 0.99 kB
dist/assets/index-a90939a3.js 3.12 kB │ gzip: 1.42 kB
dist/assets/index-f613d10c.js 3.52 kB │ gzip: 1.69 kB
dist/assets/Input-8c62094b.js 4.51 kB │ gzip: 1.82 kB
dist/assets/react-error-boundary.esm-37156e7c.js 17.20 kB │ gzip: 5.85 kB
dist/assets/index-11b4e058.js 645.95 kB │ gzip: 201.37 kB

문제가 되는 번들 사이즈입니다.

main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { ThemeProvider } from '@emotion/react';
import theme from './styles/theme.ts';
import queryClient from './libs/queryClient.ts';
import { worker } from './mocks/worker.ts';

if (process.env.NODE_ENV === 'development') {
/**
* 실제 DB랑 통신을 확인하고 싶으면 아래 worker를 주석처리
* api가 안정화 되면 아래 설정 {onUnhandledRequest: 'bypass'}은 해제
* @see https://stackoverflow.com/questions/68024935/msw-logging-warnings-for-unhandled-supertest-requests
*/
worker.start({ onUnhandledRequest: 'bypass' });
}
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
<ReactQueryDevtools />
</QueryClientProvider>
</React.StrictMode>
);

개발하는 동안 MSW를 활성화하는 설정입니다.

원래
vite v4.3.9 building for production...
✓ 359 modules transformed.
dist/index.html 0.72 kB │ gzip: 0.39 kB
dist/assets/index-103ff5dd.js 0.08 kB │ gzip: 0.09 kB
dist/assets/PageHeading-2d3c7743.js 0.13 kB │ gzip: 0.13 kB
dist/assets/index-76e731ac.js 0.32 kB │ gzip: 0.29 kB
dist/assets/index-5977a381.js 0.65 kB │ gzip: 0.46 kB
dist/assets/index-5302ca3b.js 0.91 kB │ gzip: 0.51 kB
dist/assets/index-a077f0df.js 1.25 kB │ gzip: 0.67 kB
dist/assets/index-364e022a.js 1.29 kB │ gzip: 0.75 kB
dist/assets/index-9eabc667.js 1.57 kB │ gzip: 0.99 kB
dist/assets/index-19083c5b.js 3.12 kB │ gzip: 1.42 kB
dist/assets/index-c7499265.js 3.52 kB │ gzip: 1.69 kB
dist/assets/Input-85139c49.js 4.51 kB │ gzip: 1.82 kB
dist/assets/react-error-boundary.esm-5a8d437e.js 17.20 kB │ gzip: 5.85 kB
dist/assets/index-dcf1326b.js 305.53 kB │ gzip: 101.05 kB

시도: MSW Tree shaking을 위한 여정

검색: Tree shaking does not work in vite library mode

  • 별로 도움 안되었습니다.

rollup-plugin-visualizer 설치

직접 도움되는 것은 아니였지만 유용한 라이브러입니다. 일단 남겨두겠습니다.

rollup-plugin-visualizer

vite.config.ts
import { defineConfig, type PluginOption } from 'vite';
export default defineConfig({
plugins: [visualizer() as PluginOption],
});
vite.config.ts
/// <reference types="vitest" />
/// <reference types="vite/client" />

import react from '@vitejs/plugin-react-swc';
import { defineConfig, loadEnv, type PluginOption } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
import path from 'path';

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const { VITE_PORT } = loadEnv(mode, process.cwd(), '');

return {
plugins: [react(), visualizer() as PluginOption],
server: { port: parseInt(VITE_PORT) },
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/setup.ts'],
},
};
});

이렇게 설정했습니다. state.html이 출력됩니다. 파일 구경하면 번들 사이즈별로 볼 수 있습니다.

검색: Mock Service Worker Tree shaking

다시보면 이 방법이 해결에 제일 가까웠습니다.

Mock Service Worker로 만드는 모의 서버

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

// 개발 환경에서만 실행되도록 환경 변수를 확인하는 과정이 필요하다.
if (process.env.NODE_ENV === 'development') {
const { worker } = require('./mocks/worker');
worker.start();
}

ReactDOM.render(<App />, document.getElementById('root'));

검색: msw not being tree shaken from client bundle (dead code elimination bug)

package.json
{
// 생략
"browser": {
"./.msw": false
}
}

별로 도움 안되었습니다.

해결: 동적으로 import하기

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { ThemeProvider } from '@emotion/react';
import theme from './styles/theme.ts';
import queryClient from './libs/queryClient.ts';

/**
* 실제 DB랑 통신을 확인하고 싶으면 아래 worker를 주석처리
* api가 안정화 되면 아래 설정 {onUnhandledRequest: 'bypass'}은 해제
* @see https://stackoverflow.com/questions/68024935/msw-logging-warnings-for-unhandled-supertest-requests
* import는 비동기 함수이기 때문 async await와 즉시 실행함수가 필요함
*/
(async function () {
if (process.env.NODE_ENV === 'development') {
// tree shaking을 위해 await import 활용
const { worker } = await import('./mocks/worker.ts');
worker.start({ onUnhandledRequest: 'bypass' });
}
})();
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
<ReactQueryDevtools />
</QueryClientProvider>
</React.StrictMode>
);

당황스럽게 간단했습니다. 테스트는 성공적으로 동작하고 번들사이즈 2배 이벤트는 종료되었습니다.

즉시 실행함수에 async await도 적용해서 비동기로 동작하는 import로 worker를 가져옵니다.

해결
vite v4.3.9 building for production...
✓ 635 modules transformed.
dist/index.html 0.72 kB │ gzip: 0.39 kB
dist/assets/index-103ff5dd.js 0.08 kB │ gzip: 0.09 kB
dist/assets/PageHeading-d6ffa854.js 0.13 kB │ gzip: 0.13 kB
dist/assets/index-2e281901.js 0.32 kB │ gzip: 0.29 kB
dist/assets/index-21727d9f.js 0.65 kB │ gzip: 0.46 kB
dist/assets/index-f13856c9.js 0.91 kB │ gzip: 0.51 kB
dist/assets/index-327db79f.js 1.25 kB │ gzip: 0.67 kB
dist/assets/index-e0d49bcb.js 1.29 kB │ gzip: 0.75 kB
dist/assets/index-02c71055.js 1.60 kB │ gzip: 1.01 kB
dist/assets/index-e90ed05b.js 3.12 kB │ gzip: 1.42 kB
dist/assets/index-ecd6e71f.js 3.52 kB │ gzip: 1.69 kB
dist/assets/Input-a9c99b9d.js 4.51 kB │ gzip: 1.82 kB
dist/assets/react-error-boundary.esm-e555e35f.js 17.20 kB │ gzip: 5.85 kB
dist/assets/index-e38efc25.js 305.50 kB │ gzip: 101.04 kB

원래 보여야 하는 번들 사이즈입니다. 이거로 번들사이즈 2배 이벤트는 끝났습니다.

How to make your JavaScript Bundle Smaller

예전에 본 튜토리얼인데 다시봐도 유용합니다.

학습: lazy 이외 다른 tree shaking

오늘도 자바스크립트는 저를 실망시키지 않습니다.

vite에서는 top level await를 사용할 수 있지만 브라우저에서는 지원하지 않습니다.

(async function () {
if (process.env.NODE_ENV === 'development') {
const { worker } = await import('./mocks/worker.ts');
worker.start({ onUnhandledRequest: 'bypass' });
}
})();

이런 패턴으로 tree shaking합니다. 개발할 때만 실행하기 때문에 build할 때 무시합니다.

vite의 강력함 같습니다.