fe-migrate

📁 ingpdw/pdw-fe-dev-tool 📅 6 days ago
1
总安装量
1
周安装量
#51873
全站排名
安装命令
npx skills add https://github.com/ingpdw/pdw-fe-dev-tool --skill fe-migrate

Agent 安装分布

mcpjam 1
claude-code 1
replit 1
junie 1
zencoder 1

Skill 文档

FE Migration Guide

$ARGUMENTS에서 마이그레이션 소스와 타겟을 파싱하여 단계별 가이드를 제공하고 실행한다.

지원 마이그레이션

From To 키워드
Pages Router App Router pages app-router
JavaScript TypeScript js typescript, js ts
CRA (Create React App) Vite cra vite
CSS/SCSS Tailwind CSS css tailwind
Class Components Hooks class hooks
Redux Zustand redux zustand
Jest Vitest jest vitest
Axios Fetch axios fetch
Moment.js date-fns moment date-fns

인자가 없으면 사용자에게 어떤 마이그레이션을 원하는지 질문한다.

마이그레이션 공통 절차

  1. 현재 상태 분석: 프로젝트 구조, 의존성, 설정 파일을 파악한다
  2. 영향 범위 파악: 변경이 필요한 파일 목록을 Glob/Grep으로 추출한다
  3. 마이그레이션 계획 제시: 단계별 작업 목록을 사용자에게 보여준다
  4. 단계별 실행: 승인 후 순차적으로 변환을 진행한다
  5. 검증: 빌드 및 테스트 통과를 확인한다

Pages Router → App Router

단계별 가이드

1단계: 기반 설정

src/app/layout.tsx     ← pages/_app.tsx 에서 이동
src/app/page.tsx       ← pages/index.tsx 에서 이동
src/app/globals.css    ← styles/globals.css 에서 이동

2단계: 페이지 변환 규칙

Pages Router App Router
pages/about.tsx app/about/page.tsx
pages/blog/[slug].tsx app/blog/[slug]/page.tsx
pages/api/users.ts app/api/users/route.ts
pages/_error.tsx app/error.tsx
pages/404.tsx app/not-found.tsx

3단계: 데이터 페칭 변환

// Before: getServerSideProps
export async function getServerSideProps() {
  const data = await fetchData();
  return { props: { data } };
}
export default function Page({ data }) { /* ... */ }

// After: Server Component async
export default async function Page() {
  const data = await fetchData();
  return /* ... */;
}
// Before: getStaticProps + getStaticPaths
export async function getStaticPaths() {
  return { paths: [...], fallback: false };
}
export async function getStaticProps({ params }) {
  const data = await fetchData(params.id);
  return { props: { data }, revalidate: 60 };
}

// After: generateStaticParams + fetch with revalidate
export async function generateStaticParams() {
  return [...];
}
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const data = await fetchData(id);
  return /* ... */;
}

4단계: Hook/상태 사용 컴포넌트 → “use client”

  • useState, useEffect, useContext, 이벤트 핸들러가 있는 컴포넌트에 "use client" 추가
  • 가능한 Client Component를 작게 분리

5단계: Head → Metadata

// Before: next/head
import Head from "next/head";
<Head><title>Page</title></Head>

// After: Metadata export
export const metadata: Metadata = {
  title: "Page",
};

JavaScript → TypeScript

단계별 가이드

1단계: TypeScript 설치 & 설정

pnpm add -D typescript @types/react @types/react-dom @types/node
npx tsc --init

2단계: 파일 확장자 변경

  • .js → .ts (로직 파일)
  • .jsx → .tsx (JSX 포함 파일)

3단계: 점진적 타입 추가

// tsconfig.json — 느슨하게 시작
{
  "compilerOptions": {
    "strict": false,           // 점진적으로 true로 전환
    "allowJs": true,           // JS 파일 허용
    "noImplicitAny": false     // 나중에 true로
  }
}

4단계: strict mode 순차 활성화

  1. noImplicitAny: true
  2. strictNullChecks: true
  3. strict: true

CRA → Vite

변환 포인트

CRA Vite
react-scripts vite + @vitejs/plugin-react
public/index.html index.html (루트)
REACT_APP_* 환경변수 VITE_* 환경변수
process.env.REACT_APP_* import.meta.env.VITE_*
src/setupTests.ts vitest.config.ts + setup

Jest → Vitest

변환 포인트

Jest Vitest
jest.config.js vitest.config.ts
jest.fn() vi.fn()
jest.mock() vi.mock()
jest.spyOn() vi.spyOn()
@jest/globals vitest
jest.useFakeTimers() vi.useFakeTimers()
moduleNameMapper resolve.alias in vite config
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: "jsdom",
    setupFiles: "./src/test/setup.ts",
    css: true,
  },
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

Redux → Zustand

변환 패턴

// Before: Redux Toolkit
const userSlice = createSlice({
  name: "user",
  initialState: { name: "", email: "" },
  reducers: {
    setUser: (state, action) => { Object.assign(state, action.payload); },
    clearUser: () => ({ name: "", email: "" }),
  },
});

// After: Zustand
interface UserState {
  name: string;
  email: string;
  setUser: (user: { name: string; email: string }) => void;
  clearUser: () => void;
}

const useUserStore = create<UserState>()((set) => ({
  name: "",
  email: "",
  setUser: (user) => set(user),
  clearUser: () => set({ name: "", email: "" }),
}));

실행 규칙

  1. 인자가 없으면 사용자에게 마이그레이션 종류를 질문한다
  2. 마이그레이션 전 현재 프로젝트 상태를 반드시 분석한다
  3. 대규모 변경은 단계별로 나눠서 진행하고, 각 단계 후 빌드/테스트를 확인한다
  4. 기존 코드를 삭제하기 전에 새 코드가 동작하는지 확인한다
  5. package.json 변경이 필요하면 명령어를 안내하고 사용자 승인 후 실행한다
  6. 지원 목록에 없는 마이그레이션도 요청 시 분석 후 가이드를 제공한다