전역이라는 단어을 우리는 자바스크립트에서도 본 적이 있다.
전역 객체, 전역 변수...
전역이란 군복무를 마친.... 🤜🤕
전역이란 전체영역 정도로 이해하면 좋을거 같다.
전역 상태관리는 그 이름 그대로 전역에서 상태를 관리한다는 뜻이다.
그렇다면 React에서는 전역 상태관리를 어떻게 관리를 하는가 ?
가장 쉬운 방법은 상위 컴포넌트에서 하위 컴포넌트로 prop
을 전달해주면 된다.
코드로 표현해보자 !
// 막내에게 소세지를 주자 !
function App() {
return (
<div>
<FirstComponent sausage="소세지" />
</div>
);
}
function FirstComponent({ sausage }) {
return (
<div>
<h3>난 첫째 ! 둘째야 받아 !</h3>
<SecondComponent sausage={sausage} />
</div>
);
}
function SecondComponent({ sausage }) {
return (
<div>
<h3>난 둘째 ! 막내야 받아 !</h3>
<ThirdComponent sausage={sausage} />
</div>
);
}
function ThirdComponent({ sausage }) {
return (
<div>
<h3>막내 {sausage} 잘 받았습니다 !</h3>
</div>
);
}
export default App;
위 코드를 보면 FirstComponent
에서 sausage
를 ThirdComponent
로 전달하고 있다.
이런식으로 전달되면 ThirdComponent
컴포넌트에서 sausage
을 받을 수 있다.
참 간단한 방법이지만 이 방법은 너무나 큰 단점이 존재한다.
그것은 바로....
Prop Drilling
이란 상위 컴포넌트에서 props를 하위 컴포넌트로 전달할 때 사용은 하지않는 컴포넌트들도 거쳐가는 현상이다. 이것을 드릴로 비유하여 이름을 지은 것이다.
작은 규모의 프로젝트라면 Prop Drilling
은 크게 문제가 되지 않지만 큰 규모의 프로젝트에서 prop이 많은 하위 컴포넌트를 거쳐가게 된다면 해당 prop을 추적하기도 어렵고 관리하기도 어려워 유지보수에 많은 힘이 든다.
( 만약 위에 코드에서 3남매가 아닌 20남매라면 ...? )
이러한 Prop Drilling
을 겪지 않고 상태관리를 하는 방법이 있을까 ??
props.children
이라는 녀석은 합성과 관련된 친구다.
코드로 표현해보자 !
function App() {
const sausage = "소세지";
return (
<div>
<FirstComponent>
<SecondComponent>
<ThirdComponent sausage={sausage} />
</SecondComponent>
</FirstComponent>
</div>
);
}
function FirstComponent(props) {
console.log(props);
return (
<div>
<h3>난 첫째 ! 둘째야 받아 !</h3>
{props.children}
</div>
);
}
function SecondComponent(props) {
console.log(props);
return (
<div>
<h3>난 둘째 ! 막내야 받아 !</h3>
{props.children}
</div>
);
}
function ThirdComponent(props) {
console.log(props);
return (
<div>
<h3>막내 {props.sausage} 잘 받았습니다 !</h3>
</div>
);
}
export default App;
console.log로 각 컴포넌트마다 props의 형태를 살펴보았다.
FirstComponent
의 props
는 children
이 한번더 들어있고 그 안에 소세지가 있다.
SecondComponent
의 props
는 소세지가 들어가있고 ThirdComponent
는 소세지가 그대로 출력이 된 걸 확인할 수 있다.
한마디로 props.children
은 상위 컴포넌트 사이에 있는 하위 컴포넌트를 가져온다. 이것을 바로 합성
이라고 하는데 기존 상위 컴포넌트에서 하위 컴포넌트로 상속
하는 방식과는 달리 컴포넌트들을 합치는 과정에 가깝다.
Context API
는 React
에서 전역으로 상태관리를 할 수 있도록 도와주는 API이다.
이러한 전역적인 상태관리를 이용하면 상속과 합성을 사용하지 않고도 상태관리를 할 수 있다.
코드로 표현해보자 !
const gift = "소세지";
const giftContext = createContext(gift);
function App() {
return (
<div>
<giftContext.Provider value={gift}>
<FirstComponent>
<SecondComponent>
<ThirdComponent />
</SecondComponent>
</FirstComponent>
</giftContext.Provider>
</div>
);
}
function FirstComponent() {
return (
<div>
<h3>난 첫째 ! 둘째야 받아 !</h3>
<SecondComponent />
</div>
);
}
function SecondComponent() {
return (
<div>
<h3>난 둘째 ! 막내야 받아 !</h3>
<ThirdComponent />
</div>
);
}
function ThirdComponent() {
const sausage = useContext(giftContext);
return (
<div>
<h3>막내 {sausage} 잘 받았습니다 !</h3>
</div>
);
}
export default App;
createContext
는 context
객체를 만들어준다.
객체를 구독하고 있는 컴포넌트를 렌더링 할 때 React
는 트리 상위에서 가장 가까이 있는 짝이 맞는 Provider
로 부터 현재 값을 읽는다.
쉽게 말해 createContext()
괄호 안에 있는 값을 초기 값으로 설정하고 해당 값을 전역적으로 사용할 수 있도록 해준다.
giftContext
를 콘솔로 찍어 본 결과 위와 같이 객체형태로 나오게 된다.
useContext
는 createContext
의 값을 사용할 수 있게 해준다.
테스트 결과 위와 같이 giftContext
에서 _currentValue
를 가져와서 사용해도 사용은 가능하다.
하지만 useContext
를 사용하면 그냥 바로 해당 값을 받아 올 수 있다.
function ThirdComponent() {
const sausage = useContext(giftContext);
return (
<div>
<h3>막내 {giftContext._currentValue} 잘 받았습니다 !</h3>
<h3>막내 {sausage} 잘 받았습니다 !</h3>
</div>
);
}
그래도 변수를 설정하고 사용을 하는게 더 깔끔하다.
이렇게 쓰기 좋은 Context API
의 치명적인 단점이 있다. 🚨
바로 Context API
는 상태 값이 변하게 되면 provider
로 감싼 모든 하위 컴포넌트들이 리렌더링한다.
때문에 불필요한 리렌더링이 일어나게 된다.
상속
에 대해서는 이해가 잘 되었다. HTML,CSS,JavaScript에서도 부모 자식 관계에 대해 나름 잘 이해했기 때문이다.
문제는 합성
이였는데 합성에 대한 이해만 3시간이 걸린거 같다... (뻥 안치고)
상위 컴포넌트와 하위 컴포넌트는 항상 상위에서 하위에게 props를 전달한다고 생각을 했는데 이런 고정관념이 이해하는데 큰 벽과 같은 역할을 해버렸다.
이곳 저곳 유튜브랑 블로그 설명을 듣고 직접 구현하는데 3시간 가량 쏟은 결과 바깥 컴포넌트가 안쪽 컴포넌트를 집어삼킨다고 이해를 했다.
그리고 Context API
에 대해서 알아봤는데 채용 공고에서 Context API
를 사용한다는 회사는 딱 한 곳 봤다.
그만큼 많이 사용 안하는 상태관리이고 대부분의 상태관리는 Redux로 하는거 같다.
아마 리렌더링을 하는 단점이 가장 큰 원인이 아닐까 생각이 든다.
예고편...