인터페이스는 현지화되어 있으며, Skill 소개와 설명 문서는 선택한 언어로 AI가 자동 번역합니다. 코드·명령어·경로·전문 용어는 원문을 유지하며 원작자 문서가 우선합니다.
Skills 둘러보기
개발 및 엔지니어링 · SKILL.md

generic-react-feature-developer

generic-react-feature-developer

Guide feature development for React applications with architecture focus. Covers Zustand/Redux patterns, IndexedDB usage, component systems, lazy loading strategies, and seamless integration. Use when adding new features, refactoring existing code, or planning major changes.

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill 문서

GitHub

name: generic-react-feature-developer description: Guide feature development for React applications with architecture focus. Covers Zustand/Redux patterns, IndexedDB usage, component systems, lazy loading strategies, and seamless integration. Use when adding new features, refactoring existing code, or planning major changes.

React Feature Developer

Guide feature development with React architecture patterns.

Extends: Generic Feature Developer - Read base skill for development workflow, scope assessment, and build vs integrate decisions.

React Architecture

Project Structure

src/
├── components/
│   ├── ui/          # Reusable primitives (Button, Input)
│   ├── features/    # Feature-specific components
│   └── layout/      # Layout components (Header, Sidebar)
├── hooks/           # Custom hooks (useAuth, useStore)
├── stores/          # Zustand stores
├── services/        # API clients, IndexedDB wrappers
├── types/           # TypeScript interfaces
└── lib/             # Utilities

State Management Patterns

Zustand Store (Preferred)

// stores/useFeatureStore.ts
interface FeatureState {
  items: Item[];
  isLoading: boolean;
  // Actions
  addItem: (item: Item) => void;
  removeItem: (id: string) => void;
}

const useFeatureStore = create<FeatureState>()(
  persist(
    (set) => ({
      items: [],
      isLoading: false,
      addItem: (item) => set((s) => ({ items: [...s.items, item] })),
      removeItem: (id) =>
        set((s) => ({
          items: s.items.filter((i) => i.id !== id),
        })),
    }),
    {
      name: "feature-storage",
      version: 1,
      migrate: (state, version) => {
        // Handle migrations between versions
        return state as FeatureState;
      },
    },
  ),
);

Zustand Selectors (Performance)

// Avoid re-renders with selectors
const items = useFeatureStore((state) => state.items);
const addItem = useFeatureStore((state) => state.addItem);

// Shallow compare for objects
import { shallow } from "zustand/shallow";
const { items, isLoading } = useFeatureStore(
  (state) => ({ items: state.items, isLoading: state.isLoading }),
  shallow,
);

Context vs Zustand Decision

Use ContextUse Zustand
Theme, locale (rarely changes)Frequently updated data
Authentication stateComplex state with actions
Provider already existsNeed persistence
Prop drilling 1-2 levelsCross-cutting concern

Server State (React Query)

// Server state - React Query
const { data, isLoading, error } = useQuery({
  queryKey: ["items", userId],
  queryFn: () => fetchItems(userId),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

// Mutations
const mutation = useMutation({
  mutationFn: createItem,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["items"] });
  },
});

IndexedDB Integration

When to Use

ScenarioSolution
< 5MB totallocalStorage via Zustand persist
> 5MB totalIndexedDB
Binary data (images, files)IndexedDB
Simple key-valuelocalStorage
Complex queriesIndexedDB

Service Wrapper Pattern

// services/indexedDBService.ts
class IndexedDBService {
  private db: IDBDatabase | null = null;

  async init() {
    return new Promise<void>((resolve, reject) => {
      const request = indexedDB.open("AppDB", 1);
      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };
      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        db.createObjectStore("items", { keyPath: "id" });
      };
    });
  }

  async setItem<T>(store: string, value: T): Promise<void> {
    // Implementation
  }

  async getItem<T>(store: string, key: string): Promise<T | null> {
    // Implementation
  }
}

export const indexedDBService = new IndexedDBService();

Lazy Loading

Component Lazy Loading

// Heavy components (>20KB)
const HeavyChart = lazy(() => import('./HeavyChart'));
const RichTextEditor = lazy(() => import('./RichTextEditor'));

// Pages
const SettingsPage = lazy(() => import('./pages/Settings'));

// Usage with Suspense
<Suspense fallback={<Skeleton />}>
  <HeavyChart data={data} />
</Suspense>

Route-Level Code Splitting

// React Router example
const routes = [
  {
    path: '/dashboard',
    element: <DashboardLayout />,
    children: [
      {
        path: 'settings',
        lazy: () => import('./pages/Settings'),
      },
    ],
  },
];

Custom Hook Patterns

Feature Hook

// hooks/useItems.ts
function useItems() {
  const items = useFeatureStore((s) => s.items);
  const addItem = useFeatureStore((s) => s.addItem);

  const sortedItems = useMemo(
    () => [...items].sort((a, b) => b.createdAt - a.createdAt),
    [items],
  );

  return { items: sortedItems, addItem };
}

Compound Hook (Combining Sources)

// hooks/useDashboard.ts
function useDashboard() {
  // Local state
  const [filter, setFilter] = useState("all");

  // Server state
  const { data: items } = useQuery({ queryKey: ["items"] });

  // Client state
  const preferences = usePreferencesStore((s) => s.dashboard);

  // Derived
  const filteredItems = useMemo(
    () => items?.filter((i) => filter === "all" || i.status === filter),
    [items, filter],
  );

  return { filter, setFilter, items: filteredItems, preferences };
}

Component Composition

Compound Components

// Usage: <Tabs><Tabs.List /><Tabs.Panel /></Tabs>
const TabsContext = createContext<TabsContextValue | null>(null);

function Tabs({ children, defaultValue }: TabsProps) {
  const [active, setActive] = useState(defaultValue);
  return (
    <TabsContext.Provider value={{ active, setActive }}>
      {children}
    </TabsContext.Provider>
  );
}

Tabs.List = function TabsList({ children }: { children: ReactNode }) {
  return <div role="tablist">{children}</div>;
};

Tabs.Panel = function TabsPanel({ value, children }: TabsPanelProps) {
  const { active } = useContext(TabsContext)!;
  if (value !== active) return null;
  return <div role="tabpanel">{children}</div>;
};

React Feature Checklist

Before Starting:

  • Read CLAUDE.md for project patterns
  • Check existing components for reuse
  • Plan state management approach
  • Estimate bundle size impact

During Development:

  • Follow project design system
  • TypeScript strict mode
  • Implement keyboard navigation
  • Add ARIA labels
  • Support dark mode

Before Completion:

  • Unit tests only if the user asked for them
  • Lazy load heavy components
  • Review bundle impact of new imports and dependencies
  • Review with code-reviewer skill

See Also

저장한 Skills