Loading...
Loading...
React/Next.js 프로젝트의 마이그레이션을 가이드하는 스킬. Pages Router에서 App Router, JavaScript에서 TypeScript, CSS에서 Tailwind, 라이브러리 업그레이드 등. "마이그레이션", "migrate", "업그레이드", "전환", "변환" 등의 요청 시 사용.
npx skill4agent add ingpdw/pdw-fe-dev-tool fe-migrate$ARGUMENTS| From | To | 키워드 |
|---|---|---|
| Pages Router | App Router | |
| JavaScript | TypeScript | |
| CRA (Create React App) | Vite | |
| CSS/SCSS | Tailwind CSS | |
| Class Components | Hooks | |
| Redux | Zustand | |
| Jest | Vitest | |
| Axios | Fetch | |
| Moment.js | date-fns | |
src/app/layout.tsx ← pages/_app.tsx 에서 이동
src/app/page.tsx ← pages/index.tsx 에서 이동
src/app/globals.css ← styles/globals.css 에서 이동| Pages Router | App Router |
|---|---|
| |
| |
| |
| |
| |
// 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 /* ... */;
}useStateuseEffectuseContext"use client"// Before: next/head
import Head from "next/head";
<Head><title>Page</title></Head>
// After: Metadata export
export const metadata: Metadata = {
title: "Page",
};pnpm add -D typescript @types/react @types/react-dom @types/node
npx tsc --init.js.ts.jsx.tsx// tsconfig.json — 느슨하게 시작
{
"compilerOptions": {
"strict": false, // 점진적으로 true로 전환
"allowJs": true, // JS 파일 허용
"noImplicitAny": false // 나중에 true로
}
}noImplicitAny: truestrictNullChecks: truestrict: true| CRA | Vite |
|---|---|
| |
| |
| |
| |
| |
| Jest | Vitest |
|---|---|
| |
| |
| |
| |
| |
| |
| |
// 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"),
},
},
});// 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: "" }),
}));package.json