State의 본질과 useState 훅의 동작 원리, 상태 변경이 리렌더링을 어떻게 일으키는지, 그리고 불변성 원칙을 왜 지켜야 하는지 깊이 이해합니다.
💡 State란 무엇인가 — Props와 무엇이 다른가
Props는 부모가 자식에게 넘겨주는 읽기 전용 데이터입니다. 그렇다면 컴포넌트 자신이 가지고 있고, 시간에 따라 변하는 데이터는 어떻게 관리할까요? 이것이 State(상태)입니다.
State와 Props의 차이:
| 구분 | Props | State |
|---|---|---|
| 출처 | 부모 컴포넌트 | 컴포넌트 자신 |
| 수정 | 읽기 전용 | setState 함수로 변경 |
| 목적 | 외부에서 설정값 전달 | 컴포넌트 내부의 변하는 값 |
| 예시 | 버튼 텍스트, 색상 | 클릭 횟수, 입력값, 열림/닫힘 |
⚙️ useState 훅 — 문법과 동작 원리
useState는 리액트가 제공하는 훅(Hook)입니다. 훅은 함수 컴포넌트에서 리액트 기능을 사용하게 해주는 특별한 함수입니다.
import { useState } from 'react'
function Counter() {
// [현재값, 업데이트 함수] = useState(초기값)
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>
)
}
useState(0)는 배열을 반환합니다. 첫 번째 요소는 현재 상태값(count), 두 번째 요소는 상태를 업데이트하는 함수(setCount)입니다. 구조 분해로 이름을 붙여 사용합니다.
🔄 리렌더링의 작동 방식
setCount를 호출하면 무슨 일이 일어날까요? 리액트의 핵심 동작 흐름:
setCount(count + 1)호출- 리액트가 새 상태값(
count + 1)을 기록 - 컴포넌트 함수를 다시 실행(리렌더링)
- 새로운 JSX를 만들어 이전 JSX와 비교(Virtual DOM diffing)
- 변경된 부분만 실제 DOM에 반영
중요한 점: 상태가 변하면 컴포넌트 함수 전체가 다시 실행됩니다. 하지만 DOM은 변경된 부분만 업데이트됩니다. 이것이 리액트가 효율적인 이유입니다.
function Counter() {
const [count, setCount] = useState(0)
console.log('렌더링 실행됨, count:', count) // 클릭할 때마다 출력됨
return (
<button onClick={() => setCount(count + 1)}>
{count}번 클릭
</button>
)
}
⚠️ 불변성 원칙 — 왜 직접 수정하면 안 되는가
상태를 업데이트할 때 직접 수정하면 절대 안 됩니다. 특히 배열이나 객체 상태에서 자주 실수합니다.
const [items, setItems] = useState(['사과', '바나나'])
// ❌ 직접 수정 — 리렌더링이 일어나지 않음!
function addItem() {
items.push('딸기') // 원본 배열을 변경했지만
setItems(items) // 같은 배열 참조라 리액트가 변화를 감지 못함
}
// ✅ 새 배열을 만들어 교체 (불변성 유지)
function addItem() {
setItems([...items, '딸기']) // 스프레드로 새 배열 생성
}
// ✅ 객체 상태도 마찬가지
const [user, setUser] = useState({ name: '김민준', age: 25 })
// ❌ 직접 수정 금지
user.age = 26
setUser(user)
// ✅ 새 객체 생성
setUser({ ...user, age: 26 })
리액트는 참조(reference)를 비교해서 변화를 감지합니다. 같은 배열/객체를 수정하면 참조가 바뀌지 않아 리액트가 변화를 못 보고 리렌더링이 일어나지 않습니다.
🔀 함수형 업데이트
다음 상태가 이전 상태에 의존할 때는 함수형 업데이트를 사용하세요. 연속 클릭 같은 상황에서 정확한 값을 보장합니다:
// ❌ 배치 처리 시 문제 발생 가능
const increment = () => setCount(count + 1)
// ✅ 이전 값을 인수로 받는 형태
const increment = () => setCount(prev => prev + 1)
✅ 정리
- State는 컴포넌트 자신이 가지는 변할 수 있는 값입니다.
useState(초기값)는[현재값, setter함수]를 반환합니다.- setter를 호출하면 컴포넌트가 리렌더링됩니다.
- 배열·객체는 직접 수정하지 말고 새 것을 만들어 교체하세요 (불변성).
- 이전 값 기반으로 업데이트할 때는
setCount(prev => prev + 1)형태를 사용하세요.
관련 주제
- useState 훅
- 상태와 리렌더링
- 불변성 원칙
- state vs props
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 리액트 입문 — 컴포넌트로 만드는 웹
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
