React – 상태 관리와 Hooks – State와 Props – 0 – State 정의 및 변경 (불변성 원칙)

React – 상태 관리와 Hooks – State와 Props – 0 – State 정의 및 변경 (불변성 원칙)

React – 상태 관리와 Hooks – State와 Props – 0 – State 정의 및 변경 (불변성 원칙)

안녕하세요! 😄
오늘은 React에서 컴포넌트의 내부 상태를 관리하는 핵심 개념, 바로 **State(상태)**에 대해 깊이 알아볼게요.
또한 React의 핵심 철학 중 하나인 **불변성(immutability)**에 대해서도 함께 살펴볼 거예요.

여러분이 만든 컴포넌트가 사용자와 상호작용하며 동적으로 변해야 한다면?
그 중심에는 항상 state가 있어요!


1. State란 무엇인가요?

State는 컴포넌트 내부에서 데이터를 저장하고 관리하는 공간이에요.

비유하자면, state는 컴포넌트의 기억력이에요.
버튼을 몇 번 눌렀는지, 입력창에 어떤 텍스트가 써졌는지 기억해야 하잖아요?
그걸 state가 대신 기억해주는 거랍니다.


2. State vs Props 비교

항목 props state
데이터 소유자 부모 컴포넌트 자신(자식) 컴포넌트
수정 가능 여부 읽기 전용 수정 가능
역할 외부에서 전달되는 데이터 내부에서 변화하는 데이터
사용 목적 전달과 구성 동적인 UI 관리

3. useState 훅을 이용한 상태 정의

React에서 상태는 **useState라는 훅(Hook)**을 이용해서 정의합니다.

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // count의 초기값은 0

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

설명

  • useState(0) → 초기값 0으로 상태 생성
  • count → 상태 값
  • setCount → 상태를 변경하는 함수

useState를 사용하면 컴포넌트는 상태를 기억하고, 값이 변경되면 자동으로 다시 렌더링돼요!


4. 상태는 직접 수정하면 안 돼요 ❌

count = count + 1; // ❌ 절대 이렇게 직접 변경하지 마세요!

React는 상태가 바뀌는 순간을 감지해야만, 컴포넌트를 다시 그려줄 수 있어요.
setCount처럼 전용 함수를 사용하지 않으면, 화면에 반영이 안 됩니다.


5. 상태 변경의 예시: 텍스트 입력

function InputField() {
  const [text, setText] = useState('');

  return (
    <div>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
      <p>입력한 값: {text}</p>
    </div>
  );
}
  • 입력창에 텍스트를 입력하면 text라는 상태가 갱신되며,
  • setText가 호출되고, 컴포넌트가 다시 그려집니다.

6. 불변성이 중요한 이유

불변성이란 한번 만들어진 데이터는 직접 수정하지 않고, 새로운 값을 만들어서 교체하는 것이에요.

const arr = [1, 2, 3];
const newArr = [...arr, 4]; // ✅ 새로운 배열 생성 (불변성 유지)

❌ 불변성을 깨뜨리는 예

arr.push(4); // 원본 배열 직접 수정 → React가 감지하지 못할 수 있음!

✅ 불변성을 지키는 예

const newArr = [...arr, 4]; // 새로운 배열 생성

React는 이전 상태와 새로운 상태를 비교하여 변화 여부를 판단하는데,
불변성을 지키지 않으면 변화를 감지하지 못하고 렌더링도 안 될 수 있어요!


7. 객체 상태 업데이트 예시

function Profile() {
  const [user, setUser] = useState({ name: '철수', age: 25 });

  const increaseAge = () => {
    setUser({ ...user, age: user.age + 1 });
  };

  return (
    <div>
      <p>이름: {user.name}</p>
      <p>나이: {user.age}</p>
      <button onClick={increaseAge}>나이 증가</button>
    </div>
  );
}
  • setUser 호출 시 객체 전체를 새로 만들되,
  • ...user를 통해 기존 데이터는 복사하고,
  • 변경할 부분만 덮어씌우는 패턴입니다.

8. 불변성 유지하는 패턴 모음

상황 방법
배열 추가 setItems([...items, newItem])
배열 제거 setItems(items.filter(i => i.id !== 삭제ID))
배열 수정 setItems(items.map(i => i.id === 수정ID ? {...i, 변경내용} : i))
객체 업데이트 setState({...state, 변경속성: 값})

9. 상태 변경은 비동기! 주의사항

setCount(count + 1);
setCount(count + 1); // ❌ 의도와 달리 1만 증가할 수 있어요!

이럴 땐 함수형 업데이트를 사용해야 해요:

setCount(prev => prev + 1);
setCount(prev => prev + 1); // ✅ 이렇게 하면 2만큼 증가!

10. 주의할 점 요약

항목 설명
useState는 배열 구조 분해로 사용 [value, setValue] = useState()
상태 직접 수정 금지 setXxx() 함수로만 변경
상태 변경 시 새로운 객체/배열 생성 ..., filter, map 등으로 불변성 유지
상태 변경은 비동기 연속 호출 시 prev => 방식 사용
상태 변경이 일어나면 자동 렌더링 화면이 자동으로 갱신됨

마무리하며

React에서 state는 컴포넌트의 생명력입니다!
이 state를 불변성 원칙에 따라 신중하게 다루는 습관이 쌓이면,
나중에 리팩토링, 디버깅, 성능 최적화까지 한층 수월해질 거예요. 💪

다음 글에서는 props와 state가 함께 동작하는 방식,
그리고 상태를 여러 컴포넌트 간에 어떻게 공유하고 분리하는지에 대해서도 더 깊이 배워볼게요!

🔥 한 걸음 더 성장한 React 개발자가 되어봅시다!

답글 남기기