React - State: 컴포넌트의 기억 저장소

Sally·2026년 2월 17일

컴포넌트는 상호 작용의 결과로 화면의 내용을 변경해야 하는 경우가 많다.

  • ex. 폼에 입력하면 입력 필드 업데이트, 이미지 캐러셀에서 "다음" 클릭 시 표시되는 이미지 변경 등
    이처럼 컴포넌트는 현재 입력값, 현재 이미지, 장바구니와 같은 것들을 기억 해야 하는데, React에서 이런 종류의 컴포넌트별 메모리를 state 라고 부른다.

일반 변수로 충분하지 않은 경우

import { sculptureList } from './data.js';

export default function Gallery() {
  let index = 0;

  function handleClick() {
    index = index + 1;
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

위 코드를 이용한 컴포넌트로 "Next" 버튼을 클릭해도 작동하지 않는다.
handleClick 이벤트 핸들러는 지역번수 index를 업데이트하고 있는데, 이러한 변화를 보이지 않게 하는 두 가지 이유가 있다.

  1. 지역 변수는 렌더링간에 유지되지 않는다. - React는 이 컴포넌트를 두 번쨰로 렌더링할 때 지역변수에 대한 변경사항은 고려하지 않고 처음부터 렌더링한다.
  2. 지역 변수를 변경해도 렌더링을 일으키지 않는다. - React는 새로운 데이터로 컴포넌트를 다시 렌더링해야한다는 것을 인식하지 못한다.

=> 컴포넌트를 새로운 데이터로 업데이트 하기 위해서는
1. 렌더링 사이에 데이터를 유지
2. React가 새로운 데이터로 컴포넌트를 렌더링하도록 유발
해야한다.

useState

useState 훅은 이 두 가지를 제공한다.
1. 렌더링 간에 데이터를 유지하기 위한 state 변수
2. 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 유발하는 state setter 함수

state 변수 추가하기

  1. state 변수를 추가하기 위해 파일 상단의 React에서 useState 를 가져온다.
import { useState } from 'react';
  1. 일반 변수에서 State로 전환
let index = 0;

다음과 같이 바꾼다.

const [index, setIndex] = useState(0);

index : state 변수 / setIndex : setter 함수

  1. handleClick 함수 정의 (이벤트 핸들러에서 상태 변경하기)
function handleClick() {
  setIndex(index + 1);
}

첫 번째 훅 만나기

React에서 useState와 같이 use로 시작하는 모든 함수를 훅이라고 한다.
훅은 React가 렌더링 중일 때만 사용할 수 있는 특별한 함수로, 이를 이용해 React의 다양한 기능을 연결할 수 있다.

📍 주의

  • 훅은 컴포넌트의 최상위 수준 또는 커스텀 훅에서만 호출할 수 있다.
    • 조건문, 반복문 또는 기타 중첩 함수 내부에서는 호출 불가
  • 훅은 함수이지만 컴포넌트의 필요에 대한 무조건적인 선언이라 생각하면 좋고, 파일 상단에서 모듈을 import 하는 것과 유사하게 컴포넌트 상단에서 React 기능을 사용한다.

useState에 대해서

const [index, setIndex] = useState(0);`
  • useState를 호출하는 것은 React에 이 컴포넌트가 무언가를 기억하기를 원한다고 말하는 것이다. (위의 경우 React가 index를 기억하기를 원함)

  • 이 쌍의 이름은 const [something, setSomething]과 같이 지정하는 것이 규칙 !

  • useState의 유일한 인수는 state 변수의 초깃값이다. 위 예시에서 index의 초깃값은 useState(0)에 의해 0으로 설정된다.

useState의 작동 방식
: 컴포넌트가 렌더링될 때마다, useState는 다음 두 개의 값을 포함하는 배열을 제공한다.

  1. 저장한 값을 가진 state 변수 (index)
  2. state 변수를 업데이트하고 React에 컴포넌트를 다시 렌더링하도록 유발하는 state setter 함수 (setIndex)

⚡️ 작동 방식
1. 컴포넌트 렌더링 : index의 초깃값으로 0을 전달했으므로 [0, setIndex]가 반환된다. React는 최신 state 값으로 0을 기억한다.
2. state 업데이트 : 사용자가 버튼을 클릭하면 setIndex(index + 1)을 호출한다. index0이므로 setIndex(1)이다. 이는 React에 index = 1 임을 기억하게 하고, 또 다른 렌더링을 유발한다.
3. 컴포넌트 두 번째 렌더링 : React는 여전히 useState(0)을 보지만, index1로 설정한 것을 기억하고 있기 때문에, 이번에는 [1, setIndex]를 반환함.
4. 계속 ../

컴포넌트에 여러 state 변수 지정하기

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}
  • 하나의 컴포넌트에 원하는 만큼 많은 타입의 state 변수를 가질 수 있다.
    • 위의 경우 숫자 타입 index와 불리언 타입 showMore 라는 두 개의 state 변수

React가 각각의 useState를 구분하는 방식

const [index, setIndex] = useState(0);
const [name, setName] = useState('');

Q) useState안에 index같은 식별자를 넘기지 않는데, React는 어떤 state가 누구 건지 어떻게 구분할까?
A) 호출 순서 로 구분한다.

useState(0);      // 1번째
useState('');     // 2번째
useState(false);  // 3번째

컴포넌트 안에서 React는 아래와 같은 배열을 가지고 있다.

state = [0, '', false]

그리고 렌더링 시작할 때, currentIndex = 0;로 시작하고,
useState가 호출될 때 내부적으로 아래와 같이 동작한다.
1. state[currentIndex] 반환
2. currentIndex ++
3. 다음 useState는 그 다음 칸을 사용

이러한 이유 때문에 최상위에서만 훅 호출 규칙이 중요한데, 만약

if (something) {
  useState(0);
}

이런 식으로 사용하면, 렌더링마다 호출 순서가 바뀔 수 있기 때문이다.

State는 격리되고 비공개로 유지된다

Gallery.js

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <section>
      <button onClick={handleNextClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </section>
  );
}
App.js
import Gallery from './Gallery.js';

export default function Page() {
  return (
    <div className="Page">
      <Gallery />
      <Gallery />
    </div>
  );
}
  • 동일한 컴포넌트를 두 번 렌더링한다면 각 복사본은 완전히 격리된 state를 갖는다. 즉, 그 중 하나를 변경해도 다른 하나에는 영향을 미치지 않는다.

  • 위의 예시에서 Gallery 컴포넌트가 두 번 렌더링되었는데, 각각 Gallery 내부 버튼을 클릭해보면 그들의 state가 서로 독립적임을 알 수 있다.

    • <Gallery/> 컴포넌트를 두 번 렌더링했으므로 그들의 state는 별도로 저장된다.

state는 선언한 컴포넌트 안에서만 접근 가능하다. - state의 캡슐화 (Encapsulation)

function Page() {
  return <Gallery />;
}

function Gallery() {
  const [index, setIndex] = useState(0);
}
  • Gallery 안에 index라는 state가 존재하지만, Page는 그것을 전혀 알지 못한다
  • 부모(Page)는 Gallery의 state를 읽을 수도 없고, 직접 바꿀 수도 없다

💥 vs Props

  • props는 부모 -> 자식으로 전달된다
function Page() {
  return <Gallery color="red" />;
}
  • color은 부모가 넘겨준 값으로, 부모가 컨트롤 가능하다는 의미이다

✔️ Props = 외부에서 들어오는 값
✔️ State = 그 컴포넌트만의 내부 기억

0개의 댓글