학습 목표: 시간에 따라 변하는 값을 React답게 다루는 법을 익힘
(1) State란?
한 줄 정의: 컴포넌트가 기억하는 값. 변하면 화면도 자동 업데이트
📺 TV 리모컨 비유
useState는 TV 리모컨에 비유할 수 있음
const [count, setCount] = useState(0);
// ↑값 ↑변경함수 ↑초기값
- count = 현재 채널 번호(지금 값)
- setCount = 채널 바꾸는 버튼(값 바꾸는 도구)
- useState(0) = 리모컨 초기 설정(처음 값은 0)
중요: 반드시 setCount로 바꿔야 React가 알아차림. count = 5 이렇게 직접 바꾸면 React는 모름
🔄 Props vs State
| Props | State | |
| 누가 관리? | 부모 컴포넌트가 전달 | 자기 자신이 관리 |
| 수정 가능? | X, 읽기 전용 | O, 가능(setter로만) |
| 변하면? | 부모가 다시 내려줘야 | 자동으로 화면 업데이트 |
| 비유 | 받은 택배 | 내 비밀 노트 |
(2) useState 사용하기
기본 문법
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>카운트: {count}</p>
<button onClick={() => setCount(count + 1)}>
증가
</button>
</div>
);
}
한 줄씩 뜯어보기
const [count, setCount] = useState(0);
| 부분 | 의미 |
| useState(0) | "0부터 시작하는 state 만들어줘" |
| [count, setCount] | "값은 count로, 변경 함수는 setcount로 받을게" |
| const | 재할당 방지(setCount로만 바꾸도록) |
작동 원리
1️⃣ 초기 렌더링
→ count = 0, 화면에 0 표시
2️⃣ 사용자가 버튼 클릭
→ setCount(1) 호출
→ React: "오! state가 바뀌었네!"
3️⃣ React가 자동으로 컴포넌트 재실행
→ count = 1, 화면에 1 표시 ✨
(3) 여러 State 함께 쓰기
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false);
function handleSubmit(e) {
e.preventDefault();
// 로그인 로직...
setIsLoggedIn(true);
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button>로그인</button>
</form>
);
}
분석
<input
value={email} {/* 현재 값 */}
onChange={(e) => setEmail(e.target.value)} {/* 바뀔 때 업데이트 */}
/>
- value={email} : 입력창이 보여줄 값은 email state
- onChange : 입력이 바뀔 때마다 setEmail 호출
- e.target.value : 사용자가 방금 입력한 값
(4) 중요한 패턴: 함수형 업데이트
방법 1: 직접 값 전달(기본)
setCount(count + 1);
방법 2: 함수 전달(실무에선 이게 더 안전) ⭐
setCount(prev => prev + 1); // prev = 이전 값
차이점
function handleClick() {
// 방법 1: 직접 값
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// 1만 증가
// 방법 2: 함수 전달
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// 3 증가
왜 이렇게 되는 걸까?
방법 1의 함정
// count가 0일 때 버튼 클릭
setCount(count + 1); // setCount(0 + 1) = 1
setCount(count + 1); // setCount(0 + 1) = 1 ← 같은 count
setCount(count + 1); // setCount(0 + 1) = 1 ← 역시 같은 count
// 결국 1로 덮어쓰기만 함
count는 리렌더링 전까진 0으로 고정임. 3번 호출해도 전부 같은 계산을 함
방법 2의 똑똑함
setCount(prev => prev + 1); // prev=0 → 1
setCount(prev => prev + 1); // prev=1 → 2
setCount(prev => prev + 1); // prev=2 → 3
언제 어떤 걸 써야 하나?
| 상황 | 추천 |
| 한 이벤트에서 딱 한 번 호출 | 직접 값 (setCount(count + 1)) OK |
| 여러 번 호출하거나 비동기 작업 후 | 함수 전달 필수 |
| 이전 값에 기반한 업데이트 | 함수 전달 권장 |
(5) 토글 패턴
function ToggleButton() {
const [isOn, setIsOn] = useState(false);
return (
<button onClick={() => setIsOn(prev => !prev)}>
{isOn ? 'ON' : 'OFF'}
</button>
);
}
prev => !prev → "이전 값의 반대"로 바꾸기. 함수형 업데이트 활용의 정석
(6) 자주 하는 안티패턴
❌ 안티패턴 1: State 직접 수정
// ❌ 절대 금지
const [count, setCount] = useState(0);
function handleClick() {
count = count + 1; // 직접 수정! React가 모름
count++; // 이것도 금지
}
// ✅ 반드시 setter 사용
function handleClick() {
setCount(count + 1);
}
문제점: React는 setter 호출을 감지해서 리렌더링함. 직접 수정하면 값은 바뀌어도 화면은 그대로임
❌ 안티패턴 2: setter 호출 직후 바로 값 읽기
// ❌ 착각의 함정
function handleClick() {
setCount(count + 1);
console.log(count); // 여전히 예전 값
}
이유: State 업데이트는 비동기임. setter를 호출해도 다음 렌더링까진 안바뀜
새 값이 필요하면 함수형 업데이트 내부에서 작업하거나, useEffect를 쓰기
❌ 안티패턴 3: 일반 변수로 값 기억하려 하기
// ❌ 리렌더링 때마다 초기화됨
function Counter() {
let count = 0; // 일반 변수
return <button onClick={() => count++}>{count}</button>;
}
// ✅ useState 사용
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
규칙: "시간이 지나면서 변하고, 화면에 영향을 주는 값"은 무조건 useState!
❌ 안티패턴 4: Props를 State로 복사
// ❌ 불필요한 중복 관리
function UserCard({ name }) {
const [userName, setUserName] = useState(name);
return <h1>{userName}</h2>;
}
// ✅ Props를 그대로 쓰기
function UserCard({ name }) {
return <h2>{name}</h2>;
}
문제점: Props가 바뀌어도 State는 안 바뀜. 데이터 동기화 버그의 원인
'DEV > React' 카테고리의 다른 글
| [React 개발 기초] 9. 컴포넌트 재사용하기 (심화 Part) (0) | 2026.08.10 |
|---|---|
| [React 개발 기초] 8. 참조형 State (0) | 2026.08.10 |
| [React 개발 기초] 6. children (0) | 2026.08.10 |
| [React 개발 기초] 5. Props (0) | 2026.08.10 |
| [React 개발 기초] 4. 컴포넌트(Component) (0) | 2026.08.05 |