레슨 3 / 9·20분
State와 이벤트
State란?
State(상태)는 컴포넌트 내부에서 관리되는 동적 데이터입니다. Props가 외부에서 전달받는 읽기 전용 데이터라면, State는 컴포넌트 스스로 변경할 수 있는 데이터입니다. State가 변경되면 React는 해당 컴포넌트를 자동으로 다시 렌더링합니다.
useState 사용하기
tsx
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>
);
}이벤트 핸들링
React에서 이벤트는 camelCase로 작성합니다. HTML의 onclick은 React에서 onClick이 됩니다. 이벤트 핸들러에는 함수 자체를 전달해야 하며, 함수를 호출하면 안 됩니다(onClick={handleClick} O, onClick={handleClick()} X).
tsx
import { useState } from 'react';
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); // 폼 기본 제출 동작 방지
console.log('로그인 시도:', { email, password });
};
return (
<form onSubmit={handleSubmit}>
<label>
이메일
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="이메일을 입력하세요"
/>
</label>
<label>
비밀번호
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="비밀번호를 입력하세요"
/>
</label>
<button type="submit">로그인</button>
</form>
);
}State 불변성
React의 state는 직접 수정하면 안 되고, 항상 새로운 값을 만들어 setter 함수에 전달해야 합니다. 배열이나 객체의 경우 스프레드 연산자(...)를 활용하여 새 참조를 생성합니다. 이를 **불변성(immutability)**이라고 하며, React가 변경을 감지하여 화면을 다시 렌더링하기 위해 필수적입니다.
tsx
import { useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState<string[]>([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (!input.trim()) return;
// 새 배열을 생성하여 전달 (불변성 유지)
setTodos([...todos, input]);
setInput('');
};
const removeTodo = (index: number) => {
// filter로 새 배열 생성
setTodos(todos.filter((_, i) => i !== index));
};
return (
<div>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addTodo()}
placeholder="할 일 입력"
/>
<button onClick={addTodo}>추가</button>
<ul>
{todos.map((todo, i) => (
<li key={i}>
{todo}
<button onClick={() => removeTodo(i)}>삭제</button>
</li>
))}
</ul>
</div>
);
}💡
todos.push(input) 처럼 기존 배열을 직접 수정하면 React가 변경을 감지하지 못해 화면이 업데이트되지 않습니다. 항상 setTodos([...todos, input])처럼 새 배열을 만들어야 합니다. 객체도 마찬가지로 setState({ ...prevState, key: newValue }) 패턴을 사용합니다.