서론: 왜 React인가?

2026년 현재, 프론트엔드 개발의 세계에서 React는 여전히 압도적인 존재감을 보여주고 있습니다. Stack Overflow 2025 개발자 설문조사에 따르면, React는 전 세계 프론트엔드 프레임워크 점유율에서 약 42%를 차지하며 1위를 유지하고 있습니다. 그 뒤를 Vue.js(약 18%), Angular(약 15%), Svelte(약 8%)가 따르고 있지만, React와의 격차는 여전히 상당합니다. 국내 채용 시장에서도 React 관련 포지션이 프론트엔드 채용 공고의 60% 이상을 차지할 만큼, React는 프론트엔드 개발자의 필수 기술로 자리 잡았습니다.

React를 배워야 하는 이유는 단순히 점유율이 높기 때문만이 아닙니다. React를 둘러싼 방대한 생태계(Ecosystem)가 개발자에게 거의 무한한 가능성을 제공합니다. Next.js, React Native, Remix 등 React 기반의 메타 프레임워크와 모바일 개발 도구들이 활발히 발전하고 있으며, npm에 등록된 React 관련 패키지는 수만 개에 달합니다. 또한, 전 세계에서 가장 활발한 개발자 커뮤니티 중 하나를 보유하고 있어, 문제 해결을 위한 자료와 도움을 쉽게 얻을 수 있습니다.

이 글에서는 React를 처음 접하는 비전공자도 이해할 수 있도록, React의 핵심 개념부터 실전 프로젝트까지 단계별로 안내합니다. 이 글을 끝까지 따라오면, React의 기본 원리를 이해하고, 간단한 웹 애플리케이션을 직접 만들 수 있게 될 것입니다.

1. React 기초 개념

1.1 React란?

React는 Facebook(현 Meta)이 2013년에 오픈소스로 공개한 사용자 인터페이스(UI) 구축을 위한 JavaScript 라이브러리입니다. 정확히 말하면 React는 프레임워크가 아닌 라이브러리입니다. Angular처럼 라우팅, 상태 관리, HTTP 통신 등 모든 것을 내장하고 있는 것이 아니라, UI 렌더링이라는 한 가지 목적에 집중하며, 나머지 기능은 서드파티 라이브러리를 조합하여 사용합니다. 이 철학 덕분에 React는 가볍고, 유연하며, 다양한 프로젝트에 적용할 수 있습니다.

React를 이해하기 위해 반드시 알아야 할 두 가지 핵심 개념이 있습니다:

  • Virtual DOM(가상 돔): 브라우저의 실제 DOM(Document Object Model)을 직접 조작하는 것은 성능이 좋지 않습니다. React는 메모리 상에 가상의 DOM 트리를 유지하고, 상태가 변경되면 가상 DOM에서 먼저 변경 사항을 계산한 뒤, 실제 DOM에는 최소한의 변경만 적용합니다. 이 과정을 재조정(Reconciliation)이라 하며, 이를 통해 빠르고 효율적인 UI 업데이트가 가능합니다.
  • 컴포넌트 기반 아키텍처: React에서는 UI를 독립적이고 재사용 가능한 작은 단위인 컴포넌트(Component)로 나누어 구축합니다. 버튼 하나, 검색창 하나, 네비게이션 바 하나가 각각 컴포넌트가 될 수 있으며, 이 컴포넌트들을 조합하여 복잡한 화면을 만듭니다. 레고 블록을 조립하는 것과 비슷한 개념입니다.

1.2 SPA(Single Page Application) 이해

React로 만드는 웹 애플리케이션은 대부분 SPA(Single Page Application) 방식으로 동작합니다. 전통적인 웹사이트는 페이지를 이동할 때마다 서버에서 완전히 새로운 HTML 페이지를 받아와 전체 화면을 다시 그리지만, SPA는 최초 한 번만 전체 페이지를 로드하고, 이후에는 필요한 데이터만 서버에서 받아와 화면의 일부분만 동적으로 업데이트합니다. 이 덕분에 사용자는 마치 데스크톱 앱을 사용하는 것처럼 빠르고 부드러운 경험을 할 수 있습니다.

Gmail, Facebook, Instagram, Netflix 등 우리가 일상적으로 사용하는 대부분의 모던 웹 서비스가 SPA 방식으로 구축되어 있습니다. SPA의 핵심 장점은 페이지 전환 시 깜빡임 없이 부드러운 사용자 경험을 제공한다는 것이며, 단점은 초기 로딩 시간이 길 수 있고 검색 엔진 최적화(SEO)에 추가적인 노력이 필요하다는 것입니다. 후자의 문제는 Next.js 같은 프레임워크가 서버 사이드 렌더링(SSR)을 통해 해결해 주고 있습니다.

1.3 React vs Vue vs Angular 간단 비교

프론트엔드 개발을 시작할 때 어떤 기술을 선택할지 고민하는 경우가 많습니다. 세 가지 주요 프레임워크/라이브러리를 간단히 비교해 보겠습니다:

항목 React Vue.js Angular
개발사 Meta (Facebook) Evan You (커뮤니티) Google
유형 라이브러리 프레임워크 프레임워크
학습 곡선 중간 낮음 높음
생태계 매우 넓음 넓음 넓음 (내장 기능 많음)
언어 JavaScript / JSX JavaScript / SFC TypeScript
채용 시장 가장 많음 증가 추세 대기업 중심
대표 메타 프레임워크 Next.js, Remix Nuxt.js Angular Universal

결론적으로, React는 채용 시장, 생태계, 유연성 측면에서 가장 범용적인 선택이며, 첫 프론트엔드 기술로 배우기에 적합합니다. Vue.js는 진입 장벽이 가장 낮아 빠르게 시작하고 싶은 분들에게, Angular는 대규모 엔터프라이즈 프로젝트에서 체계적인 구조가 필요한 경우에 적합합니다.

2. 개발 환경 세팅

2.1 Node.js 설치

React 개발을 시작하려면 먼저 Node.js를 설치해야 합니다. Node.js는 JavaScript를 브라우저 밖에서 실행할 수 있게 해주는 런타임 환경이며, React 프로젝트의 빌드 도구와 패키지 관리자(npm)를 사용하기 위해 필수적입니다.

Node.js 공식 사이트에 접속하여 LTS(Long Term Support) 버전을 다운로드하고 설치합니다. 설치가 완료되면 터미널(명령 프롬프트)에서 아래 명령어로 정상 설치 여부를 확인합니다:

# Node.js 버전 확인
node -v
# 예시 출력: v22.x.x

# npm 버전 확인
npm -v
# 예시 출력: 10.x.x

2.2 프로젝트 생성

2026년 현재, React 프로젝트를 생성하는 표준적인 방법은 Vite를 사용하는 것입니다. 과거에는 Create React App(CRA)이 공식 도구로 권장되었지만, 현재는 유지보수가 중단되었으며, React 공식 문서에서도 Vite를 추천하고 있습니다. Vite는 빌드 속도가 매우 빠르고, 개발 서버의 Hot Module Replacement(HMR)가 거의 즉시 반영되어 개발 경험이 탁월합니다.

# Vite를 사용하여 React 프로젝트 생성
npx create-vite@latest my-first-react-app --template react

# 프로젝트 폴더로 이동
cd my-first-react-app

# 의존성 패키지 설치
npm install

# 개발 서버 실행
npm run dev

위 명령어를 실행하면, http://localhost:5173 주소에서 React 앱이 실행됩니다. 브라우저에서 React 로고와 함께 기본 화면이 표시되면 성공입니다. 생성된 프로젝트의 폴더 구조는 다음과 같습니다:

my-first-react-app/
├── node_modules/       # 설치된 패키지들 (직접 수정하지 않음)
├── public/             # 정적 파일 (favicon 등)
├── src/                # 소스 코드 (여기서 개발)
│   ├── App.css         # App 컴포넌트 스타일
│   ├── App.jsx         # 메인 App 컴포넌트
│   ├── index.css       # 전역 스타일
│   └── main.jsx        # 앱 진입점 (Entry Point)
├── index.html          # HTML 템플릿
├── package.json        # 프로젝트 설정 및 의존성 목록
└── vite.config.js      # Vite 설정 파일

가장 중요한 폴더는 src/입니다. 앞으로의 모든 개발 작업은 이 폴더 안에서 이루어집니다. main.jsx가 앱의 시작점이며, 이 파일이 App.jsx 컴포넌트를 렌더링합니다.

2.3 VS Code 추천 확장 프로그램

React 개발을 쾌적하게 만들어 주는 VS Code 확장 프로그램을 소개합니다:

  • ES7+ React/Redux/React-Native snippets: rfce만 입력하면 함수 컴포넌트의 기본 구조가 자동 생성됩니다. 코드 작성 속도를 비약적으로 높여줍니다.
  • Prettier - Code formatter: 코드를 저장할 때마다 자동으로 포맷팅해 줍니다. 팀 프로젝트에서 코드 스타일을 통일하는 데 필수적입니다.
  • ESLint: JavaScript/JSX 코드의 문법 오류와 잠재적 버그를 실시간으로 감지하여 알려줍니다.
  • Auto Rename Tag: HTML/JSX에서 여는 태그를 수정하면 닫는 태그도 자동으로 함께 수정됩니다.
  • Thunder Client: VS Code 내에서 API 요청을 테스트할 수 있는 경량 REST 클라이언트입니다.

3. React 핵심 문법

3.1 JSX - HTML과 JavaScript의 만남

JSX(JavaScript XML)는 React에서 UI를 작성할 때 사용하는 문법으로, HTML과 JavaScript를 결합한 형태입니다. 처음 보면 낯설 수 있지만, 익숙해지면 UI 구조를 직관적으로 표현할 수 있어 매우 편리합니다. JSX는 브라우저가 직접 이해할 수 없으므로, Vite(내부적으로 Babel/SWC 사용)가 일반 JavaScript로 변환(트랜스파일)해 줍니다.

// JSX 기본 문법 예시
function Greeting() {
  const userName = "홍길동";
  const isLoggedIn = true;

  return (
    <div className="greeting">
      {/* JSX 안에서 JavaScript 표현식 사용: 중괄호 {} */}
      <h1>안녕하세요, {userName}님!</h1>
      <p>현재 시간: {new Date().toLocaleTimeString()}</p>

      {/* 조건부 렌더링: 삼항 연산자 */}
      {isLoggedIn ? (
        <p>환영합니다! 로그인 상태입니다.</p>
      ) : (
        <p>로그인이 필요합니다.</p>
      )}

      {/* 조건부 렌더링: && 연산자 */}
      {isLoggedIn && <button>로그아웃</button>}
    </div>
  );
}

JSX를 작성할 때 주의해야 할 몇 가지 규칙이 있습니다:

  • 반드시 하나의 루트 요소로 감싸야 합니다. 여러 요소를 반환하려면 <div><>...</>(Fragment)로 감쌉니다.
  • HTML의 class 대신 className을 사용합니다 (JavaScript에서 class는 예약어이기 때문).
  • HTML의 for 대신 htmlFor를 사용합니다.
  • 모든 태그는 반드시 닫아야 합니다 (<img />, <br />).
  • JavaScript 표현식은 {} 중괄호 안에 작성합니다.

3.2 컴포넌트

React에서 컴포넌트는 UI를 구성하는 독립적이고 재사용 가능한 코드 조각입니다. 2026년 현재, React 컴포넌트는 함수 컴포넌트(Function Component) 방식으로 작성하는 것이 표준입니다. 과거의 클래스 컴포넌트는 레거시 프로젝트에서만 볼 수 있으며, 새로운 프로젝트에서는 사용하지 않습니다.

// 기본 함수 컴포넌트
function Welcome() {
  return <h1>안녕하세요, React!</h1>;
}

// props를 받는 컴포넌트
function UserCard({ name, email, role }) {
  return (
    <div className="user-card">
      <h2>{name}</h2>
      <p>이메일: {email}</p>
      <p>역할: {role}</p>
    </div>
  );
}

// children을 사용하는 레이아웃 컴포넌트
function Card({ title, children }) {
  return (
    <div className="card">
      <div className="card-header">
        <h3>{title}</h3>
      </div>
      <div className="card-body">
        {children}
      </div>
    </div>
  );
}

// 컴포넌트 조합하여 사용
function App() {
  return (
    <div>
      <Welcome />
      <Card title="사용자 정보">
        <UserCard
          name="김개발"
          email="dev@example.com"
          role="프론트엔드 개발자"
        />
      </Card>
    </div>
  );
}

props(Properties)는 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하는 방법입니다. 함수의 매개변수처럼 동작하며, 자식 컴포넌트 내부에서 props를 직접 수정할 수 없습니다(읽기 전용). children은 특별한 props로, 컴포넌트의 여는 태그와 닫는 태그 사이에 넣은 내용이 전달됩니다.

3.3 State와 이벤트

State(상태)는 컴포넌트 내부에서 관리되는 동적 데이터입니다. 사용자의 입력, 서버에서 받아온 데이터, UI의 현재 상태(열림/닫힘 등) 등이 state에 해당합니다. React에서는 useState Hook을 사용하여 state를 관리합니다. state가 변경되면 React는 자동으로 해당 컴포넌트를 다시 렌더링하여 화면을 업데이트합니다.

import { useState } from 'react';

function Counter() {
  // useState: [현재 상태값, 상태를 변경하는 함수]
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);

  const increment = () => setCount(count + step);
  const decrement = () => setCount(count - step);
  const reset = () => setCount(0);

  return (
    <div className="counter">
      <h2>카운터 앱</h2>
      <p className="count-display">현재 값: {count}</p>

      <div className="controls">
        <label>
          증감 단위:
          <input
            type="number"
            value={step}
            onChange={(e) => setStep(Number(e.target.value))}
            min="1"
          />
        </label>
      </div>

      <div className="buttons">
        <button onClick={decrement}>-{step}</button>
        <button onClick={reset}>초기화</button>
        <button onClick={increment}>+{step}</button>
      </div>
    </div>
  );
}

export default Counter;

위 예시에서 useState(0)은 초기값이 0인 state를 생성합니다. count는 현재 상태값이고, setCount는 상태를 업데이트하는 함수입니다. 버튼의 onClick 이벤트에 핸들러 함수를 연결하여, 클릭 시 state가 변경되고 화면이 자동으로 갱신됩니다. onChange 이벤트는 입력 필드의 값이 변경될 때 호출됩니다.

3.4 useEffect - 사이드 이펙트 관리

useEffect는 컴포넌트의 렌더링과 직접 관련이 없는 '부수 효과(Side Effect)'를 처리하기 위한 Hook입니다. API 호출, 타이머 설정, 이벤트 리스너 등록, DOM 직접 조작 등이 사이드 이펙트에 해당합니다.

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // API 호출 (비동기 처리)
    const fetchUser = async () => {
      try {
        setLoading(true);
        const response = await fetch(
          `https://jsonplaceholder.typicode.com/users/${userId}`
        );
        if (!response.ok) throw new Error('사용자를 찾을 수 없습니다');
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();

    // 클린업 함수: 컴포넌트가 언마운트되거나 의존성이 변경될 때 실행
    return () => {
      console.log('정리 작업 실행');
    };
  }, [userId]); // 의존성 배열: userId가 변경될 때마다 useEffect 재실행

  if (loading) return <p>로딩 중...</p>;
  if (error) return <p>오류: {error}</p>;
  if (!user) return null;

  return (
    <div>
      <h2>{user.name}</h2>
      <p>이메일: {user.email}</p>
      <p>전화: {user.phone}</p>
    </div>
  );
}

useEffect의 두 번째 인자인 의존성 배열(Dependency Array)은 매우 중요합니다. 빈 배열 []을 전달하면 컴포넌트가 처음 마운트될 때 한 번만 실행되고, 특정 값을 넣으면 해당 값이 변경될 때마다 재실행됩니다. 의존성 배열을 생략하면 매 렌더링마다 실행되므로 주의해야 합니다.

3.5 조건부 렌더링과 리스트

실제 앱에서는 조건에 따라 다른 UI를 보여주거나, 배열 데이터를 목록으로 표시하는 경우가 매우 많습니다.

function FruitList() {
  const fruits = [
    { id: 1, name: "사과", emoji: "🍎", inStock: true },
    { id: 2, name: "바나나", emoji: "🍌", inStock: true },
    { id: 3, name: "포도", emoji: "🍇", inStock: false },
    { id: 4, name: "딸기", emoji: "🍓", inStock: true },
  ];

  return (
    <div>
      <h2>과일 목록</h2>
      <ul>
        {fruits.map((fruit) => (
          <li key={fruit.id}>
            {fruit.emoji} {fruit.name}
            {fruit.inStock ? (
              <span style={{ color: "green" }}> - 재고 있음</span>
            ) : (
              <span style={{ color: "red" }}> - 품절</span>
            )}
          </li>
        ))}
      </ul>
      <p>재고 있는 과일: {fruits.filter(f => f.inStock).length}개</p>
    </div>
  );
}

map()은 배열의 각 요소를 컴포넌트로 변환하여 리스트를 렌더링하는 데 사용합니다. 이때 각 요소에 고유한 key prop을 반드시 전달해야 합니다. React는 이 key를 사용하여 어떤 항목이 추가, 삭제, 변경되었는지 효율적으로 파악합니다. key로는 배열의 인덱스 대신 데이터 고유의 ID를 사용하는 것이 좋습니다.

4. 실전 프로젝트: 할일(Todo) 앱 만들기

4.1 프로젝트 설계

지금까지 배운 React 핵심 개념을 모두 활용하여, 실전 할일(Todo) 앱을 만들어 보겠습니다. 이 프로젝트를 통해 컴포넌트 설계, state 관리, 이벤트 처리, 리스트 렌더링, 조건부 렌더링을 종합적으로 연습할 수 있습니다. 구현할 기능은 다음과 같습니다:

  • 새로운 할일 추가
  • 할일 완료/미완료 토글
  • 할일 삭제
  • 남은 할일 개수 표시

4.2 컴포넌트 구조

Todo 앱의 컴포넌트를 다음과 같이 분리하여 설계합니다:

App (최상위 컴포넌트 - 전체 state 관리)
├── TodoForm (할일 입력 폼)
├── TodoList (할일 목록 컨테이너)
│   └── TodoItem (개별 할일 항목) x N
└── TodoFooter (남은 할일 수 표시)

4.3 상태 관리

이 앱에서 관리해야 할 state는 할일 목록 배열(todos)입니다. 각 할일 항목은 id, text(내용), completed(완료 여부)를 가집니다. 모든 CRUD 기능은 App 컴포넌트에서 state를 변경하는 함수로 구현하고, 이를 자식 컴포넌트에 props로 전달합니다.

4.4 CRUD 기능 구현

아래는 Todo 앱의 전체 소스 코드입니다. 실제로 src/App.jsx 파일에 아래 코드를 입력하고 저장하면, 브라우저에서 바로 동작하는 것을 확인할 수 있습니다:

// src/App.jsx - 할일(Todo) 앱 전체 코드
import { useState } from 'react';

// ─── TodoForm 컴포넌트 ─────────────────────────
function TodoForm({ onAdd }) {
  const [inputValue, setInputValue] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault(); // 폼 기본 동작(페이지 새로고침) 방지
    const trimmed = inputValue.trim();
    if (!trimmed) return; // 빈 값 방지
    onAdd(trimmed);
    setInputValue(''); // 입력 필드 초기화
  };

  return (
    <form onSubmit={handleSubmit} className="todo-form">
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="할일을 입력하세요..."
      />
      <button type="submit">추가</button>
    </form>
  );
}

// ─── TodoItem 컴포넌트 ─────────────────────────
function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <li className={`todo-item ${todo.completed ? 'completed' : ''}`}>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => onToggle(todo.id)}
      />
      <span
        style={{
          textDecoration: todo.completed ? 'line-through' : 'none',
          color: todo.completed ? '#999' : '#333',
        }}
      >
        {todo.text}
      </span>
      <button onClick={() => onDelete(todo.id)}>삭제</button>
    </li>
  );
}

// ─── TodoList 컴포넌트 ─────────────────────────
function TodoList({ todos, onToggle, onDelete }) {
  if (todos.length === 0) {
    return <p className="empty-message">등록된 할일이 없습니다.</p>;
  }

  return (
    <ul className="todo-list">
      {todos.map((todo) => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
}

// ─── App 컴포넌트 (메인) ─────────────────────────
function App() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'React 기초 문법 공부하기', completed: false },
    { id: 2, text: 'Todo 앱 만들기', completed: false },
    { id: 3, text: 'Git에 프로젝트 올리기', completed: false },
  ]);

  // 할일 추가 (Create)
  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(), // 고유 ID 생성
      text,
      completed: false,
    };
    setTodos([...todos, newTodo]);
  };

  // 완료 토글 (Update)
  const toggleTodo = (id) => {
    setTodos(
      todos.map((todo) =>
        todo.id === id
          ? { ...todo, completed: !todo.completed }
          : todo
      )
    );
  };

  // 할일 삭제 (Delete)
  const deleteTodo = (id) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  // 남은 할일 수 계산
  const remainingCount = todos.filter((t) => !t.completed).length;

  return (
    <div className="todo-app">
      <h1>나의 할일 목록</h1>
      <TodoForm onAdd={addTodo} />
      <TodoList
        todos={todos}
        onToggle={toggleTodo}
        onDelete={deleteTodo}
      />
      <p className="todo-footer">
        남은 할일: <strong>{remainingCount}</strong>개
      </p>
    </div>
  );
}

export default App;
코드 핵심 포인트 정리

1. 불변성(Immutability): state 배열을 직접 수정(push, splice 등)하지 않고, 항상 새로운 배열을 생성하여 setTodos에 전달합니다. 스프레드 연산자(...), map(), filter()를 활용합니다.

2. 단방향 데이터 흐름: state는 App에서 관리하고, 자식 컴포넌트에는 props로 데이터와 함수를 전달합니다. 자식은 전달받은 함수를 호출하여 부모의 state를 변경합니다.

3. key의 중요성: map()으로 리스트를 렌더링할 때, 각 항목에 고유한 key를 부여하여 React가 효율적으로 DOM을 업데이트하도록 합니다.

5. React 생태계 한눈에 보기

React 자체는 UI 렌더링에 집중하는 라이브러리이므로, 실제 프로젝트를 진행할 때는 다양한 서드파티 라이브러리를 함께 사용합니다. React를 둘러싼 풍부한 생태계를 카테고리별로 정리합니다.

카테고리 주요 도구 특징 추천 대상
라우팅 React Router v7 SPA에서 페이지 이동 구현, 중첩 라우트 지원 모든 SPA 프로젝트
상태 관리 Zustand 가볍고 직관적, 보일러플레이트 최소 중소규모 프로젝트 (현재 가장 인기)
Redux Toolkit 예측 가능한 상태 관리, 풍부한 미들웨어 대규모 엔터프라이즈 프로젝트
Jotai / Recoil 원자(Atom) 기반, React 친화적 복잡한 비동기 상태 관리
스타일링 Tailwind CSS 유틸리티 퍼스트, 빠른 프로토타이핑 대부분의 프로젝트 (현재 가장 인기)
styled-components CSS-in-JS, 컴포넌트 단위 스타일 캡슐화 디자인 시스템 구축
데이터 페칭 TanStack Query (React Query) 서버 상태 관리, 캐싱, 자동 리페치 API 통신이 많은 프로젝트
폼 관리 React Hook Form + Zod 성능 최적화, 스키마 기반 유효성 검사 폼이 복잡한 관리자 페이지
서버 사이드 프레임워크 Next.js SSR, SSG, API 라우트, 파일 기반 라우팅 SEO가 중요한 프로젝트, 풀스택 개발
Remix 웹 표준 중심, 프로그레시브 향상 웹 표준에 충실한 프로젝트
테스트 Vitest + Testing Library Vite 기반 빠른 테스트, 사용자 관점 테스트 모든 프로젝트 (테스트는 필수)

입문 단계에서는 위 도구들을 모두 배울 필요가 없습니다. 우선 React 자체를 충분히 익히고, 프로젝트의 필요에 따라 하나씩 추가해 나가면 됩니다. 첫 번째 외부 도구로는 React Router(페이지 이동)와 Tailwind CSS(스타일링)를 추천합니다.

6. 학습 로드맵 및 다음 단계

6.1 4주 학습 로드맵

React를 체계적으로 학습하기 위한 4주 로드맵을 제안합니다. 하루 1~2시간 학습을 기준으로 합니다:

주차 학습 주제 목표 실습 프로젝트
1주차 JavaScript ES6+, React 기초 화살표 함수, 구조 분해, 스프레드 연산자, JSX, 컴포넌트, props 이해 자기소개 카드 만들기
2주차 State, 이벤트, useEffect useState, 이벤트 핸들링, 사이드 이펙트 관리, 조건부 렌더링, 리스트 카운터 앱, Todo 앱
3주차 React Router, API 연동 페이지 라우팅, fetch/axios, 로딩/에러 처리 영화 검색 앱, 날씨 앱
4주차 스타일링, 배포 Tailwind CSS, 반응형 디자인, Vercel/Netlify 배포 포트폴리오 사이트 제작 및 배포
주의: JavaScript 기초를 먼저 익히세요!
React를 배우기 전에 JavaScript의 기본 문법, 특히 ES6+ 문법(화살표 함수, 구조 분해 할당, 템플릿 리터럴, 배열 메서드 map/filter/reduce, Promise, async/await)을 충분히 이해하고 있어야 합니다. JavaScript 기초 없이 React에 바로 뛰어들면, React 자체의 어려움이 아니라 JavaScript 문법 때문에 막히는 경우가 많습니다.

6.2 추천 학습 리소스

React 학습에 도움이 되는 리소스를 우선순위별로 정리합니다:

  • React 공식 문서 (react.dev): 2023년에 완전히 새로 작성된 공식 문서는 인터랙티브 튜토리얼과 풍부한 예시를 포함하고 있어, 가장 좋은 학습 자료입니다. 한국어 번역도 제공됩니다. 반드시 첫 번째 학습 자료로 활용하세요.
  • 무료 온라인 강좌: Nomad Coders(한국어), freeCodeCamp(영어), Scrimba(영어, 인터랙티브) 등에서 양질의 무료 React 강좌를 제공합니다.
  • 유료 강좌: Udemy, Inflearn 등에서 체계적이고 깊이 있는 React 강좌를 수강할 수 있습니다. 실전 프로젝트 기반의 강좌를 선택하는 것이 좋습니다.
  • 서적: 「모던 리액트 Deep Dive」(김용찬 저) - React의 내부 동작 원리까지 깊이 있게 다루는 국내 서적으로, 기초를 넘어 심화 학습에 적합합니다.
  • 커뮤니티: GitHub, Stack Overflow, Discord(Reactiflux), 국내 오픈카톡방 등에서 다른 개발자들과 소통하며 함께 성장할 수 있습니다.

6.3 포트폴리오 프로젝트 아이디어

React 기초를 익힌 후, 포트폴리오에 포함할 수 있는 프로젝트 아이디어를 난이도별로 제안합니다:

  • 초급: 날씨 앱 (OpenWeatherMap API 활용), 메모장 앱 (localStorage 저장), 타이머/스톱워치 앱, 계산기 앱
  • 중급: 영화 정보 검색 앱 (TMDB API 활용, 무한 스크롤), 블로그 플랫폼 (마크다운 에디터, 카테고리 분류), 가계부 앱 (차트 라이브러리 활용), 채팅 앱 (WebSocket 또는 Firebase)
  • 고급: 이커머스 쇼핑몰 (장바구니, 결제 연동, 관리자 페이지), 프로젝트 관리 도구 (칸반 보드, 드래그 앤 드롭), 소셜 미디어 피드 (무한 스크롤, 이미지 업로드, 댓글)

프로젝트를 진행할 때는 다음 사항을 지키면 포트폴리오 품질이 크게 높아집니다:

  • GitHub에 코드를 올리고, README.md를 정성껏 작성하세요 (프로젝트 소개, 기술 스택, 설치 방법, 스크린샷 포함).
  • Vercel 또는 Netlify에 배포하여 실제로 접속 가능한 URL을 제공하세요.
  • 반응형 디자인을 적용하여 모바일에서도 잘 보이도록 하세요.
  • 사소하더라도 테스트 코드를 작성하면 큰 차별점이 됩니다.

결론: React, 시작이 반이다

React는 처음에는 새로운 개념들(JSX, Virtual DOM, 컴포넌트, State, Hooks)이 많아 부담스럽게 느껴질 수 있습니다. 하지만 하나씩 차근차근 익혀나가면, React가 왜 전 세계 개발자들에게 사랑받는 도구인지 자연스럽게 깨닫게 될 것입니다. React의 선언적 프로그래밍 방식과 컴포넌트 기반 아키텍처는 복잡한 UI를 체계적으로 관리할 수 있는 강력한 기반을 제공합니다.

무엇보다 중요한 것은 직접 코드를 작성하며 배우는 것입니다. 이론만 공부해서는 절대 프로그래밍을 익힐 수 없습니다. 이 글에서 다룬 카운터 앱, Todo 앱 코드를 직접 타이핑하고 실행해 보세요. 코드를 수정하며 결과가 어떻게 변하는지 실험해 보세요. 오류가 발생하면 오류 메시지를 읽고, 검색하고, 해결해 보세요. 이 과정 자체가 가장 효과적인 학습입니다.

2026년 현재, React는 단순한 UI 라이브러리를 넘어 프론트엔드 개발의 표준이자, Next.js를 통해 풀스택 개발까지 아우르는 거대한 생태계로 성장했습니다. 지금 React를 시작한다면, 여러분은 가장 넓은 가능성의 문 앞에 서 있는 것입니다.

오늘 시작하는 React 3단계

1단계: Node.js를 설치하고, npx create-vite@latest my-app --template react로 첫 프로젝트를 생성하세요. 5분이면 충분합니다.

2단계: 이 글의 카운터 앱 코드를 src/App.jsx에 직접 타이핑하고, 브라우저에서 동작하는 것을 확인하세요. 복사-붙여넣기가 아닌 직접 타이핑을 권장합니다.

3단계: Todo 앱 코드를 입력하고, 기능을 하나씩 추가해 보세요. 예를 들어 "할일 수정 기능", "완료된 할일 필터링", "로컬스토리지 저장" 등을 직접 구현해 보는 것이 최고의 학습 방법입니다.