C
React/상태관리/Lesson 07

useState

30분·theory
이 챕터
1/2
JavaScript

useState

💡 왜 배워야 할까요?

🎯 React에서 화면에 표시되는 값을 저장하고 변경하는 가장 기본적인 방법입니다.
💼 버튼 클릭, 입력창 입력 같은 사용자 상호작용에 반응하려면 필수입니다.
모든 React 개발자가 매일 사용하는 가장 중요한 기능입니다.
🏢 실무에서는
실무에서는 '좋아요' 버튼을 누르면 숫자가 증가하거나, 토글 버튼으로 메뉴를 열고 닫을 때 useState를 사용합니다. 쇼핑몰의 상품 수량 증감 버튼도 모두 useState로 만들어집니다.

useState — 컴포넌트의 기억, 값이 바뀌면 화면도 바뀐다

실생활 비유: 칠판

컴포넌트가 칠판을 갖고 있습니다.

  • state: 칠판에 적힌 값
  • setState: 칠판을 지우고 새 값을 쓰는 행위
  • 칠판이 바뀌면 → 화면이 자동으로 다시 그려집니다

왜 배우나요?

일반 변수를 바꿔도 화면이 갱신되지 않습니다.

useState로 관리해야 React가 변화를 감지하고 리렌더링합니다.

기본 사용법

js
const [state, setState] = useState(초기값);
// state: 현재 값
// setState: 값을 바꾸는 함수

주의사항

  • setState 없이 state 직접 수정 금지 → 화면 갱신 안됨
  • setState는 비동기 → 직후에 state 값이 바뀌지 않을 수 있음
  • 배열/객체는 새 참조로 교체해야 함 (불변성)
💻 카운터 + 토글 + 입력폼 예제
import { useState } from 'react';

// ===== 카운터 컴포넌트 =====
function Counter() {
  // 입력: 초기값 0
  const [count, setCount] = useState(0);

  // 처리: 증가/감소/초기화
  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(count - 1)}>-1</button>
      <button onClick={() => setCount(0)}>초기화</button>
    </div>
  );
}

// ===== 토글 컴포넌트 =====
function Toggle() {
  const [isOn, setIsOn] = useState(false);

  return (
    <button onClick={() => setIsOn(!isOn)}>
      {isOn ? "🟢 ON" : "⚫ OFF"}
    </button>
  );
}

// ===== 입력폼 컴포넌트 =====
function NameForm() {
  const [name, setName] = useState("");

  // 출력: 실시간 입력값 표시
  return (
    <div>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="이름을 입력하세요"
      />
      <p>{name ? `안녕하세요, ${name}님!` : "이름을 입력해주세요"}</p>
    </div>
  );
}
💻 2025 권장 패턴 - 함수형 업데이트와 불변성 보장
// ✅ 좋은 예시 - 함수형 업데이트, 지연 초기화, 불변성
import { useState, useCallback } from 'react';
import { produce } from 'immer'; // 2025년 표준 불변성 라이브러리

interface User {
  id: number;
  name: string;
  settings: { theme: string; notifications: boolean; };
}

function GoodExample() {
  // 지연 초기화 - 함수로 전달하면 첫 렌더링에만 실행
  const [count, setCount] = useState(() => {
    console.log('초기화 한 번만 실행됨');
    return expensiveCalculation();
  });
  
  const [user, setUser] = useState<User>(() => ({
    id: 1,
    name: 'John',
    settings: { theme: 'light', notifications: true }
  }));

  // 함수형 업데이트로 클로저 문제 해결
  const handleMultipleClicks = useCallback(() => {
    setTimeout(() => {
      setCount(prevCount => prevCount + 1); // 항상 최신 상태 기반
      setCount(prevCount => prevCount + 1); // 정확히 +2 됨
    }, 1000);
  }, []);

  // immer를 사용한 불변성 업데이트 (2025 권장)
  const toggleNotifications = useCallback(() => {
    setUser(prevUser => 
      produce(prevUser, draft => {
        draft.settings.notifications = !draft.settings.notifications;
      })
    );
  }, []);

  // 또는 전개 연산자 방식
  const toggleTheme = useCallback(() => {
    setUser(prevUser => ({
      ...prevUser,
      settings: {
        ...prevUser.settings,
        theme: prevUser.settings.theme === 'light' ? 'dark' : 'light'
      }
    }));
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Theme: {user.settings.theme}</p>
      <p>Notifications: {user.settings.notifications ? 'On' : 'Off'}</p>
      <button onClick={handleMultipleClicks}>Add 2</button>
      <button onClick={toggleNotifications}>Toggle Notifications</button>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}

function expensiveCalculation(): number {
  return Array.from({length: 1000000}, (_, i) => i).reduce((a, b) => a + b, 0);
}

💡 ⚠️ 흔한 실수

  • useState 초기값에 함수 호출 결과를 직접 전달해서 매 렌더링마다 불필요한 연산 수행
  • 이전 상태에 의존하는 업데이트에서 함수형 업데이트 대신 현재 상태 직접 참조로 인한 클로저 문제
  • 객체나 배열 상태를 직접 변이(mutate)시켜서 React가 변화를 감지하지 못하는 문제

💡 🎯 면접 준비

Q: useState에서 함수형 업데이트가 필요한 상황과 그 이유는?
Q: React에서 상태 불변성이 중요한 이유와 구현 방법은?

힌트: 함수형 업데이트는 클로저 문제 해결과 이전 상태 기반 안전한 업데이트를 위해 필요하며, 불변성은 React의 얕은 비교 최적화와 예측 가능한 상태 관리를 위해 중요합니다. immer나 전개 연산자 등의 구체적 구현 방법도 언급하세요.

⚛️ React 패턴 — useState

useState을 React에서 어떻게 쓰는지 코드와 함께 단계별로 익혀보세요.
1 📦 1. State 선언
useState로 컴포넌트 내부 상태 만들기
const [count, setCount] = useState(0);
2 👁️ 2. 렌더링
state 값을 JSX에 표시
return <h1>현재 카운트: {count}</h1>;
3 🔄 3. 업데이트
setState 호출 → 리렌더링 트리거
<button onClick={() => setCount(count + 1)}>+1</button>
4 💡 4. 핵심 원칙
state는 immutable — 직접 수정 X, 항상 setter로

✨ useState — 카운터 만들기

코드를 직접 수정하면 0.7초 후 자동 반영됩니다. React 18 + Babel로 브라우저에서 즉시 실행.
🖥️ 실행 결과 — 렌더링된 React 컴포넌트
✏️ React 코드 수정하기 (클릭해서 열기)
⚛️ React 18 + Babel Standalone — 결과 먼저 확인 후 코드 편집기에서 직접 수정해볼 수 있습니다.

확인 퀴즈

setState를 사용하지 않고 state를 직접 변경하면?
💡 state를 직접 변경하면 메모리의 값은 바뀌지만, React는 변화를 감지하지 못해 화면이 갱신되지 않습니다. 반드시 setState 함수를 통해야 리렌더링이 트리거됩니다!
먼저 읽으면 좋은 개념: 리스트와 key
다음 추천: 폼 처리
useState - React