Communitygithub.com

react-patterns

React 18/19 패턴: 훅 규율, 서버/클라이언트 컴포넌트 경계, Suspense + 에러 경계, 폼 액션, 데이터 페칭, 상태 관리 결정 트리, 접근성 우선 구성. React 컴포넌트 작성 또는 리뷰 시 사용하세요.

react-patterns란 무엇인가요?

이 스킬은 React 18과 19에서 권장되는 관용적 패턴을 모아둔 종합적인 가이드입니다. 훅 규율(최상위 호출, 필요할 때만 메모이제이션), 서버/클라이언트 컴포넌트 경계 설계(Server Components 기본 사용, 'use client' 지시어로 클라이언트 컴포넌트 구분), Suspense와 ErrorBoundary를 이용한 선언적 로딩/에러 처리, React 19의 `useActionState` 및 `useOptimistic`을 활용한 폼 액션 패턴을 다룹니다. 또한 상태 관리 결정 트리를 제공하여 `useState` → `useReducer` → Context → 외부 스토어로의 점진적 확장을 안내하고, TanStack Query/SWR/RSC 간 데이터 페칭 선택을 위한 결정 매트릭스를 포함합니다. 접근성 우선 컴포넌트 작성법, 성능 최적화를 위한 `React.memo` 사용 조건, 컨텍스트 분할 전략, 리스트 가상화 등 실무에 바로 적용 가능한 패턴을 예제와 함께 설명합니다. 이 스킬은 Next.js App Router, React Router, TanStack Router 등 라우터에 독립적이며, React Native나 Remix 전용 패턴은 다루지 않습니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/ECC/tree/main/skills/react-patterns

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

React 패턴

견고하고 접근 가능하며 성능 좋은 컴포넌트 트리를 구축하기 위한 관용적인 React 18/19 패턴입니다.

활성화 시점

  • React 함수 컴포넌트, 커스텀 훅 또는 컴포넌트 트리를 작성하거나 수정할 때
  • JSX/TSX 파일을 리뷰할 때
  • 상태 구조나 컴포넌트 구성을 설계할 때
  • 클래스 컴포넌트 또는 forwardRef/useEffect가 많은 코드를 마이그레이션할 때
  • 지역 상태, 상태 끌어올리기, Context, 외부 스토어 중에서 선택할 때
  • Server Components / Client Components (Next.js App Router, RSC)로 작업할 때
  • React 19 액션 또는 제어 입력으로 폼을 구현할 때
  • TanStack Query / SWR / RSC로 데이터 페칭을 연결할 때

핵심 원칙

1. 렌더링은 Props와 State의 순수 함수

// 좋음: 렌더링 중에 파생
function Cart({ items }: { items: CartItem[] }) {
  const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
  return <span>{formatMoney(total)}</span>;
}

// 나쁨: 파생 상태를 별도로 저장
function Cart({ items }: { items: CartItem[] }) {
  const [total, setTotal] = useState(0);
  useEffect(() => {
    setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0));
  }, [items]);
  return <span>{formatMoney(total)}</span>;
}

useEffect에서 파생 상태를 계산하면 추가 렌더링 사이클이 생기고, 동기화가 깨질 수 있으며, 데이터 흐름이 불명확해집니다.

2. 사이드 이펙트는 렌더링 밖에서

이펙트, 변이, 네트워크 호출, 구독은 이벤트 핸들러나 useEffect 안에 있어야 하며, 절대 렌더 본문(body) 안에 있으면 안 됩니다.

3. 상속보다 합성

React에는 컴포넌트 상속 모델이 없습니다. children, render prop, 또는 컴포넌트 prop을 사용하여 합성하세요.

훅 규율

전체 규칙은 rules/react/hooks.md를 참조하세요. 주요 사항:

  • 최상위 레벨에서만 호출, 절대 조건부로 호출하지 않음
  • 모든 구독, 인터벌, 리스너는 정리(cleanup) 처리
  • 새 상태가 이전 상태에 의존할 때는 함수형 업데이터 사용 (setX(prev => prev + 1))
  • 기본 원칙: 메모이제이션 하지 말 것 — useMemo/useCallback은 프로파일러나 의존성 체인이 그 중요성을 증명할 때만 추가
  • 동일한 훅 시퀀스가 2개 이상의 컴포넌트에 나타날 때만 커스텀 훅 추출

상태 위치 결정 트리

한 컴포넌트에서만 사용?
  -> 그 컴포넌트 안에 useState

부모와 몇몇 하위 컴포넌트에서 사용?
  -> 가장 가까운 공통 조상으로 끌어올림

멀리 떨어진 브랜치에서 사용되면서 읽기 빈도가 낮음(테마, 인증, 로케일)?
  -> React Context

트리 전체에서 공유되며 업데이트 빈도가 높음?
  -> 외부 스토어 (Zustand, Jotai, Redux Toolkit)

서버에서 파생됨?
  -> 서버 상태 라이브러리 (TanStack Query, SWR, RSC fetch)

대부분의 페이지는 Context나 전역 스토어가 필요하지 않습니다. 중복된 상태 끌어올리기가 고통스러워지기 전까지는 추상화를 자제하세요.

서버 / 클라이언트 컴포넌트 (RSC)

// 서버 컴포넌트 - 기본값, 비동기, 자체적으로 JS를 전송하지 않음
export default async function ProductPage({ params }: { params: { id: string } }) {
  const product = await db.product.findUnique({ where: { id: params.id } });
  if (!product) notFound();
  return <ProductView product={product} />;
}

// 클라이언트 컴포넌트 - "use client"로 선택
export function AddToCartButton({ productId }: { productId: string }) {
  const [pending, startTransition] = useTransition();
  return (
    <button
      disabled={pending}
      onClick={() => startTransition(() => addToCart(productId))}
    >
      {pending ? "추가 중..." : "장바구니에 담기"}
    </button>
  );
}

경계:

  • 서버 -> 클라이언트: 직렬화 가능한 props 또는 children 전달
  • 클라이언트 -> 서버: <form action={...}>을 통해 서버 액션 호출하거나 이벤트 핸들러에서 명령형으로 호출
  • 클라이언트 컴포넌트 파일에서 서버 컴포넌트를 import 하지 말고 children을 통해 합성

Suspense + 에러 경계

<ErrorBoundary fallback={<ErrorView />}>
  <Suspense fallback={<UserSkeleton />}>
    <UserDetail id={id} />
  </Suspense>
</ErrorBoundary>
  • Suspense 경계는 라우트 루트가 아닌 데이터 가까이 배치하여 점진적으로 콘텐츠를 표시
  • Error Boundary는 여전히 클래스 API이며, 훅 친화적인 래퍼를 위해 react-error-boundary 사용
  • 경계는 자식의 렌더링, 생명주기, 생성자에서 발생한 에러를 잡지만, 이벤트 핸들러나 비동기 코드 내 에러는 잡지 않음

폼

React 19 폼 액션 (새 코드에 권장)

export function UserForm() {
  const [state, formAction, pending] = useActionState(updateUserAction, initial);
  return (
    <form action={formAction}>
      <input name="name" required />
      <button type="submit" disabled={pending}>저장</button>
      {state.error && <p role="alert">{state.error}</p>}
    </form>
  );
}

제어 입력(Controlled inputs)

값이 다른 UI를 제어하거나, 키 입력마다 포맷을 적용하거나, 실시간 유효성 검사를 구현할 때 제어 방식을 사용하세요.

복잡한 폼

다단계 폼, 동적 필드 배열, 교차 필드 유효성 검사에는 라이브러리(React Hook Form, TanStack Form)를 사용하세요. 단순함을 넘어서는 폼의 상태 관리를 직접 구현하는 것은 유지보수 함정입니다.

데이터 페칭 결정 매트릭스

필요 사항도구
Next.js App Router에서 요청별 데이터RSC await fetch()
클라이언트 측 캐시 + 변이 + 무효화TanStack Query
가벼운 클라이언트 캐시 + 재검증SWR
실시간 구독Server-Sent Events, WebSockets 또는 라이브러리의 구독 API
일회성 파이어 앤 포겟이벤트 핸들러 내 fetch()

애플리케이션 데이터에는 useEffect + fetch를 피하세요 — 경쟁 조건, 캐시 부재, 재시도 없음, Suspense 통합 없음.

합성 레시피

children을 통한 슬롯

<Layout>
  <Header />
  <Main>{content}</Main>
</Layout>

명명된 슬롯

<Page header={<Nav />} sidebar={<Filters />}>
  <Results />
</Page>

컴파운드 컴포넌트 (Context를 통한 공유 상태)

<Tabs defaultValue="profile">
  <Tabs.List>
    <Tabs.Trigger value="profile">프로필</Tabs.Trigger>
    <Tabs.Trigger value="settings">설정</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Panel value="profile"><Profile /></Tabs.Panel>
  <Tabs.Panel value="settings"><Settings /></Tabs.Panel>
</Tabs>

Render prop / 함수-자식 패턴

부모가 렌더링된 출력에 매개변수를 전달해야 할 때 유용합니다:

<DataLoader id={id}>
  {({ data, isLoading }) => isLoading ? <Spinner /> : <UserCard user={data} />}
</DataLoader>

현대적인 대안: 같은 형태를 반환하는 훅 (useData(id)) — 보통 더 깔끔합니다.

성능

React.memo가 실제로 도움이 되는 경우

다음 조건을 모두 만족할 때만 React.memo로 컴포넌트를 감싸세요:

  1. 자주 리렌더링됨
  2. props가 대부분의 렌더링 사이에 동일함
  3. 렌더링 비용이 측정 가능할 정도로 큼

React.memo는 모든 렌더링에서 동등성 검사를 추가합니다. props가 대부분의 렌더링에서 다르다면, 검사는 순수 오버헤드입니다.

렌더링 캐스케이드 방지

  • 가능하면 상태를 위로 끌어올리기보다 아래로 내리기
  • 컨텍스트 분할: 관심사별로 하나의 컨텍스트. themeContext의 변경이 auth 사용자를 리렌더링하지 않도록
  • 외부 상태 라이브러리에는 useSyncExternalStore 사용 — 안전한 동시성 렌더링을 위해 필수

목록

  • 안정적인 key prop 제공 (데이터베이스 ID, 배열 인덱스는 사용하지 말 것)
  • 화면에 보이는 항목 수가 비간단한 행을 가진 ~50개를 초과하면 @tanstack/react-virtual 또는 react-window로 긴 목록 가상화

접근성 우선 구성

  • role 속성을 사용하기 전에 항상 의미론적 HTML (<button>, <a>, <nav>, <main>)을 먼저 렌더링
  • 모든 상호작용 요소는 키보드로 접근 가능해야 함
  • 폼 입력에는 레이블 필요 — <label htmlFor> 또는 시각적으로 아이콘으로 레이블링된 경우 aria-label
  • 라우트 변경 및 모달 열기/닫기 시 포커스 관리
  • 컴포넌트 테스트에서 axe 실행 ( skills/react-testing 참조)
  • 관련 링크: skills/accessibility/SKILL.md 에서 WCAG 기준과 패턴 라이브러리 다룸

라우팅

이 스킬은 라우터에 독립적입니다. 위의 패턴은 React Router, TanStack Router, Next.js App Router, Remix Router와 함께 작동합니다. 라우터별 패턴(로더, 액션, 중첩 레이아웃)은 해당 라우터의 문서를 따르세요 — 이는 React 코어 위에 계층화된 프레임워크 관심사입니다.

범위 밖 (참고 섹션)

  • Next.js 세부 사항: App Router 데이터 로딩, Route Handlers, Middleware, Parallel Routes — 별도의 관심사이며 Next.js 문서 참조
  • React Native: 플랫폼별 패턴은 별도의 react-native-patterns 스킬(아직 없음)을 보증할 만큼 다름
  • Remix: Loader/action 규칙은 RSC와 겹치지만 Remix 문서를 따름

관련 자료

  • 규칙: rules/react/ — 코딩 스타일, 훅, 패턴, 보안, 테스팅
  • 스킬: react-performance (Vercel 기반 성능 규칙), frontend-patterns (프레임워크 간 UI 관심사), accessibility, angular-developer (프레임워크 비교)
  • 에이전트: react-reviewer (코드 리뷰), react-build-resolver (빌드/번들러 오류 처리)
  • 명령어: /react-review, /react-build, /react-test

예제

디바운스된 검색을 위한 커스텀 훅

function useDebounce<T>(value: T, delay = 300): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}

function SearchBox() {
  const [query, setQuery] = useState("");
  const debounced = useDebounce(query, 300);
  const { data } = useQuery({
    queryKey: ["search", debounced],
    queryFn: () => searchApi(debounced),
    enabled: debounced.length > 0,
  });
  return (
    <>
      <input value={query} onChange={(e) => setQuery(e.target.value)} />
      <Results items={data ?? []} />
    </>
  );
}

React 19 useOptimistic을 사용한 낙관적 UI

export function MessageList({ messages }: { messages: Message[] }) {
  const [optimistic, addOptimistic] = useOptimistic(
    messages,
    (state, newMessage: Message) => [...state, newMessage],
  );

  async function send(formData: FormData) {
    const text = String(formData.get("text"));
    addOptimistic({ id: "pending", text, sender: "me" });
    await saveMessage(text);
  }

  return (
    <>
      <ul>{optimistic.map((m) => <li key={m.id}>{m.text}</li>)}</ul>
      <form action={send}>
        <input name="text" />
        <button type="submit">보내기</button>
      </form>
    </>
  );
}

렌더링 캐스케이드를 방지하기 위한 컨텍스트 분할

// 두 개의 컨텍스트: 하나는 거의 변하지 않고, 다른 하나는 자주 변함
const ThemeContext = createContext<Theme>("light");
const NotificationsContext = createContext<Notification[]>([]);

// ThemeContext만 사용하는 컴포넌트는 알림이 변경되어도 리렌더링되지 않음

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/content-engine

Create platform-native content systems for X, LinkedIn, TikTok, YouTube, newsletters, and repurposed multi-platform campaigns. Use when the user wants social posts, threads, scripts, content calendars, or one source asset adapted cleanly across platforms.

affaan-m/ECC

Multi-platform content distribution across X, LinkedIn, Threads, and Bluesky. Adapts content per platform using content-engine patterns. Never posts identical content cross-platform. Use when the user wants to distribute content across social platforms.

affaan-m/fal-ai-media

Unified media generation via fal.ai MCP — image, video, and audio. Covers text-to-image (Nano Banana), text/image-to-video (Seedance, Kling, Veo 3), text-to-speech (CSM-1B), and video-to-audio (ThinkSound). Use when the user wants to generate images, videos, or audio with AI.

affaan-m/manim-video

日本語翻訳:このファイルは manim-video 用の日本語翻訳が必要です

affaan-m/remotion-video-creation

Remotion のベストプラクティス - React で動画を作成する。3D、アニメーション、音声、字幕、チャート、トランジションなどをカバーするドメイン固有の29のルール。

affaan-m/video-editing

AI-assisted video editing workflows for cutting, structuring, and augmenting real footage. Covers the full pipeline from raw capture through FFmpeg, Remotion, ElevenLabs, fal.ai, and final polish in Descript or CapCut. Use when the user wants to edit video, cut footage, create vlogs, or build video content.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

agent-self-evaluation

Use after completing any non-trivial task. The agent self-rates its output on 5 axes — accuracy, completeness, clarity, actionability, conciseness — with concrete evidence per criterion. Produces a structured 1-5 scorecard with specific improvement suggestions.

agent-sort

Build an evidence-backed ECC install plan for a specific repo by sorting skills, commands, rules, hooks, and extras into DAILY vs LIBRARY buckets using parallel repo-aware review passes. Use when ECC should be trimmed to what a project actually needs instead of loading the full bundle.

ai-first-engineering

Engineering operating model for teams where AI agents generate a large share of implementation output. Use when setting team process, review gates, or ownership rules for a codebase largely written by agents.

ai-regression-testing

Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code. Use when adding regression coverage to AI-assisted code, or when the same model both wrote and reviewed a change.

android-clean-architecture

Clean Architecture patterns for Android and Kotlin Multiplatform projects — module structure, dependency rules, UseCases, Repositories, and data layer patterns. Use when structuring modules, layers, or data flow in an Android or KMP project.

angular-developer

Generates Angular code and provides architectural guidance. Trigger when creating projects, components, or services, or for best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling (component styles, Tailwind CSS), testing, or CLI tooling.

관련 스킬