vue-cli 프로젝트를 vite 3으로 마이그레이션해보기

kdeun1·2022년 7월 13일
8
post-thumbnail

이 글은 vue-cli(vue3, typescript, composition API)로 구성되어 webpack5 환경의 프로젝트를 vite 3버전 환경으로 마이그레이션하는데 구글링하고 직접 헤딩하여 완성되기까지의 경험을 기록한 내용이다.

BE 서버 환경이 불안정한 경우 서버를 재시작해야하는 경우가 빈번한 경우 환경에 따라 API 서버가 자주 변경되는 경우에는 FE환경에서 개발하는데 약간의(?) 불편함을 겪곤한다.
이 부분을 해결하기 위해 .env 파일로 여러 환경을 커버하거나 여러 서버를 띄우는 등의 임시 방편으로 처리해왔다. 하지만 HMR로 커버가 안되는 부분이 존재하였으며, 프로젝트가 비대해짐에 따라 빌드 속도가 점점 느려지게 되었다.
이러한 문제점이 지속되면서 동료개발자들의 불만 제기도 있었으며, 느린 CI 속도와 효율적이지 않는 FE 환경 구성, 느린 빌드 속도 등 다양한 부분들을 개선하기 위해서 마이그레이션을 하게되었다.

빌드 시간 비교

webpack 5(vue-cli) development build의 cold start 시간

stylelint, lintOnStart 옵션이 켜져있는 경우

약 130,000 ~ 140,000 ms (대략 2분 넘음)

stylelint가 없는 경우

약 110,000 ~ 120,000 ms (2분 언저리)

webpack 5(vue-cli) development build의 HMR 시간

약 2,000 ~ 3,000 ms

Vite의 development build cold의 start 시간

stylelint, lintOnStart 옵션이 켜져있는 경우

약 41,000 ms

stylelint가 없는 경우

약 4,000 ms

Vite의 HMR 시간

콘솔에 출력되지는 않지만 곧바로 반영

결과적으로 stylelint(lintOnStart)가 없는 환경에서의 개발용 빌드 시간은 약 27.5 ~ 30배 더 빠른 효과를 보였다.


1. vite 3 설치

0) 변경점 정리

vite2에서 3으로 크게 메이저버전이 업데이트되었다. vite3으로 마이그레이션하기 전에 변경점을 간단하게 정리해보자. 영어를 발번역한 글이므로 참고만 하면 될 것 같다.

Vite CLI

  • default dev server port는 5173이며, preview server port는 4173이다.
  • default dev server의 host는 127.0.0.1이 아니라 localhost이다.

호환성

  • Vite는 EOL에 도달한 Node v12를 더이상 지원하지 않고, Node 14.18 이상이 필요하다.
  • 호환성을 위해 ESM entry에 대한 CJS proxy와 함께 ESM으로 배포된다.
  • 모던 브라우저 베이스라인은 native ES Modules과 native ESM의 동적 import와 import.meta를 지원하는 브라우저를 대상으로 한다.
  • SSR과 lib모드의 JS 파일 확장자는 이제 형식과 패키지 타입에 따라 output JS entry와 chunk에 유효한 extension(js, mjs, cjs)을 사용한다.

아키텍쳐

  • Vite는 이제 초기에 정적으로 import한 모듈들을 크롤링하는 동안에 plugins이 import할 때, cold start하는 동안의 full reload를 방지한다.
  • 기본적으로 SSR 빌드에 ESM을 사용하며, 이전의 SSR 외부화 휴리스틱을 필요하지 않게 되었다.

기타

  • import.meta.glob이 개선되었다.
  • WebAssembly import API는 향후 표준과의 충돌을 피하기 위해 수정되었다
  • relative base에 대한 지원이 향상되었다.

실험 기능

  • Build Advanced Base Options
  • HMR Partial Accept
  • Vite now allows the use of esbuild to optimize dependencies during build time avoiding the need of @rollupjs/plugin-commonjs, removing one of the difference id dependency handling between dev and prod.

번들 사이즈 감소

  • Terser가 이제 선택적인 디펜던시가 되었다. build.minify: 'terser'를 사용하기 위해서 terser를 설치해야한다. (npm add -D terser)
  • node-forge는 monorepo에서 @vitejs/plugin-basic-ssl로 이동되었다.
  • 업데이트 하기 전에 v2에서 마이그레이션 가이드를 확인하세요.

참고 : https://github.com/vitejs/vite/blob/v3.0.0/packages/vite/CHANGELOG.md#300-2022-07-13

1) 지원 Node 버전 확인

Vite는 EOL(End-of-life)에 도달한 Node v12를 더이상 지원하지 않는다. Node는 14.18+ 버전을 사용해야한다. node v14의 마지막 버전은 14.19.3 이므로 이 버전을 사용하기로 생각했다. 기존 프로젝트들이 node v12를 사용하는 경우도 존재하므로, nvm을 사용하여 다중 노드 환경을 구성해준다.

2) 최신 브라우저 기준 변경

프로덕션 버전으로 빌드 및 번들링 시 소스코드가 최신 JS를 지원하는 환경에서 동작한다고 가정하고 진행된다. 기본적으로 Vite는 Native ES Module, Native ESM의 동적 Import, import.meta를 지원하는 브라우저를 대상으로 하고 있다.

Chrome >= 87
Firefox >= 78
Safari >= 13
Edge >= 88

3) 설치

생성 명령어 사용

npm create vite@latest "프로젝트명"

Scaffolding Project

  • Project Name
  • Select a framework : vanilla, vue, react, preact, lit, svelte
  • Select a variant : vue, vue-ts

framework에 lit, svelte도 추가되었다.

4) 실행

cd "프로젝트명"
npm install
npm run dev

정상적으로 프로젝트가 돌아가는 것을 확인한다. 이 때, 디펜던시에 있는 라이브러리들의 버전을 최신화하였다.

vite 3으로 업데이트되면서 기본 port가 3000에서 5173으로 변경되었다.
scripts.dev 명령어의 값을 "vite"에서 "vite --host"로 변경해주자. 이는 Local뿐만 아니라 Network방식을 허용하여 접속할 수 있게 변경하기 위함이다.


2. 마이그레이션 시작

0) 프로젝트 폴더, 파일들 복사

우선 폴더를 그대로 복사 붙여넣기한다. src/App.vue, src/main.ts 파일 내용도 복붙한다.
이미 vite로 마이그레이션할 계획으로 프로젝트의 폴더구조를 개선하였다. vite 환경의 프로젝트를 생성한 후에 기본적인 폴더 구조를 파악하고나서 기존 프로젝트의 폴더 구조와 비교/분석 후에 복붙할 때 사이드이펙트가 발생하지 않도록 스무스하게 진행될 수 있도록 개선하였다.
결과적으로는 거의(?) 문제가 발생하지 않았다. index.html이나 favicon같은 문제가 약간 있었지만 아래에 그 부분에 대해 작성하였다.

1) package.json의 dependency 변경

기존 프로젝트의 디펜던시들을 복사해준다. 그 뒤 vite에 맞게 package.json 파일에 필요한 라이브러리를 변경한다. 필요한 라이브러리는 업데이트를 진행하였다.

1-1) vue-cli에서 사용되었던 디펜던시를 삭제

  • vue-cli 전용 디펜던시를 삭제하였다.
// package.json
"@vue/cli-plugin-babel": "~0.0.0",
"@vue/cli-plugin-eslint": "~0.0.0",
"@vue/cli-plugin-router": "~0.0.0",
"@vue/cli-plugin-vuex": "~0.0.0",
"@vue/cli-service": "~0.0.0",
  • sass-loader 삭제하였다. 나중에 이야기하겠지만, saas 패키지만 설치하면 해결된다.
// package.json
"sass-loader": "^0.0.0"

1-2) 디펜던시 최신화

  • vite로 설치된 vue 버전은 고정되어있음. vue 버전을 최신화한다.

1-3) 모던 브라우저 지원으로 인한 디펜던시 수정

  • vue3버전을 사용하고 있으며, IE11 지원중단에 따라 프로젝트의 지원브라우저를 크롬으로 한정하는 스펙으로 정하였다.
  • Vite는 차세대 빌드 도구이므로 비교적 최신 브라우저만 지원한다. 브라우저 기준을 위에 적어놓았다. 대부분의 최신 브라우저에서는 ES6를 거의 지원하므로 Babel을 제거해도 된다.
  • 트랜스파일링 관련 바벨 디펜던시인 babel-eslint, 폴리필 관련 디펜던시인 core-js 삭제하였다.
  • babel.config.js 파일 삭제, babel-eslint 삭제(eslint에서의 babel-eslint 옵션 코드도 삭제), @vue/cli-plugin-babel 디펜던시도 제거한다.

1-3-1) package.json 파일 내 module 속성 추가됨

// package.json
{
  "name": "프로젝트명",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --host",
    "build": "vue-tsc --noEmit && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    // ...
  },
  // ...
}
  • "type": "module"이 기본적으로 추가되었다.

1-3-2) scripts 수정

  • 외부에서 지정된 ip로 접근할 수 있도록 npm run dev 스크립트에 --host 옵션도 추가해주었다.

2) main.ts 파일 내 path 에러

처음에 파일을 복붙하고 나서 path url 부분에 빨간줄이 그어지며 resolve 관련한 알림이 나타난다. alias의 문제인데 이는 아래의 방식으로 해결할 수 있다.

2-1) alias '@' 관련 문제

2-1-1) (Javascript 관련)'@'와 같은 alias를 vite.config.js 파일에 세팅해준다.

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  resolve: {
    alias: [
      { find: '@', replacement: '/src' },
    ]
  },
  plugins: [vue()]
})

구글링하면 path를 사용해 path.resolve로 alias를 추가하는 방법이 많이 나온다. 하지만 기본적으로 위 코드처럼 alias를 지원해주기 때문에 불필요하게 path 라이브러리가 없어도 된다.

2-1-2) (Typescript 관련) IDE(webstorm)에서 alias를 인식해주기 위해 tsconfig.json 파일에 다음과 같이 추가한다.

baseUrl, paths 옵션을 추가한다.

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ],
    }
  },
  // ...
}

2-2) TypeScript로 정의하지 않은 Javascript용 라이브러리를 사용할 때 발생하는 에러

  • @types/... 관련 라이브러리를 설치한다. 하지만 TS를 지원하지 않는다면 다음과 같이 해결한다.
  • // @ts-ignore 주석 사용하기. 이 방법도 임시방편인 것 같다.
  • d.ts 파일로 직접 모듈화하기. (Vite2에서는 env.d.ts파일, Vite3에서는 vite-env.d.ts파일)
    vite 프로젝트를 생성하면 vite-env.d.ts 파일이 존재한다. 다음과 같이 라이브러리명을 선언해준다.
// vite-env.d.ts
declare module '라이브러리명';

vite-env.d.ts 파일에 선언하는 방식으로 진행하였다. @ts-ignore 주석은 꼼수와 같은 방법이라고 생각되었기 때문이다.

3) vite 환경 변경

3-1) 환경 변수와 모드 변경

우선 vite에서도 .env 파일의 명명규칙이 유지된다. 파일을 복붙하였을 때 파일명은 그대로 사용하면 된다. 하지만 파일 내 변수명은 변경이 필요하다.

vue3 환경에서는 VUE_APP_* 접두사를 사용해서 환경변수를 선언하는데, VITE_* 로 변경되었으며 그에 따라 환경변수명도 변경한다.

vue-cli에서 사용되었던 process.env.ㅁㅁㅁ 형태의 환경변수를 변경해야한다. vite의 환경 변수는 import.meta.env 객체를 이용해 접근하도록 되어있다.

대표적인 환경변수를 다음과 같이 변경한다. 프로젝트 전체 검색을 통해 변경한다.

  • process.env.BASE_URL -> import.meta.env.BASE_URL 로 변경
  • process.env.NODE_ENV -> import.meta.env.MODE 로 변경
  • import.meta.env.PROD 추가(boolean)
  • import.meta.env.DEV 추가(boolean)
  • import.meta.env.SSR 추가(boolean)
  • 참고 : https://vitejs.dev/guide/env-and-mode.html#env-variables

.env 파일의 내용을 다음과 같이 변경한다.

  • VUE_APP_BASE_URL -> VITE_BASE_URL 로 변경

3-1-1) index.html 파일 위치가 변경

위에서 언급한대로 파일을 복붙하는데 경로가 달라진 파일은 index.html이다. public 폴더 내부에 있는 index.html 파일의 위치가 프로젝트로 루트로 변경되었다.
추가 번들링 없이 index.html 파일이 앱의 진입점이 되게끔 의도적으로 변경되었기 때문이다. vite는 개발모드 시 esbuild를 사용하기 때문에 <script type="module" src="/src/main.ts"></script> 코드가 html 파일의 body 하단에 위치하게 된다.

3-1-2) favicon이 없어짐

public/favicon.icon이 public/vite.svg로 대체되었다. 그리고 index.html 파일에서 favicon을 사용하는 코드를 변경해야한다.

<link rel="icon" href="<%= BASE_URL %>favicon.icon">

이렇게 BASE_URL을 사용하는데, 이 부분은 변경이 필요하다. index.html이 번들링하지 않으므로 일반적으로 사용하는 방식처럼 코드를 변경해주자.

<link rel="icon" type="image/svg+xml" href="/favicon.svg" />

3-2) 생략 확장자 관련

vite.config.js 파일의 resolve.extensions에 기본값은 ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json']이다. .vue 확장자는 vue3에서도 생략하지 않도록 되어있으며, 기본 값을 그대로 유지해준다. 추후 필요한 확장자가 있는 경우 vite.config.js에 추가해준다.

3-3) 웹팩 청크 관련 sytax sugar 코드(webpackChunkName) 제거

webpack 환경이 아니기 때문에 vue-router에서 사용되는 dynamic import의 webpackChunkName 코드를 제거한다. 프로젝트의 메뉴가 많기 때문에 router 파일도 모두 모듈화하였으며, 제거할 코드들을 일일히 살펴보며 오타가 없는지 잘 지워졌는지 신경쓰면서 코드를 삭제하였다.

4) sass 문제 수정

4-1) ~ 제거(tilde 제거)

vite는 모던 브라우저만을 타깃으로 하기 때문에, 표준을 준수하도록 native css를 사용하도록 권고된다. 다시말해 CSS Pre-processors를 기본적으로 지원하지 않는다는 뜻이다. 다만 필요한 경우에는 어렵지 않게 설치해서 바로 사용할 수 있다.

# .scss and .sass
npm add -D sass

# .less
npm add -D less

# .styl and .stylus
npm add -D stylus

Vue SFC를 사용하는 경우 설치후 별다른 설정없이 <style lang="sass">와 같은 css 전처리기를 바로 사용할 수 있다. Sass나 Less에서의 @import aliasurl()도 사용가능하다.

위 이미지는 webpack의 sass-loader 공식문서 내용에 일부이다. ~를 사용하는 것은 deprecated되었으며, 코드에서 제거하는 것을 권장한다. 이미 구식 코드에서 사용 중이기 때문에 역사적인 이유(?)와 불필요한 사이드이펙트 발생을 막기때문에 호환성을 지원한다.

sass-loader의 change log중 일부이다. 11.0.0 버전이 되면서 위와 같은 내용이 적용된다.

프로젝트의 alias 중 가장 유명한 별칭은 at(@)이다. webpack 설정으로 @을 src 폴더를 가리키게 세팅해준다. tilde를 사용하는 ~@는 sass 구문에서 alias를 사용하기 위한 prefix이다. ~는 sass의 기능이 아니고 sass-loader에서 구현되는 기능이다. 그러므로 sass-loader의 문법을 수정해야한다. vite에는 sass의 package만 필요할 뿐 loader는 필요없다.

해결책
~@/src로 치환하는 alias로 설정하여 scss파일 내 url(~@/...) 코드를 정상적으로 읽을 수 있게 되었다.

4-1-1) sass의 alias 세팅

~@에서 ~를 제거하는 방식으로 진행하려고 했다. 하지만 프로젝트 코드 중 모든 ~@를 제거하기에 너무 노가다스럽다는 생각을 하였다. 혹시 ~@를 sass의 alias로 세팅하면 *.scss 파일 내 코드를 수정없이 사용할 수 있지 않을까 생각하여 검색하게 되었다. vite > github > issue에서 검색한 내용을 힌트로 삼아 다음과 같은 코드로 해결하게 되었다.

export default defineConfig({
  resolve: {
    alias: [
      { find: '@', replacement: '/src' },
      { find: /^~@/, replacement: '/src' }
    ]
  },
  // ...
})

물론 프로젝트 내 ~@를 @로 치환하는 방법이 가장 빠르고 단순하다. @로 변경하는 방법을 추천한다. 위 방식은 sass에서 사용되는 alias를 추가하기 위해서 조사한 내용이다.

4-2) sass, css.preprocessorOptions 옵션

위 과정까지 적용하면 vite 서버는 정상적으로 작동한다. 하지만 실제 localhost로 접속해보면 sass의 문제가 발생한다. 뭔가 sass의 전역 변수가 말썽을 부린 것 같다. 환경 설정이 잘못 설정된 것일까?

Undefined variable.                                                           
   ╷                                                                          
53 │     border-radius: $border-radius-base;                                  
   │                    ^^^^^^^^^^^^^^^^^^^                                   
   ╵    

vite.config.ts에 css.preprocessorOptions를 추가해보자. (참고 : https://vitejs.dev/config/#css-preprocessoroptions).

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  resolve: {
    alias: [
      { find: '@', replacement: '/src' },
    ]
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/styles/main.scss";'
      }
    }
  },
  plugins: [vue()]
})

위의 방식대로 css.preprocessorOptions에 scss 경로를 걸어줬다. 하지만 또다른 문제가 발생하고 말았다.

4-2-1) dev 환경에서 여러 개의 stylesheet 복사본이 중복되는 현상 발생

위와 같이 scss 옵션을 세팅하고나서 dev 환경에서 style에 대한 문제가 발생한다. 같은 스타일이 반복되게 중복되는 현상이 생기는데, 이 문제는 vite github > issue(https://github.com/vitejs/vite/issues/4448, https://github.com/vitejs/vite/issues/7504)에 올라와있는 이슈이다. 프로젝트 내 scss 파일에 대한 이해가 필요하다. 우선 vite.config.ts 파일의 css.preprocessorOptions.scss.additionalData에 대해 알아보자.

additionalData 옵션 설명

additionalData 옵션은 다음과 같이 정의할 수 있다. (https://github.com/vitejs/vite/issues/4448#issuecomment-1110997673)

vite는 각각의 sass, scss, css import를 webpack과 동일한 방식으로 개별적으로 처리한다. 프로젝트 내 js, ts파일에서 import된 모든 sass, scss, css 파일 앞에 additionalData가 추가된다는 뜻이다.

현재 프로젝트의 scss 구조

위 문제를 해결하기 위해 우선 프로젝트의 scss구조를 알아보자. util 폴더에 있는 부분은 scss의 공통 변수와 함수가 들어있다.

scss 구조를 참고하여 설정을 변경

그러므로 모든 스타일 앞에 추가하는 역할을 하는 scss.additionalData 옵션에는 saas의 공통 variables(변수들), mixins(함수들)에 해당하는 util성 scss파일을 등록한다.

// vite.config.ts
export default defineConfig({
  // ...
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: '@import "@/styles/utils/index";'
      }
    }
  },
  // ...

그리고 나머지 공통 스타일은 main.ts파일에 추가한다.

// main.ts
import { createApp } from 'vue';
import App from '@/App.vue';
import router from '@/router';
import { store, key } from '@/store';
import '@/styles/main.scss';

const app = createApp(App);
// ...
app.mount('#app');

vite.config.ts의 additionalData 옵션과 main.ts에 공통 스타일 import를 통해 scss가 정상적으로 반영되는 것을 볼 수 있다.

vite 3버전의 기본 프로젝트 구조의 main.ts는 다음과 같다.

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

프로젝트 스타일 파일은 main.ts에 추가해준다. sass의 유틸성 변수, 함수는 vite.config.ts파일의 css.preprocessorOptions.scss.additionalData에 추가해준다.

5) proxy 설정

vite 환경에 Restful API를 위한 proxy 설정을 해준다. vite.config.ts 파일에 server.proxy의 값을 추가해준다.

export default defineConfig({
  // ...
  server: {
    proxy: {
      '/api/v1': {
        target: 'http://0000.0000.000/',
      },
      '/api/v2': {
        target: 'http://0000.0000.000/',
      },
    },
  },
  // ...
})

vite의 proxy 설정은 webpack의 proxy 설정과 거의 같다. 다만 다른점은 속성명이 devServerserver로 변경된 것 밖에 없다.

target의 값을 문자열이 아닌 환경 변수로 설정하는 방법은 다음과 같다.
"@types/node" 패키지 설치도 필요하다.

5-1) proxy의 target을 환경변수로 변경해보자.

위와 같은 방식은 proxy.URL.target의 값을 문자열로만 세팅할 수밖에 없다. 하지만 vite.config.ts파일 안에서 .env파일로부터 환경 변수 값을 꺼내올 수 없다. 또한 ImportMeta interface에는 env가 존재하지 않아 import.meta.env도 사용할 수 없다.

동적 프록시 설정을 위해 위 이미지처럼 세팅을 해준다. process.env를 사용하기 위해서는 @types/node 패키지도 필요하다. 설치해주도록 하자.


3. Lint

1) ESLint

ESLint와 ESLint를 위한 vue plugin을 설치한다.

npm i --D eslint eslint-plugin-vue

그리고 ESLint 설정을 위한 파일을 생성한다. 무의식적으로 .eslintrc.js 파일을 만들어서 아래와 같이 추가하였다.

// .eslintrc.js
module.exports = {
  env: {
    node: true,
  },
  extends: [
    'plugin:vue/vue3-recommended',
  ],
  rules: {
    // override/add rules settings here, such as:
    // 'vue/no-unused-vars': 'error'
  }
}

이상하게도 위와 같은 에러가 발생한다. package.json 파일 내 "type": "module", 속성이 추가된 것을 기억하는가? eslint 설정 파일의 확장자를 변경해야한다.

vue-cli 프로젝트에서 존재했던 .eslintrc.js 파일의 확장자를 .eslintrc.cjs 로 변경해준다.
그리고 IDE(webstorm)의 auto fix를 위한 eslint configuration files의 파일 path도 변경된 확장자 파일에 맞게 변경해준다. 그리고 기존의 eslint의 룰을 추가해준다.

@vue/eslint-config-typescript
이 config는 @vue/cli, create-vue 설정에서 사용하도록 특별히 설계되었으며, 외부 사용을 위한 것이 아니다. 설치되어있다면 지워주도록 하자.

1-1) eslint-config-airbnb 설치

eslint를 기본적으로 airbnb 룰에 따라 개발하되, rules에 추가적인 규칙을 추가해주는 방향으로 설정하려고 한다.

2) TypeScript-ESLint

Typescript를 위해서 다음과 같은 플러그인/파서를 설치해준다.

npm i --D @typescript-eslint/eslint-plugin @typescript-eslint/parser

@typescript-eslint/eslint-plugin : Typescript 고유의 규칙을 포함하는 플러그인
@typescript-eslint/parser : ESLint가 Typescript 코드를 린트하도록 허용하는 파서

2-1) type-only import, export 세팅

TypeScript 3.8부터 type-only imports, exports를 위한 새로운 문법이 추가되었다. type을 export할 때 다음과 같이 사용해야한다.

import type { SampleType } from './sampleModule.ts';
export type { SampleType };

type을 import/export하는 모든 코드에 type을 일일히 다 붙여줄 수가 없기 때문에, type-only 옵션을 수정해준다.

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "isolatedModules": false, // true가 기본 값이지만 false로 변경해준다.
    // ...
  }
}

시간이 된다면 type을 따로 나누고 isolatedModules옵션을 true로 변경해야할 것이다.

2-2) tsconfig.json 파일 세팅

위의 옵션 외에도 추가적으로 다른 옵션들을 추가해준다.

// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "noImplicitAny": false,
    "isolatedModules": false,
    "esModuleInterop": true,
    "lib": ["ESNext", "DOM"],
    "skipLibCheck": true,
    "baseUrl": ".",
    "paths": {
      "@/*": [
        "src/*"
      ],
    }
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

3) .eslintrc.cjs

eslint의 몇몇 주요 설정들 정리

  • settings : 모든 rules에서 공유되어야 하는 정보를 지정한다. 실행 중인 모든 rules에 제공된다. 커스텀 규칙을 추가하고 동일한 정보에 접근하기 쉽게 구성할 수 있도록 하는 경우 유용하다.
  • parser : ESLint에서 사용할 파서를 지정한다. 기본 값은 espree가 사용된다.
    babel과 함께 사용되는 파서는 babel-eslint가 있고, typescript 구문 분석을 위해서는 @typescript-eslint/parser가 있다.
  • parserOptions : 파서옵션을 통해 JS 언어 옵션을 지정할 수 있다. JSX나 다른 JS버전에 대해 지원할 수 있도록 재정의가 가능하다.

    env 옵션은 전역 변수를 위한 것이고, parserOptions는 syntax를 위한 것이다.

vite 환경의 .eslintrc.cjs 내용
eslint-plugin-vue 패키지의 공식문서의 유저가이드의 내용은 다음과 같다.

vue-eslint-parser를 parser 옵션에 사용하고, @typescript-eslint/parser를 parserOptions.parser에 사용한다.
그 후에 vue-cli 환경의 eslintrc.js 파일의 rules를 vite 환경의 .eslintrc.cjs 파일의 rules로 옮겨준다.

// .eslintrc.cjs
module.exports = {
  root: true,
  env: {
    node: true,
    jest: true,
    'vue/setup-compiler-macros': true, // API와 같은 defineProps 호환성을 위함
  },
  extends: [
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'eslint-config-airbnb', // 위에서 설치한 패키지
  ],
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 2020,
    parser: '@typescript-eslint/parser',
    sourceType: 'module'
  },
  rules: {
    // 룰을 추가해준다.
  }
}

4) IDE(webstorm)의 도움으로 eslint의 auto fix 옵션 (Run eslint --fix on save 옵션)을 설정하는 경우

webstorm에서 eslint 규칙을 읽어 auto fix 하는 기능을 위해 IDE 세팅을 적용한다.

.eslintrc.cjs 파일을 설정파일로 추가한다. 하지만 다음과 같은 문제가 발생한다.

eslint에서 Typescript 절대경로 Import에 '@'를 인식하지 못하는 문제가 발생한다.

IDE의 auto fix 옵션을 설정하기 전까지는 typescript 파일 경로에 alias '@'가 들어있는 경우 문제가 발생하지 않았다. 하지만 옵션을 켜는 경우 경로를 Ctrl+Click하여 파일을 찾아갈 수는 있지만, 다음과 같은 에러가 발생한다.

ESLint: Missing file extension for "@/utils/types"(import/extensions)

이 문제를 해결하기 위해서는 eslint-import-resolver-typescript 패키지 설치가 필요하다.

npm i -D eslint-import-resolver-typescript

이렇게 설치한 후에 .eslintrc.* 파일에 다음과 같이 추가한다.

// .eslintrc.cjs
module.exports = {
  // ...
  "settings": {
    "import/resolver": {
      "typescript": {}
    }
  }
  // ...
}

위처럼 세팅하면 코드를 작성할 때, eslintrc에 세팅한 룰대로 prettier처럼 auto fix가 설정된다.


4. StyleLint

1) styleLint 사용 여부

기존 프로젝트에서 stylelint를 사용하고 있으며, 특히 css attr의 ordering과 코드의 3depth 초과를 방지하기 위해서 사용 중이다.
사용 여부에 대해 의견이 분분하지만 이미 사용 중이기 때문에 상태를 유지하기 위해서 vite만의 stylelint plugin을 설치해준다.

npm install -D stylelint vite-plugin-stylelint

그리고 기존에 사용했던 stylelint.config.js의 확장자를 .cjs로 변경한다. 이는 package.json의 "type": "module" 옵션으로 인한 설정이다.

이제는 vite.config.jts 파일에서 StylelintPlugin을 추가해준다.

// vite.config.ts
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import StylelintPlugin from 'vite-plugin-stylelint';

// ...
    plugins: [
      vue(),
      StylelintPlugin({
        include: ['src/**/*.{vue,scss}'],
        exclude: ['node_modules'],
        lintOnStart: true,
        emitErrorAsWarning: true,
      }),
    ],
  });
};

webpack에서 사용했던 StyleLintPlugin의 파라미터의 속성이 달라졌다. 위에서 참고로 걸어놓은 깃허브 페이지의 문서를 읽어보면서 사용할만한 옵션을 설정해보자.
직접 적은 코드는 lint가 잘못된 경우 error가 아닌 warning이 발생되도록 한 옵션이다. 또한 npm run dev 개발 서버가 시작될 때 stylelint가 한 번 돌도록 옵션을 설정하였다.

vite에서 lintOnStart를 켠 경우
[vite] warning: Stylelint is linting all files in the project because lintOnStart is true. This will significantly slow down Vite.
워닝이 발생한다. 이 부분은 조금 개선이 필요하다.


참고

profile
프론트엔드 개발자입니다.

1개의 댓글

comment-user-thumbnail
2023년 1월 19일

정말 대단하십니다.. 👍

답글 달기