CRA를 떠나 vite로

HR·2023년 4월 15일
4

스택티콘

목록 보기
3/5
post-thumbnail
post-custom-banner

스택티콘은 많은 기능이 없기 때문에, 최대한 가벼운 앱을 만들어보고 싶었다. 그렇게 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 를 사용했다.

  1. 최근까지 업데이트가 되고 있으며,
  2. vite 팀 소속의 개발자가 관리하고 있으므로

충분히 사용해도 괜찮다고 판단했다.

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()],
});

이미지 import

리액트에서 사진을 사용하는 방법에는 다양한 방법이 있지만, 기존의 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
}
...

env 사용

CRA 앱에서는 env의 내용을 아래와 같이 사용했다.

process.env.REACT_APP_ENVNAME

key 이름은 REACT_APP_ 로 시작하는 형식이어야 했다.
vite에서는 아래와 같은 방식으로 사용해야 한다.

import.meta.env.VITE_SOME_KEY_NAME

key 이름은 VITE_로 시작해야 하고, 가져올 때는 위와 같이 import.meta.env로 가져온다.

CRA에서는 마주하지 못한 에러

위 페이지는 UI가 로드되지 않은 경우의 로딩 처리가 되지 않아서 발생하는 이슈다. Suspense와 fallback과 같은 로딩 처리를 통해 해결 가능하다.

CRA vs vite 비교

그러면 프로젝트를 다 옮겨온 뒤, 우선 얼마나 차이가 나는지 두 가지로 확인해보기로 했다.

1. 빌드 타임
2. 빌드 파일 크기
3. 로컬 컴파일 시간 (cold start)

1) 빌드 타임 - CRA

1) 빌드 타임 - vite

속도는 2배보다 좀 더 빨라졌다.

2) 빌드 파일 크기 - CRA

2) 빌드 파일 크기 - vite

빌드 파일 크기는 큰 차이가 없는 것으로 보인다. 이는 코드 스플리팅을 통해 개선될 수 있을 것으로 보인다.

3) 로컬 컴파일 시간 - CRA

yarn start를 입력 후, 초기 화면이 렌더링 되기 까지의 대략적인 시간을 측정해봤다.

3) 로컬 컴파일 시간 - vite

로컬 컴파일 시간은 굉장히 빨라졌다!

code splitting

빌드 파일 사이즈를 위해 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와 같은 전체를 모두 가져와야 하는 모듈이 없거나, 서비스의 사이즈가 더 비대해진다면 훨씬 더 많이 체감될 것 같다.

마이그레이션을 해보자

이제 본격적으로 마이그레이션을 해보자.

1. CRA 제거, vite 설치

우선 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 팀 멤버가 메인테이너로 있으니 그냥 쓰자.

2. vite.config.ts 파일

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),
        },
      },
    },
  },
});

3. index.ts 파일 위치 변경

public에서 root 경로로 이동한다.
또한 기존에 존재하던 %PUBLIC_URL%은 vite에서는 참조할 수 없으므로, 제거해준다.

마지막으로 entry point를 지정해주기 위해서, body 안에 아래 내용을 추가한다.

<body>
  	...
    <script type="module" src="/src/index.tsx"></script>
</body>

4. vite-env.d.ts

src 폴더에 해당 파일을 생성한다.

5. 배포 디렉토리 변경

기존에 CRA는, 빌드 결과물이 ./build 위치에 생성된다. 하지만 vite는 (별다른 설정을 하지 않았다면) 빌드된 결과물이 ./dist 위치에 생성된다.

따라서 ./dist 위치의 파일들을 배포해주고, 만약 Github Actions 와 같이 배포 자동화를 위한 파이프라인이 세팅 되어 있다면 target path를 변경해주어야 한다.

이 외에는 위에서 언급한 대로 에러를 하나씩 수정해나가면 된다!

post-custom-banner

0개의 댓글