본문으로 건너뛰기
AIDevOps
  • Learn
  • Learning Paths
  • Practice
  • Open Source
  • Books
  • Engineering

    AI DevOpsAI 서비스 개발·운영 전체 지도LLMOpsLLM 배포·평가·관측실전 프로젝트AI Agent 프로젝트 실습

    Knowledge

    Docs기술 문서 모음Blog엔지니어링 아티클Plogger개발 기록 피드

    Validate

    Certification3단계 역량 인증 · 준비 중
AI Models
LlamaMistralGemmaDeepSeekQwen
🎨 Frontend
Frontend 입문 & 로드맵JavaScriptTypeScript|ReactNext.js|VueNuxt
🤖 AI 실전 개발
AI 실전 입문 & 로드맵Hugging FaceLangChainLlamaIndexLLMOps|LangGraphMCPMulti-AgentAgent Evaluation
🧠 AI Core
AI 입문 & 로드맵ML FundamentalsLLM Fundamentals|Python AIC++|PyTorchTensorFlowJAX
🧠 AI Agent 개발
금융 AI AgentLLM API 서버주식 투자 AgentAIOps AI Agent교육 AI Agent코딩 AI Agent
🌱 Spring Cloud
Spring 입문 & 로드맵Spring Cloud GatewaySpring BootJava|Spring AISpring SecuritySpring BatchSpring JPA
🐳 DevOps
DevOps 입문 & 로드맵LinuxDockerCI/CD|Kubernetes 기본K8s 심화/실무PrometheusGrafana
🧱 인프라
인프라 입문 & 로드맵NginxRedis
☁️ 클라우드
클라우드 입문 & 로드맵AWSGCPAzureNCPCloudflare
📱 Mobile
Mobile 입문 & 로드맵KotlinAndroidFlutter
⚙️ Backend
Backend 입문 & 로드맵Python 기본FastAPIDjangoFlask|CGoGinNode.js
💾 Database
DB 입문 & 로드맵공통 SQLOracleMySQLPostgreSQL|MongoDB벡터 DB
🧪 검증
k6JMeternGrinder
AIDevOps

Engineering AI. From Code to Production.
AI와 AI Agent를 개발하고 운영하기 위한 엔지니어링 학습 플랫폼

Learn

  • 전체 가이드
  • Learning Paths
  • Practice
  • Books

Resources

  • AI DevOps
  • LLMOps
  • 실전 프로젝트
  • Docs
  • Blog
  • Plogger
  • Open Source
  • Certification (준비 중)

Start Here

  • AI Core 로드맵
  • AI 실전 개발 로드맵
  • Spring Cloud 로드맵
  • DevOps 로드맵
  • 인프라 로드맵

 

  • 클라우드 로드맵
  • Frontend 로드맵
  • Mobile 로드맵
  • Backend 로드맵
  • Database 로드맵
© 2026 AI DevOps Korea. All rights reserved.
이용약관개인정보처리방침Sitemaptestforge.kr
  1. Home
  2. Learn
  3. Frontend
  4. React
프론트엔드 프레임워크 가이드

⚛️ React 완전 가이드

Visitors

Meta의 React 19로 현대적인 UI를 구축합니다. Hooks 기본기부터 Actions 기반 신기능, 상태 관리 전략, 서버 상태 페칭, 폼 검증, 컴포넌트 설계 패턴, 성능 최적화, 테스트, 접근성까지 실무 React 개발의 전 과정을 다룹니다.

  • Intermediate · 중급
  • 업데이트 2026.07.28
  • 약 12분 읽기
  • 14개 섹션
  • 예제 코드 9개
  • 웹 IDE 실습 제공
⚛️

React 웹 IDE

설치 없이 브라우저에서 코드를 실행하고 단계별 예제로 익혀보세요.

웹 IDE 열기 →
SPA 웹앱Next.js SSRReact Native (모바일)Electron (데스크톱)

목차

0 / 16
  1. 가이드 사용법
  2. 구조 다이어그램
  3. 환경 설정
  4. Hooks 기본기
  5. React 19 신기능
  6. 상태 관리 전략
  7. 서버 상태 & 데이터 페칭
  8. 폼 처리 & 검증
  9. 컴포넌트 설계 패턴
  10. 성능 최적화
  11. 테스트
  12. 접근성
  13. 다음 단계
  14. React 설계
  15. 운영 기준
  16. 검증 전략
목차 16개 섹션
  1. 가이드 사용법
  2. 구조 다이어그램
  3. 환경 설정
  4. Hooks 기본기
  5. React 19 신기능
  6. 상태 관리 전략
  7. 서버 상태 & 데이터 페칭
  8. 폼 처리 & 검증
  9. 컴포넌트 설계 패턴
  10. 성능 최적화
  11. 테스트
  12. 접근성
  13. 다음 단계
  14. React 설계
  15. 운영 기준
  16. 검증 전략

가이드 사용법

읽는 방향

React를 실무 흐름으로 이해하기

Meta의 React 19로 현대적인 UI를 구축합니다. Hooks 기본기부터 Actions 기반 신기능, 상태 관리 전략, 서버 상태 페칭, 폼 검증, 컴포넌트 설계 패턴, 성능 최적화, 테스트, 접근성까지 실무 React 개발의 전 과정을 다룹니다. 이 가이드는 개념을 나열하기보다, 실제 프로젝트에서 판단해야 하는 순서대로 내용을 따라갈 수 있게 구성했습니다.

핵심 관점

프론트엔드 / 앱 개발

화면을 그리는 법에서 멈추지 않고, 상태, 데이터 요청, 라우팅, 접근성, 배포 단위까지 함께 봅니다.

SPA 웹앱Next.js SSRReact Native (모바일)Electron (데스크톱)

구조 다이어그램

글로 읽은 내용을 머릿속에 오래 남기려면 먼저 흐름을 그림으로 잡는 편이 좋습니다. 아래 두 그림은 React를 학습할 때 계속 되돌아볼 수 있는 기준 지도입니다.

학습 흐름

다이어그램 렌더링 중…

아키텍처 관점

다이어그램 렌더링 중…

환경 설정

React를 처음 펼칠 때는 세부 명령보다 큰 그림이 먼저입니다. 이 섹션에서는 앞으로 배울 개념들이 어떤 문제를 풀기 위해 등장했는지부터 잡아봅니다.

서버 렌더링·라우팅·SEO까지 필요하면 Next.js, 가볍고 빠른 순수 SPA면 Vite를 선택하세요.
BASH
# Next.js (추천 — SSR/Server Components 포함)
npx create-next-app@latest my-app --typescript --tailwind --app

# Vite (SPA)
npm create vite@latest my-app -- --template react-ts
cd my-app && npm install && npm run dev

# React 19 코어만 별도 설치할 때
npm install react@19 react-dom@19

Hooks 기본기

여기서는 Hooks 기본기을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

useState, useEffect, useCallback, useMemo, useRef — Hook 규칙은 단 두 가지입니다. 최상위에서만 호출하고, 컴포넌트/커스텀 훅 안에서만 호출합니다.
TSX
import { useState, useEffect, useCallback } from 'react';

function useFetch<T>(url: string) {
  const [data, setData]   = useState<T | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;
    fetch(url)
      .then(r => r.json())
      .then(d => { if (!cancelled) setData(d); })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [url]);

  return { data, loading };
}

Tip

  • Hook은 조건문·반복문 안에서 호출하지 않는다 — 위반 시 상태 슬롯이 뒤섞인다
  • useEffect 의존성 배열은 ESLint exhaustive-deps 규칙으로 관리한다
  • cleanup 함수는 언마운트뿐 아니라 의존성이 바뀔 때마다 실행된다는 점을 기억한다
  • 여러 state가 항상 같이 바뀐다면 useState 여러 개보다 useReducer로 묶는 것이 낫다

React 19 신기능

여기서는 React 19 신기능을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

React 19는 폼 제출과 비동기 작업을 다루는 방식을 Actions 중심으로 재정의했습니다. useActionState, useOptimistic, use()가 핵심입니다.
TSX
import { useActionState, useOptimistic, use } from 'react';

// 폼 제출 상태(pending/error)를 자동으로 관리
function ProfileForm({ updateName }: {
  updateName: (prev: { error?: string }, formData: FormData) => Promise<{ error?: string }>;
}) {
  const [state, formAction, isPending] = useActionState(updateName, {});

  return (
    <form action={formAction}>
      <input name="name" />
      <button type="submit" disabled={isPending}>저장</button>
      {state.error && <p>{state.error}</p>}
    </form>
  );
}

// 서버 응답 전에 UI를 낙관적으로 갱신
function LikeButton({ likes, addLike }: { likes: number; addLike: () => Promise<void> }) {
  const [optimisticLikes, addOptimisticLike] = useOptimistic(likes, (state) => state + 1);
  return (
    <button onClick={async () => { addOptimisticLike(); await addLike(); }}>
      좋아요 {optimisticLikes}
    </button>
  );
}

// Promise를 조건부로 읽기 (Suspense와 연동)
function Comments({ commentsPromise }: { commentsPromise: Promise<{ id: string; text: string }[]> }) {
  const comments = use(commentsPromise);
  return <ul>{comments.map(c => <li key={c.id}>{c.text}</li>)}</ul>;
}
API용도
useActionStateform action의 pending/에러 상태를 자동으로 관리
useOptimistic서버 응답 전에 UI를 낙관적으로 먼저 갱신
use()Promise·Context를 조건부로 읽고 Suspense와 자동 연동
React Compilermemo/useMemo/useCallback을 빌드 타임에 자동 삽입하는 최적화

상태 관리 전략

여기서는 상태 관리 전략을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

상태를 어디에 둘지 결정하는 기준이 컴포넌트 트리 구조보다 중요합니다. 기본은 로컬 state, 여러 컴포넌트가 공유하면 끌어올리기(lift up), 트리 전체에 필요하면 Context, 앱 전역 client state는 전용 라이브러리로 관리하세요.
store.tsTYPESCRIPT
import { create } from 'zustand';

interface BearStore {
  count: number;
  inc: () => void;
  dec: () => void;
}

export const useBearStore = create<BearStore>((set) => ({
  count: 0,
  inc: () => set((s) => ({ count: s.count + 1 })),
  dec: () => set((s) => ({ count: s.count - 1 })),
}));
규모도구적합한 경우
컴포넌트 로컬useState / useReducer해당 컴포넌트와 자식만 사용하는 상태
일부 트리 공유Context + useReducer테마, 인증 사용자 등 자주 안 바뀌는 값
앱 전역 client stateZustandUI 상태, 폼 위저드 단계 등 (보일러플레이트 최소)
대규모/복잡한 도메인Redux Toolkit액션 이력 추적·시간여행 디버깅이 필요한 대형 팀
서버에서 온 데이터TanStack Query / SWR캐싱·재검증·낙관적 업데이트가 필요한 모든 API 데이터

Tip

서버에서 받아온 데이터는 client state 라이브러리에 복사해 두지 말 것 — 다음 섹션의 서버 상태 관리를 사용한다

서버 상태 & 데이터 페칭

여기서는 서버 상태 & 데이터 페칭을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

서버에서 온 데이터는 client state와 다른 문제(캐싱, 재검증, 중복 요청 제거, 낙관적 업데이트)를 가지므로 TanStack Query 같은 전용 라이브러리로 다루는 편이 안전합니다.
TSX
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

function TodoList() {
  const queryClient = useQueryClient();

  const { data, isPending, error } = useQuery({
    queryKey: ['todos'],
    queryFn: () => fetch('/api/todos').then(r => r.json()),
    staleTime: 30_000,
  });

  const addTodo = useMutation({
    mutationFn: (title: string) =>
      fetch('/api/todos', { method: 'POST', body: JSON.stringify({ title }) }),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
  });

  if (isPending) return <p>로딩 중...</p>;
  if (error) return <p>불러오기 실패</p>;

  return (
    <ul>
      {data.map((todo: { id: string; title: string }) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  );
}

Tip

  • staleTime을 0으로 두면 포커스 전환마다 재요청이 발생하니 데이터 특성에 맞게 조정한다
  • invalidateQueries 대신 setQueryData로 캐시를 직접 갱신하면 낙관적 업데이트가 더 빨라진다

폼 처리 & 검증

여기서는 폼 처리 & 검증을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

React Hook Form + Zod 조합이 타입 안전한 폼 검증의 표준입니다. 입력마다 전체 리렌더링이 아니라 필드 단위로만 리렌더링되어 성능도 좋습니다.
TSX
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  email: z.string().email('올바른 이메일을 입력하세요'),
  password: z.string().min(8, '8자 이상 입력하세요'),
});
type FormValues = z.infer<typeof schema>;

function LoginForm() {
  const { register, handleSubmit, formState: { errors, isSubmitting } } =
    useForm<FormValues>({ resolver: zodResolver(schema) });

  const onSubmit = async (values: FormValues) => {
    await fetch('/api/login', { method: 'POST', body: JSON.stringify(values) });
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} />
      {errors.email && <p>{errors.email.message}</p>}
      <input type="password" {...register('password')} />
      {errors.password && <p>{errors.password.message}</p>}
      <button type="submit" disabled={isSubmitting}>로그인</button>
    </form>
  );
}

컴포넌트 설계 패턴

여기서는 컴포넌트 설계 패턴을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

컴포넌트가 커지면 로직은 커스텀 훅으로 뽑아내고, 에러는 Error Boundary로 격리하며, 함께 동작하는 컴포넌트 묶음은 Compound Component로 응집도를 높입니다.
TSX
import { Component, createContext, useContext, useState, type ReactNode } from 'react';

// Error Boundary — 하위 트리 에러가 앱 전체를 무너뜨리지 않게 격리
class ErrorBoundary extends Component<{ fallback: ReactNode; children: ReactNode }, { hasError: boolean }> {
  state = { hasError: false };
  static getDerivedStateFromError() { return { hasError: true }; }
  componentDidCatch(error: unknown) { console.error(error); }
  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}

// Compound Component — 내부 상태를 Context로 공유
const TabsContext = createContext<{ active: string; setActive: (id: string) => void } | null>(null);

function Tabs({ defaultTab, children }: { defaultTab: string; children: ReactNode }) {
  const [active, setActive] = useState(defaultTab);
  return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;
}
Tabs.Tab = function Tab({ id, children }: { id: string; children: ReactNode }) {
  const ctx = useContext(TabsContext)!;
  return <button onClick={() => ctx.setActive(id)} aria-selected={ctx.active === id}>{children}</button>;
};

성능 최적화

여기서는 성능 최적화을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

React 19부터는 React Compiler가 memo/useMemo/useCallback을 빌드 타임에 자동으로 삽입해줍니다. 그래도 원리를 이해해야 컴파일러가 놓치는 경우를 판단할 수 있습니다.
TSX
import { memo, useMemo, useCallback } from 'react';

// 불필요한 리렌더링 방지
const ListItem = memo(({ item }: { item: Item }) => (
  <div>{item.name}</div>
));

function List({ items }: { items: Item[] }) {
  // 비싼 계산 캐싱
  const sorted = useMemo(
    () => [...items].sort((a, b) => a.name.localeCompare(b.name)),
    [items]
  );
  // 핸들러 캐싱
  const handleClick = useCallback((id: string) => {
    console.log(id);
  }, []);
  return sorted.map(item => <ListItem key={item.id} item={item} />);
}

Tip

  • React Compiler를 켜면 대부분의 memo/useCallback을 직접 쓸 필요가 없어지지만, Hook 규칙을 어기면 최적화가 비활성화된다
  • 리렌더링 문제의 대부분은 memo 부족이 아니라 state 위치 문제 — 상태를 필요한 만큼만 아래로 내린다
  • 리스트 key는 index 대신 안정적인 id를 사용한다
  • React DevTools Profiler로 실제 병목을 측정한 뒤 최적화한다

테스트

여기서는 테스트을 실제 코드와 함께 확인합니다. 예제를 그대로 따라 하기보다, 입력과 출력, 그리고 바뀌기 쉬운 부분이 어디인지 보면서 읽어보세요.

Vitest + React Testing Library로 "사용자가 보는 화면" 기준으로 테스트합니다. 구현 세부사항이 아니라 동작을 검증하세요.
Counter.test.tsxTSX
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { Counter } from './Counter';

describe('Counter', () => {
  it('클릭하면 값이 증가한다', () => {
    render(<Counter />);
    const button = screen.getByRole('button', { name: /count/i });
    fireEvent.click(button);
    expect(screen.getByText('1')).toBeInTheDocument();
  });
});

Tip

  • getByTestId보다 getByRole/getByLabelText를 우선 사용하면 접근성 문제도 같이 잡힌다
  • API 모킹은 MSW(Mock Service Worker)로 네트워크 레이어에서 처리한다

접근성

이 섹션은 접근성을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.

접근성은 나중에 추가하는 기능이 아니라 시맨틱 HTML을 쓰는 것만으로 대부분 해결됩니다.

Tip

  • div/span 대신 button, nav, main, header 등 시맨틱 태그를 사용한다
  • 이미지에는 항상 의미 있는 alt를 넣는다 (장식용이면 alt="")
  • 커스텀 드롭다운/모달은 role, aria-expanded, aria-modal, focus trap을 직접 챙겨야 한다
  • 키보드만으로 전체 플로우가 동작하는지 Tab/Shift+Tab으로 확인한다
  • 색상 대비는 WCAG AA 기준 4.5:1 이상을 유지한다

다음 단계

이 섹션은 다음 단계을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.

🚀
컴포넌트 기본기를 마쳤다면?

서버 렌더링, App Router, 서버 액션, ISR 등 프로덕션급 React 애플리케이션 구축은 Next.js 가이드에서 이어집니다. 타입 안전한 대규모 코드베이스가 필요하다면 TypeScript 가이드를, 언어 기초를 더 다지고 싶다면 JavaScript 가이드를 함께 참고하세요.

연계 가이드: Next.js 가이드 · TypeScript 가이드 · JavaScript 가이드 · Vue 가이드

React 실무 설계

React 실무 설계은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

React는 component tree보다 data flow 설계가 중요합니다. server state, client state, form state를 분리하고 component 책임을 작게 유지해야 합니다.
결정 지점확인 질문실무 기준
경계React 코드에서 바뀌기 쉬운 부분은 어디인가?입출력, 설정, 외부 연동, 핵심 규칙을 분리합니다.
상태상태가 어디서 생성되고 어디서 사라지는가?상태 소유자와 수명 주기를 코드로 드러냅니다.
장애실패했을 때 호출자는 무엇을 받는가?timeout, fallback, error contract를 먼저 정합니다.

React 운영 기준

이 섹션은 React 운영 기준을 실무 관점에서 정리합니다. 개념을 외우기보다, 어떤 상황에서 이 기준을 꺼내 쓸지에 초점을 맞춰보세요.

렌더링 성능은 memo보다 불필요한 state 위치와 waterfall이 더 큰 영향을 줍니다. Suspense/loading/error 상태를 설계해야 합니다.

Tip

  • state ownership
  • server state cache
  • error boundary
  • a11y tests

React 검증 전략

React 검증 전략은 선택지가 갈리는 지점입니다. 표를 기준으로 각 방법의 쓰임새와 운영상의 차이를 비교해두면 이후 판단이 훨씬 쉬워집니다.

사용자 시나리오 기반 테스트와 accessibility test가 핵심입니다.
품질 축검증 방법완료 기준
정확성정상/실패 케이스를 자동화합니다.핵심 시나리오가 재현 가능하게 통과합니다.
회귀 방지버그 수정 시 동일 케이스를 테스트로 남깁니다.같은 장애가 다시 배포되지 않습니다.
운영성로그, 메트릭, 알림을 확인합니다.문제가 생겼을 때 원인 추적 경로가 있습니다.
← 이전 가이드TypeScript다음 가이드 →Next.js