개인정보처리방침© 2026 DEV BAK - 기술블로그. All rights reserved.
DEV BAK - 기술블로그
frontend

린팅이 62배 빨라지면 개발 습관이 달라진다 — ESLint에서 Oxlint로, Vite 프로젝트 마이그레이션 경험

린터가 느리다고 느낀 적 있으신가요? 저도 그랬습니다. 중간 규모 React 프로젝트에서 pnpm lint를 돌릴 때마다 30초 가까이 기다리는 게 당연한 줄 알았는데, Oxlint로 바꾸고 나서 499ms가 나오는 걸 보고 처음엔 "설정이 잘못된 거 아닌가?" 의심했을 정도입니다. 공식 벤치마크 수치가 31초 vs 499ms이니 약 62배 차이인데, 실제로 경험해보면 그 숫자가 코드 작성 흐름에 얼마나 큰 영향을 주는지 실감하게 됩니다.

린팅이란 코드를 작성하면서 잠재적인 버그나 스타일 문제를 자동으로 감지해주는 것입니다. ESLint가 오랫동안 JavaScript/TypeScript 생태계의 표준이었는데, 요즘 Rust로 작성된 Oxlint가 그 자리를 빠르게 위협하고 있습니다. 속도뿐만 아니라 CI 린팅 비용 절감, 설정 유지보수 부담 감소도 실무에서 체감하는 이유입니다.

이 글에서는 기존 Vite + ESLint 프로젝트에서 Oxlint로 전환하는 방법을, 점진적 병행 운용부터 완전 교체, 그리고 Vite+ 통합 툴체인 미리 보기까지 단계별로 살펴봅니다. 이 글을 읽고 나면 자신의 프로젝트 상황에 맞는 마이그레이션 전략을 선택할 수 있습니다.


핵심 개념

Oxlint는 왜 이렇게 빠를까

처음엔 그냥 "Rust로 짜면 빠르다더라" 정도로만 알고 있었는데, 실제로 차이를 보고 나서 이유가 궁금해졌습니다. ESLint는 기본적으로 단일 스레드 모델로 설계되어 있어서, 규칙이 많아질수록 선형적으로 느려지는 구조입니다. 반면 Oxlint는 OXC(Oxidation Compiler) 프로젝트의 일부로 Rust로 작성되어 있고, 파일을 병렬로 처리하기 때문에 파일이 많을수록 효과가 배가됩니다.

OXC(Oxidation Compiler) — JavaScript/TypeScript 툴링을 Rust로 재구현하는 오픈소스 프로젝트. 파서, 린터(Oxlint), 번들러(Rolldown), 포매터(Oxfmt)가 동일한 코어 엔진을 공유합니다.

속도 차이가 실감이 안 된다면 이 수치를 보면 됩니다. M2 Max 기준 공식 벤치마크에서 ESLint가 31초 걸리는 작업을 Oxlint는 499ms에 처리합니다. 2026년 현재 695개 이상의 규칙이 eslint-plugin-react, typescript-eslint 등 주요 플러그인의 핵심 규칙 상당 부분을 커버하고 있어서, 많은 프로젝트에서 규칙 손실 없이 속도만 올리는 시나리오가 현실적으로 가능해졌습니다.

Vite 생태계에서 Oxlint의 위치

2026년 초, Vite 제작사인 VoidZero가 Vite+(vp CLI)를 공개 알파로 출시했습니다. Vite, Vitest, Oxlint, Oxfmt, Rolldown, tsdown을 단일 바이너리로 묶은 통합 툴체인입니다. Cloudflare의 VoidZero 인수로 이 생태계에 기업 투자도 가속화되고 있습니다.

도구 역할 상태
Oxlint 린터 (ESLint 대체) v1.0 Stable
Rolldown 번들러 (Vite 8+ 기본) Stable
Oxfmt 포매터 (Prettier 대체) 개발 중
Vite+ 통합 CLI Alpha

지금 당장 적용하고 싶다면 vite-plugin-oxlint로 기존 Vite 프로젝트에 바로 붙일 수 있고, 더 과감하게 가고 싶다면 Vite+로의 전체 이전도 선택지입니다. 이 글에서는 두 경로를 모두 다룹니다.

@oxlint/migrate와 eslint-plugin-oxlint의 차이

저도 처음 마이그레이션을 준비할 때 이 두 패키지가 헷갈렸는데, 역할이 완전히 다릅니다.

패키지 역할 언제 사용
@oxlint/migrate ESLint 설정 → .oxlintrc.json 자동 생성 CLI ESLint를 완전히 걷어낼 때
eslint-plugin-oxlint Oxlint가 커버하는 규칙을 ESLint에서 비활성화 ESLint와 병행 운용할 때

핵심 차이 — @oxlint/migrate는 ESLint 설정을 Oxlint 설정으로 변환하는 도구이고, eslint-plugin-oxlint는 두 린터를 동시에 쓸 때 중복 검사를 방지하는 브릿지입니다. 둘 다 설치할 필요는 없습니다.


실전 적용

예시 1: vite-plugin-oxlint로 5분 만에 통합하기

가장 빠른 방법입니다. ESLint를 당장 제거하지 않아도 되고, 기존 빌드 파이프라인에 Oxlint를 얹는 것부터 시작할 수 있습니다. 저는 이 방법으로 처음 도입해봤는데, 설정 파일 없이도 바로 오류를 잡아줘서 진입 장벽이 생각보다 낮았습니다.

bash
pnpm add -D vite-plugin-oxlint
typescript
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import oxlintPlugin from 'vite-plugin-oxlint'
 
export default defineConfig({
  plugins: [
    react(),
    oxlintPlugin({
      deny: ['correctness', 'perf'],  // 이 카테고리 규칙 위반 시 빌드 실패
      warn: ['suspicious'],            // 빌드는 통과하되 경고 출력
      allow: ['debugger'],             // 규칙 이름 기준으로 비활성화
    }),
  ],
})

allow 옵션에는 규칙 이름('debugger')과 카테고리('style') 둘 다 넣을 수 있습니다. 카테고리는 correctness, perf, suspicious, style, pedantic 중 하나를 사용하면 됩니다.

옵션 설명
deny 해당 카테고리 규칙 위반 시 빌드 실패로 처리
warn 빌드는 통과하되 터미널에 경고 출력
allow 특정 규칙(이름) 또는 카테고리 비활성화

이 상태에서 pnpm dev나 pnpm build를 실행하면 린팅이 함께 돌아갑니다. .oxlintrc.json이 없어도 기본값만으로 즉시 오류를 잡아주기 때문에, 설정 파일 없이 시작해서 필요할 때 추가하는 접근도 충분히 실용적입니다.

예시 2: @oxlint/migrate로 ESLint 설정 자동 변환하기

ESLint flat config(v9 방식)를 사용 중이라면 자동 변환이 됩니다. v8 레거시 설정(.eslintrc.*)을 쓰고 있다면 먼저 flat config로 변환이 필요한데, 이 순서를 지켜야 @oxlint/migrate가 제대로 읽을 수 있습니다.

bash
# ESLint v8 레거시 설정이라면 먼저 flat config로 변환
npx @eslint/migrate-config .eslintrc.js
 
# flat config 기준으로 .oxlintrc.json 자동 생성
npx @oxlint/migrate

실행하고 나면 이런 형태의 .oxlintrc.json이 생성됩니다.

json
{
  "$schema": "./node_modules/oxlint/configuration_schema.json",
  "plugins": ["react", "typescript", "oxc"],
  "rules": {
    "react/rules-of-hooks": "error",
    "react/only-export-components": ["warn", { "allowConstantExport": true }]
  }
}

주의 — "typeAware": true 옵션은 알파 기능입니다. options 섹션에 추가하면 no-floating-promises 같은 타입 기반 규칙이 활성화되지만, 알파 특성상 CI 환경에서는 별도 검증을 권장합니다.

변환 후 반드시 터미널 출력에서 "unsupported rules" 로그를 확인해보는 게 중요합니다. Oxlint가 아직 지원하지 않는 규칙은 자동으로 건너뛰고 어떤 규칙이 누락됐는지 목록으로 알려줍니다. 흔히 마주치는 상황 중 하나가 plugin:react/recommended 같은 프리셋이 변환되지 않는 경우인데, Oxlint는 현재 ESLint 방식의 프리셋을 직접 지원하지 않습니다. 이 경우 누락 규칙 목록을 확인하고 필요한 규칙을 rules 섹션에 직접 추가하면 됩니다.

변환이 잘 됐는지 확인하고 싶다면 이렇게 돌려볼 수 있습니다.

bash
# 변환된 설정으로 전체 린팅 결과 확인
pnpm oxlint .
 
# 특정 파일만 확인
pnpm oxlint src/components/MyComponent.tsx

예시 3: ESLint와 병행 운용하는 점진적 마이그레이션

실무에서 가장 현실적인 시나리오입니다. 팀에서 사용하는 커스텀 ESLint 플러그인이 있거나, Oxlint가 아직 지원하지 않는 규칙이 있을 때 유용합니다.

bash
pnpm add -D eslint-plugin-oxlint
javascript
// eslint.config.mjs
import oxlint from 'eslint-plugin-oxlint'
import reactPlugin from 'eslint-plugin-react'
 
export default [
  {
    plugins: { react: reactPlugin },
    rules: {
      'react/prop-types': 'error',
      'no-unused-vars': 'error',
    },
  },
  // Oxlint가 커버하는 규칙을 ESLint에서 자동으로 비활성화
  // → 두 린터가 같은 규칙을 중복 검사하지 않음
  oxlint.configs['flat/recommended'],
]
json
{
  "scripts": {
    "lint": "oxlint . && eslint ."
  }
}

Oxlint가 먼저 전체 파일을 빠르게 훑고, ESLint는 Oxlint가 커버하지 못하는 규칙만 처리하는 방식입니다. 솔직히 처음엔 "두 개를 동시에 돌리면 의미가 있나?" 싶었는데, Oxlint가 전체 파일의 95%를 0.5초에 처리하고 ESLint는 나머지 규칙만 확인하니 전체 속도가 눈에 띄게 빨라졌습니다.

예시 4: Vite+로 전체 툴체인 일원화하기

아직 알파 단계이지만, 미래 방향을 미리 살펴보고 싶다면 이렇게 접근할 수 있습니다. 저는 사이드 프로젝트에서 먼저 써봤는데, 설정 파일이 하나로 줄어드는 경험이 꽤 인상적이었습니다.

bash
# 기존 프로젝트를 Vite+로 이전
npx vp migrate

주의 — Vite+는 현재 알파 단계이고, 아래 설정 형식은 추후 변경될 가능성이 있습니다. 프로덕션 중요 프로젝트에는 신중하게 적용하는 것을 권장합니다.

typescript
// vite.config.ts (Vite+ 방식 — 린트 설정이 vite.config 안으로 통합됨)
export default {
  lint: {
    rules: {
      'no-unused-vars': 'error',
      'react/rules-of-hooks': 'error',
    },
  },
}

그린필드 프로젝트나 사이드 프로젝트에서 먼저 경험해보시면, 통합 툴체인이 실제 개발 흐름에 어떤 변화를 주는지 체감할 수 있습니다.


장단점 분석

장점

항목 내용
압도적인 속도 ESLint 대비 50~100배 빠름. M2 Max 기준 499ms vs 31초
멀티스레딩 CPU 코어를 최대 활용, 파일이 많을수록 효과 배가
점진적 마이그레이션 가능 ESLint와 공존 가능, 한 번에 다 바꾸지 않아도 됨
자동 마이그레이션 도구 @oxlint/migrate로 flat config 자동 변환
제로 설정 시작 .oxlintrc.json 없이도 기본값으로 즉시 동작
타입 인식 린팅 typescript-eslint 대비 8~12배 빠른 타입 기반 규칙 (알파)
낮은 CI 비용 린팅 시간이 줄면 CI 파이프라인 전체 실행 비용도 줄어듦

단점 및 주의사항

항목 내용 대응 방안
플러그인 프리셋 미지원 plugin:react/recommended 같은 프리셋 직접 사용 불가 .oxlintrc.json에서 필요한 규칙을 rules 섹션에 개별 지정
ESLint v8 레거시 설정 .eslintrc.* 파일 자동 변환 불가 npx @eslint/migrate-config로 flat config 변환 후 마이그레이션 진행
일부 규칙 미지원 695개+ 지원이지만 전체 ESLint 생태계 커버 안 됨 마이그레이션 로그에서 누락 규칙 확인 후 eslint-plugin-oxlint로 병행
JS Plugin은 알파 ESLint 호환 JS 플러그인 지원이 2026년 3월 알파 사용 중인 플러그인의 Oxlint 호환 여부를 먼저 확인
타입 인식 린팅은 알파 no-floating-promises 등 강력한 규칙은 알파 기능 활성화 필요 CI에서 간헐적 실패 가능성 있으므로 로컬에서 충분히 검증 후 활성화 권장
Vite+는 알파 vp CLI 전체가 아직 알파 단계 사이드 프로젝트에서 먼저 테스트 후 프로덕션 도입 검토

타입 인식 린팅(Type-Aware Linting) — TypeScript 타입 정보를 분석해 런타임 에러로 이어질 수 있는 패턴을 잡아내는 린팅 방식. no-floating-promises(await 없이 Promise를 흘려보내는 패턴 감지) 같은 규칙이 대표적입니다. 알파 기능이라 CI에서 간헐적으로 실패하는 경우가 있어서, 처음엔 로컬에서만 켜두고 안정화를 확인한 뒤 CI에 활성화하는 것을 권장합니다.

실무에서 가장 흔한 실수

  1. ESLint v8 설정을 그대로 @oxlint/migrate에 넘기기 — .eslintrc.js 파일은 @oxlint/migrate가 읽지 못합니다. 반드시 npx @eslint/migrate-config로 flat config로 변환한 뒤 Oxlint 마이그레이션을 진행해야 합니다.

  2. 누락 규칙 로그를 그냥 넘기기 — @oxlint/migrate 실행 후 "unsupported rules" 목록이 출력됩니다. 이걸 그냥 넘기면 기존 ESLint에서 잡아주던 규칙이 조용히 사라집니다. 누락된 규칙이 팀에 중요한 규칙인지 꼭 확인해보는 것을 권장합니다.

  3. eslint-plugin-oxlint 없이 두 린터 동시 실행 — ESLint와 Oxlint를 함께 쓸 때 이 플러그인 없이 돌리면 같은 규칙을 두 번 검사하게 됩니다. 속도 이점이 반감될 뿐 아니라 에러 메시지가 중복으로 출력되어 혼란스러울 수 있습니다.


마치며

30초씩 기다리던 습관이 없어졌습니다. 이게 가장 솔직한 경험담입니다. 린팅이 빠르면 "저장 후 결과 확인"을 무의식적으로 자주 하게 되고, 그게 쌓이면 버그를 훨씬 일찍 발견하는 습관으로 이어집니다. 지금 당장 전체를 바꾸지 않아도 됩니다. 작은 것부터 시작해서 속도 차이를 직접 체감한 뒤 본격적인 마이그레이션을 결정해보시면 좋겠습니다.

지금 바로 시작할 수 있는 3단계:

  1. 플러그인으로 맛보기 — pnpm add -D vite-plugin-oxlint를 설치하고 vite.config.ts에 플러그인을 추가한 뒤, pnpm build 또는 pnpm dev 실행 시 린팅 속도가 어떻게 달라지는지 확인해볼 수 있습니다.

  2. 자동 마이그레이션 시도 — ESLint flat config를 사용 중이라면 npx @oxlint/migrate를 실행해 .oxlintrc.json을 생성하고, 출력되는 "unsupported rules" 목록에서 팀에 중요한 규칙이 빠졌는지 검토해볼 수 있습니다.

  3. 전략 선택 및 CI 적용 — 누락 규칙이 없거나 대체 가능하다면 ESLint를 완전히 제거하고, 아직 커버되지 않는 규칙이 있다면 eslint-plugin-oxlint로 병행 운용할 수 있습니다. 팀 전체가 혜택을 보려면 이 단계에서 CI 파이프라인의 린팅 스크립트도 함께 교체하는 것을 권장합니다.


참고 자료

  • Oxlint 공식 문서
  • Migrate from ESLint | Oxlint 공식 가이드
  • vite-plugin-oxlint GitHub
  • Announcing Vite+ Alpha | VoidZero
  • Oxlint JS Plugins Alpha (2026-03-11)
  • Announcing Oxlint Type-Aware Linting | VoidZero
  • How to Gradually Migrate from ESLint to Oxlint
  • Speed kills: It's time to retire ESLint and migrate to Oxlint | LogRocket
  • Oxlint bench-linter GitHub (공식 벤치마크)
  • I Tried Vite+ and Replaced My Entire Frontend Toolchain | DEV
#Oxlint#ESLint#Vite#TypeScript#React#Rust#마이그레이션#린팅#CI-CD#OXC
공유하기

목차

핵심 개념Oxlint는 왜 이렇게 빠를까Vite 생태계에서 Oxlint의 위치@oxlint/migrate와 eslint-plugin-oxlint의 차이실전 적용예시 1: vite-plugin-oxlint로 5분 만에 통합하기예시 2: @oxlint/migrate로 ESLint 설정 자동 변환하기예시 3: ESLint와 병행 운용하는 점진적 마이그레이션예시 4: Vite+로 전체 툴체인 일원화하기장단점 분석장점단점 및 주의사항실무에서 가장 흔한 실수마치며참고 자료

추천 포스트

Tailwind v4 @container — 사이드바에서도 그리드에서도 깨지지 않는 반응형 컴포넌트
frontend

Tailwind v4 @container — 사이드바에서도 그리드에서도 깨지지 않는 반응형 컴포넌트

반응형 UI 작업을 하다 보면 어느 순간 이런 벽에 부딪힙니다. 공들여 만든 카드 컴포넌트가 메인 피드에서는 멀쩍멀쩍 잘 동작하는데, 똑같은 코드를 사이드바에 가져다 놓으니 이미지와 텍스트가 뭉개지는 상황이요. 브레이크포인트를 더 잘게 쪼개봐도, 뷰포트를 기준으로 판단하는 미디어 쿼리는...

2026년 06월 23일읽는 데 18분
TanStack DB 실전 도입: 클라이언트 사이드 DB가 낙관적 업데이트를 어떻게 바꾸는가
frontend

TanStack DB 실전 도입: 클라이언트 사이드 DB가 낙관적 업데이트를 어떻게 바꾸는가

솔직히 "클라이언트 사이드 임베디드 DB"라는 말을 처음 들었을 때 '또 유행어겠지' 싶었습니다. TanStack Query가 이미 서버 상태 관리를 잘 해주고 있는데, 그 위에 DB를 하나 더 올린다는 게 무슨 의미인가 싶었거든요. 그런데 TanStack Query에서 낙관적 업...

2026년 06월 23일읽는 데 17분
엣지 MFE 오케스트레이션으로 TTFB를 줄이는 Cloudflare Workers 구현법
frontend

엣지 MFE 오케스트레이션으로 TTFB를 줄이는 Cloudflare Workers 구현법

솔직히 처음 마이크로 프론트엔드(MFE) 이야기를 들었을 때, "팀마다 독립 배포가 된다고? 좋아 보이는데, 그럼 사용자 브라우저에서 각자 번들을 다 받아야 하는 거 아냐?" 하는 생각부터 들었습니다. 클라이언트 사이드 컴포지션의 고질적인 문제, 그러니까 여러 MFE의 JS 번들이 순차...

2026년 06월 23일읽는 데 23분
CSS @layer로 마이크로 프론트엔드 스타일 충돌 잡기 | Cascade Layers 격리 전략
frontend

CSS @layer로 마이크로 프론트엔드 스타일 충돌 잡기 | Cascade Layers 격리 전략

마이크로 프론트엔드(MFE, Micro Frontend) 프로젝트를 처음 진행했을 때, 팀A가 배포한 버튼 스타일이 팀B의 화면에서 멋대로 바뀌어 있는 걸 보고 꽤 당황했던 기억이 있습니다. 그 시절엔 CSS Modules로 클래스명 해시를 만들거나, 선택자 앞에 팀 prefix를 붙이...

2026년 06월 23일읽는 데 22분
Webpack을 Rsbuild로 옮겼더니 프로덕션 빌드가 74% 빨라졌다 — 마이그레이션 현실과 Rspack 함정
frontend

Webpack을 Rsbuild로 옮겼더니 프로덕션 빌드가 74% 빨라졌다 — 마이그레이션 현실과 Rspack 함정

솔직히 저도 처음엔 "또 새로운 빌드 도구야?"라는 반응이었습니다. Vite가 나왔을 때도 그랬고, Turbopack 발표 때도 그랬죠. 그런데 Alan 핀테크 팀의 사례를 보고 나서는 그냥 지나칠 수가 없었습니다. 가장 복잡한 프로젝트에서 프로덕션 빌드 시간이 2분 10초에서 34초로...

2026년 06월 23일읽는 데 19분
View Transitions API — 2025 Baseline 달성 이후, 라이브러리 없이 구현하는 프로덕션 전환 애니메이션
frontend

View Transitions API — 2025 Baseline 달성 이후, 라이브러리 없이 구현하는 프로덕션 전환 애니메이션

솔직히 처음 이 API를 봤을 때 "이게 된다고?" 싶었습니다. Framer Motion이나 GSAP 없이, CSS 몇 줄과 JavaScript 메서드 하나만으로 앱 같은 화면 전환을 구현한다는 게 너무 좋아 보였거든요. 그런데 실제로 써보니 정말 됩니다. 브라우저가 화면 전환 전후 스...

2026년 06월 07일읽는 데 20분