스택티콘은 많은 기능이 없기 때문에, 최대한 가벼운 앱을 만들어보고 싶었다. 그렇게 vite로의 마이그레이션을 결정하게 되었다..!
스택티콘은 react
, typescript
, yarn
을 이용해 만들어져있다. 따라서 빈 프로젝트에 아래와 같이 입력한다.
yarn start vite my-react-app --template react-ts
참고로 위의 react-ts 대신에 사용할 수 있는 템플릿 종류들은 아래와 같다.
vite 프로젝트를 생성한 뒤, 간단하게 라우터들과 랜딩페이지 정도만 가져와서 테스트를 해보려고 했다. 그런데 아래와 같은 오류가 발생했다.
import를 하지 못하는 것이다. 분명 경로는 제대로 돼있는데..?
이렇게 쓸 수 있었던 것은 기존 프로젝트에서 tsconfig에 baseUrl
을 설정해주었기 때문에 가능한 것이었고, vite에서는 path alias를 설정하기 위해선 vite.config.ts
파일에도 명시해주어야 한다.
폴더들에 대해 vite.config.ts 의 resolve.alias 설정을 모두 해 줄수도 있지만, 기존 tsconfig의 path 설정을 가져와서 사용할 수 있는 vite-tsconfig-paths 를 사용했다.
충분히 사용해도 괜찮다고 판단했다.
yarn add -D vite-tsconfig-paths
로 설치 후, vite.config.ts 파일에 아래와 같이 작성하면 된다.
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react(), tsconfigPaths()],
});
리액트에서 사진을 사용하는 방법에는 다양한 방법이 있지만, 기존의 CRA 프로젝트에서는 ES6 시스템에 따라 아래와 같이 사용하고 있었다.
import LandingImage from "../../assets/images/landing-image.png";
import LandingLabel from "../../assets/images/landing-label.png";
사진 파일들을 import 해서 사용하고 있었다면, 기존에 사진의 확장자에 맞는 모듈을 선언해줬을 것이다.
하지만 vite에는 이미지들에 대한 asset type들을 미리 선언해놓아서, 따로 설정해 줄 필요가 없다. node_modules > vite > client.d.ts
에 보면, 아래와 같이 선언되어 있다.
// Built-in asset types
// see `src/node/constants.ts`
// images
declare module '*.png' {
const src: string
export default src
}
declare module '*.jpg' {
const src: string
export default src
}
declare module '*.jpeg' {
const src: string
export default src
}
...
CRA 앱에서는 env의 내용을 아래와 같이 사용했다.
process.env.REACT_APP_ENVNAME
key 이름은 REACT_APP_
로 시작하는 형식이어야 했다.
vite에서는 아래와 같은 방식으로 사용해야 한다.
import.meta.env.VITE_SOME_KEY_NAME
key 이름은 VITE_
로 시작해야 하고, 가져올 때는 위와 같이 import.meta.env로 가져온다.
위 페이지는 UI가 로드되지 않은 경우의 로딩 처리가 되지 않아서 발생하는 이슈다. Suspense와 fallback과 같은 로딩 처리를 통해 해결 가능하다.
그러면 프로젝트를 다 옮겨온 뒤, 우선 얼마나 차이가 나는지 두 가지로 확인해보기로 했다.
1. 빌드 타임
2. 빌드 파일 크기
3. 로컬 컴파일 시간 (cold start)
속도는 2배보다 좀 더 빨라졌다.
빌드 파일 크기는 큰 차이가 없는 것으로 보인다. 이는 코드 스플리팅을 통해 개선될 수 있을 것으로 보인다.
yarn start
를 입력 후, 초기 화면이 렌더링 되기 까지의 대략적인 시간을 측정해봤다.
로컬 컴파일 시간은 굉장히 빨라졌다!
빌드 파일 사이즈를 위해 code splitting을 한다.
기존에 리액트에서 코드 스플리팅을 위해 사용하던 React.lazy를 사용한다.
const Page1 = lazy(() => import('pages/home'));
const Page2 = lazy(() => import('pages/result'));
아래 보이는 것처럼 코드 스플리팅은 되었지만, 효과는 크지 않은 것으로 보인다.
그 이유는 해당 페이지에서 사용된 모듈들이 아닌, 페이지 코드 자체만
스플리팅 되었기 때문이다. 페이지별로 사용된 모듈들까지 스플리팅을 함으로서 main 파일의 크기가 비대해지는 것을 막기 위해서, 번들러인 rollup
을 사용해서 코드를 스플리팅 하기로 결정했다.
vite는 rollup 관련된 세팅을 vite.config.ts
파일에 작성해주면 된다.
아래와 같이 작성했다.
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tsconfigPaths()],
build: {
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-router-dom', 'react-dom'],
...renderChunks(),
},
},
},
},
});
vendor를 설정해 준 뒤, 코드 내부에서 쓰이는 모듈들은 key: [key]
형태로 만들어 청크를 분리했다. (관련 rollup 설정)
그 결과 아래와 같이 코드 스플리팅의 효과를 볼 수 있었다.
simple-icons는 모듈 내부의 전체 아이콘 값을 가져다 쓰고 있어 더 이상 분리를 할 수 없었다. 하지만 기존 2MB
가 넘는 하나의 빌드 파일에서 가장 큰 사이즈의 파일이 약 1.8MB
이하로 10% 이상
청크 사이즈가 감소했다. simple-icons와 같은 전체를 모두 가져와야 하는 모듈이 없거나, 서비스의 사이즈가 더 비대해진다면 훨씬 더 많이 체감될 것 같다.
이제 본격적으로 마이그레이션을 해보자.
우선 CRA를 제거해보자.
yarn remove react-scripts
그 후 vite를 설치하자.
yarn add -D vite @vitejs/plugin-react vite-tsconfig-paths
cf) vite-tsconfig-paths
위에서도 언급했지만, 기존 tsconfig에 있는 설정 파일을 그대로 가져다 쓸 수 있게 해준다.
원래 vite.config.ts 파일에 설정을 해줘도 되지만, 그냥 이거 설치만 하면 해결되니 맘편히 설치하자. vite core 팀 멤버가 메인테이너로 있으니 그냥 쓰자.
package.json과 같은 레벨에 vite.config.ts
파일을 생성한다. 그 후 위에서 생성한 코드를 작성한다.
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
const renderChunks = (deps: Record<string, string>) => {
const chunks = {};
Object.keys(deps).forEach((key) => {
if (['react', 'react-router-dom', 'react-dom', 'pretendard'].includes(key)) {
return;
}
chunks[key] = [key];
});
return chunks;
};
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), tsconfigPaths()],
build: {
sourcemap: false,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-router-dom', 'react-dom'],
...renderChunks(dependencies),
},
},
},
},
});
public에서 root 경로로 이동한다.
또한 기존에 존재하던 %PUBLIC_URL%
은 vite에서는 참조할 수 없으므로, 제거해준다.
마지막으로 entry point를 지정해주기 위해서, body 안에 아래 내용을 추가한다.
<body>
...
<script type="module" src="/src/index.tsx"></script>
</body>
src 폴더에 해당 파일을 생성한다.
기존에 CRA는, 빌드 결과물이 ./build
위치에 생성된다. 하지만 vite는 (별다른 설정을 하지 않았다면) 빌드된 결과물이 ./dist
위치에 생성된다.
따라서 ./dist 위치의 파일들을 배포해주고, 만약 Github Actions 와 같이 배포 자동화를 위한 파이프라인이 세팅 되어 있다면 target path를 변경해주어야 한다.
이 외에는 위에서 언급한 대로 에러를 하나씩 수정해나가면 된다!