⚙️ 자식 컴포넌트에 상태 변경 전달 방식
React에서 부모가 관리하는 상태를 자식이 변경해야 할 때,
다음 두 가지 방식 중 하나를 선택할 수 있습니다.
1️⃣ setState 직접 전달
function Parent() {
const [count, setCount] = useState(0);
return <Child count={count} setCount={setCount} />;
}
function Child({ count, setCount }: { count: number; setCount: React.Dispatch<React.SetStateAction<number>> }) {
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((prev) => prev + 1)}>+</button>
</div>
);
}
- 부모의
setState함수를 자식에게 직접 넘김 - 자식이 상태를 직접 변경할 수 있음
📌 특징
| 항목 | 내용 |
|---|---|
| 캡슐화 | ❌ 부모의 내부 상태 변경 로직이 자식에게 노출됨 |
| 함수의 올바른 사용 | ⚠️ 자식이 setState를 임의로 호출할수 있어 예측 어려움 |
| 유지보수성 | ⬇️ 부모 상태 변경 로직이 분산되어 관리 어려움 |
| 의도 전달 | ❌ "무엇을 하려는가"가 불명확 (setCount 호출만으로는 의도를 알기 어려움) |
2️⃣ 상태 변경 로직을 담은 함수 전달 (권장 ✅)
function Parent() {
const [count, setCount] = useState(0);
const handleIncrement = () => setCount((prev) => prev + 1);
const handleReset = () => setCount(0);
return (
<Child
count={count}
onIncrement={handleIncrement}
onReset={handleReset}
/>
);
}
function Child({
count,
onIncrement,
onReset,
}: {
count: number;
onIncrement: () => void;
onReset: () => void;
}) {
return (
<div>
<p>Count: {count}</p>
<button onClick={onIncrement}>+</button>
<button onClick={onReset}>리셋</button>
</div>
);
}
- 부모가 상태 변경 로직을 감싼 함수를 자식에게 전달
- 자식은 "무엇을 해야 하는가"만 할고, 상태 변경 세부 로직은 모름
📌 특징
| 항목 | 내용 |
|---|---|
| 캡슐화 | ✅ 부모의 상태 관리 로직이 보호됨(자식은 직접 접근 불가) |
| 함수의 올바른 사용 | ✅ 자식은 정해진 인터페이스(onIncrement)만 사용 |
| 유지보수성 | ⬆️ 상태 변경 로직이 부모에 집중되어 관리 용이 |
| 의도 전달 | ✅ 함수 이름만으로 의도가 명확(onIncrement, onReset) |
3️⃣ 비교 요약
| 구분 | setState 직접 전달 | 상태 변경 로직 전달(권장) |
|---|---|---|
| 상태 제어권 | 자식이 직접 변경 | 부모가 완전 제어 |
| 캡슐화 | 낮음 | 높음 |
| 의도 전달 | 불명확 | 명확 |
| 유지보수성 | 낮음 | 높음 |
| 권장 여부 | ❌ 단순 상황 외 비권장 | ✅ 대부분의 경우 권장 |
💡 정리
React는 단방향 데이터 흐름(one-way data flow) 과 컴포넌트 캡슐화(encapsulation) 를 핵심 원칙으로 삼습니다.
따라서 자식에게
setState를 직접 넘기기보다는, 의도가 명확하고 캡슐화된 상태 변경 함수를 전달하는 것이 유지보수성과 예측 가능성을 모두 높이는 올바른 패턴입니다.