| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- WebP
- VITE
- 반응형
- 삼탠바이미
- JS슬라이더
- 상하중앙정렬
- hooks
- CSS헥사코드
- 시멘틱마크업
- 컨테이너쿼리
- 벤더프리픽스
- webapi
- iife
- css 수직중앙정렬
- 수직중앙정렬
- useState
- 스마트모니터
- 웹폰트최적화
- Nextjs
- vendorprefix
- 43인치모니터
- 리액트
- 이미지확장자
- 00000000
- CSS
- 즉시실행함수
- state끌어올리기
- react
- 미디어쿼리
- LS43FM701
- Today
- Total
Nimbus
state 끌어올리기 본문
리액트를 순서대로 공부하다 보면 컴포넌트를 여러 개로 쪼개서 만드는 법을 배운 다음에 꼭 나오는 개념이 있다.
바로 [state 끌어올리기]다.
이번 포스팅에서는 이게 뭔지, 그리고 왜 이런 방식으로 데이터를 다뤄야 하는지 학습한 내용을 정리해 보려고 한다.
어떤 상황에서 필요한가
온도를 입력하면 그 값을 다른 곳에 표시해주는 기능을 만든다고 해보자. 입력창 컴포넌트와 표시 컴포넌트를 따로 만들면 이렇게 된다.
function TempInput() {
const [temp, setTemp] = useState('');
return <input value={temp} onChange={e => setTemp(e.target.value)} />;
}
function TempDisplay() {
return <div>온도: ???</div>;
}
TempDisplay가 temp 값을 표시하려면 그 값을 알아야 하는데, temp는 TempInput 안에서만 존재하는 state라 바깥에서 접근할 방법이 없다.
"props로 넘기면 되지 않나?" 싶을 수 있지만, 둘은 부모-자식 관계가 아니라 형제 관계다. 리액트는 데이터가 부모에서 자식으로만 흐르도록 설계되어 있어서, 형제끼리는 직접 데이터를 주고받는 통로 자체가 없다.
왜 이런 구조인가
이건 리액트가 일부러 그렇게 만든 구조라고 한다.
만약 형제 컴포넌트끼리 자유롭게 서로의 state를 읽고 쓸 수 있다면, 데이터가 어디서 시작해서 어디로 흘러가는지 추적하기가 어려워진다. 컴포넌트가 몇 개 안 될 때는 괜찮아도, 프로젝트가 커지면 "이 값이 대체 어디서 바뀌는 거지?"를 찾아 헤매게 되는 구조다.
그래서 리액트는 규칙을 하나로 통일한다.
데이터는 항상 위(부모)에서 아래(자식)로만 흐른다
이 규칙 덕분에 컴포넌트 하나만 봐도 "이 값은 props로 받은 거구나" 아니면 "내 state구나"를 바로 알 수 있다. 대신 형제끼리 데이터를 공유하려면 그 값을 공통 부모로 옮겨야 하는데, 이 작업이 바로 state 끌어올리기다.
해결방안 : state를 부모로 옮기기
방법은 temp state를 TempInput 안에 두지 않고, 두 컴포넌트를 감싸는 부모 컴포넌트로 옮기는 것이다.
function Parent() {
const [temp, setTemp] = useState('');
return (
<>
<TempInput temp={temp} setTemp={setTemp} />
<TempDisplay temp={temp} />
</>
);
}
function TempInput({ temp, setTemp }) {
return <input value={temp} onChange={e => setTemp(e.target.value)} />;
}
function TempDisplay({ temp }) {
return <div>온도: {temp}</div>;
}
흐름을 정리하면 이렇다.
- Parent가 temp state를 들고 있다가 두 자식에게 props로 내려준다 (temp, setTemp)
- TempInput에서 사용자가 값을 바꾸면 setTemp()를 호출해서 Parent의 state를 갱신한다
- 갱신된 값은 다시 props로 두 자식에게 흘러가고, TempDisplay도 최신 값을 보여준다
핵심은 "state를 어디에 둘지 정하는 것" 하나다. 데이터가 필요한 컴포넌트들의 가장 가까운 공통 조상을 찾아서 그 자리에 state를 두면, 나머지는 props로 자연스럽게 풀린다.
어디까지 끌어올려야 하나
그렇다고 무조건 최상위 컴포넌트에 state를 다 몰아넣는 건 오히려 안 좋은 방식이라고 한다.
state를 필요 이상으로 위로 올리면, 관련 없는 컴포넌트들까지 props를 계속 물려받아서 전달만 하는 코드(prop drilling)가 늘어난다. 그래서 원칙은 이거다.
데이터를 실제로 필요로 하는 컴포넌트들의 가장 가까운 공통 부모까지만 끌어올린다
즉 무조건 위로 올리는 게 아니라, "누가 이 값을 필요로 하는가"를 먼저 파악하고 그 지점까지만 옮기는 게 핵심이다.
마무리
정리하면 state 끌어올리기는 "형제 컴포넌트가 같은 데이터를 공유해야 할 때, 그 데이터를 가장 가까운 공통 부모로 옮기는 것"이다.
리액트가 데이터를 부모→자식 방향으로만 흐르게 하는 이유도, 결국 데이터 흐름을 예측 가능하게 만들기 위해서라는 걸 이번에 알게 됐다.
프로젝트가 더 커지면 이 패턴만으로는 관리가 버거워져서 Context API나 상태관리 라이브러리를 쓴다고 하는데, 그 이야기는 공부한 뒤 다음에 따로 정리해봐야겠다.