전역 상태관리란 우리가 만드는 리액트 프로젝트 안에 있는 모든 컴포넌트안에서 동일한 상태를 관리한다는 이야기이다.
전역으로 관리해야 할것으로는 두가지가 있다.
첫번째는 클라이언트단의 로컬 상태, 두번째는 서버단의 서버 상태이다.
오늘은 이중에서 클라이언트단의 로컬 상태관리에 대해서 알아보자
흔히아는 로컬 전역 상태관리 툴은 redux, recolil일것이다. 하지만 이것들은 구현하기 위해 보일러 플레이트 코드가 너무 많고 어렵기 때문에 점점 탈 redux? 현상이 일어나고 있다.
그에 따라서 Apollo는 굉장히 좋은 대안이 될 수 있다.
그렇다면 왜 전역 상태관리를 해야할까?
다들 알겠지만 리액트에서의 데이터 이동 방향은 부모 컴포넌트에서 자식 컴포넌트로 밖에 이동하지 못한다. 이러한 단일 흐름은 구조를 명확하게 해주는 장점이 있고 유지보수 또한 쉽게 해준다는 장점이 있지만 그로인한 비효율 또한 존재한다.
Apollo는 GraphQl을 통해 서버와 통신하고 캐시로 저장하는 상태 관리 라이브러리이다. 하지만 꼭 서버에 국한되지 않고 완전히 독립적으로 사용될 수 있어서 로컬 상태관리로도 많이 쓰인다.
react + typescript
npx create-react-app@latest --template=typescript
router 설치
npm install react-router-dom
apollo 설치
npm install @apollo/client
이제 본격적으로 Apollo를 사용해보자. 만약 redux를 사용해 봤다면 그 기능을 얼마나 쉽게 구현 할수 있는지 바로 느낄수 있을 것이다.
우선 전역으로 상태가 잘 관리되고 있는지 보기위해 두개의 페이지를 만들어보자
APage
// src/pages/aPage.tsx
function APage() {
return (
<>
<div>a 페이지 입니다.</div>
<Link to="/bPage">b 페이지로 이동</Link>
</>
);
}
export default APage;
BPage
// src/pages/bPage.tsx
function BPage() {
const navigate = useNavigate();
return (
<>
<div>b 페이지 입니다.</div>
<button
onClick={() => {
navigate("/");
}}>
a 페이지로 이동
</button>
</>
);
}
export default BPage;
router 설정
// src/App.tsx
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<APage />} />
<Route path="/bPage" element={<BPage />} />
</Routes>
</BrowserRouter>
);
}
export default App;
결과화면
A와 B페이지는 서로 부모 자식관계가 아니기 때문에 만약 같은 상태를 공유한다면 전역 상태관리가 잘 되고 있다는 것을 알 수 있다.
Apollo 설정
// src/modules/apollo.ts
import { ApolloClient, InMemoryCache } from "@apollo/client";
const cache = new InMemoryCache();
const client = new ApolloClient({
cache,
});
export default client;
우선 Apollo의 캐쉬 저장공간을 만들어서 사용한다고 이햐하면 될것 같다.
전역 변수 설정
// src/modules/exam.ts
import { makeVar } from "@apollo/client";
const examValue = makeVar("");
export default examValue;
makeVar라는 Apollo의 메소드를 이용해 초기값을 지정하고 전역변수를 생성하면 된다.
정말이지 redux에 비해서 얼마나 설정하기가 편한가 심지어 사용하는 것은 더 쉽다 ㅎㅎ
전역설정
// src/index.tsx
import client from "./modules/apollo";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement,
);
root.render(
<>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</>,
);
방금 만들었던 client를 불러와 전역으로 사용할 수 있도록 Provider로 전달해주었다.
값 사용
// src/pages/aPage.tsx
function APage() {
const exam = useReactiveVar(examValue);
return (
<>
<div>a 페이지 입니다.</div>
<p>{exam}</p>
<Link to="/bPage">b 페이지로 이동</Link>
</>
);
}
export default APage;
값을 불러올땐 useReactiveVar라는 hook을 이용해서 불러와야한다. 그래야지만 state처럼 바로바로 반응하여 값이 변경된다.
값 변경
// src/pages/bPage.tsx
function BPage() {
const navigate = useNavigate();
const exam = useReactiveVar(examValue);
const onChange = (e: any) => {
examValue(e.target.value);
};
return (
<>
<div>b 페이지 입니다.</div>
<input type="text" placeholder="검색" onChange={onChange} />
<p>{exam}</p>
<button
onClick={() => {
navigate("/");
}}>
a 페이지로 이동
</button>
</>
);
}
export default BPage;
input tag를 하나줘서 값이 onChange가 될때 마다 전역 상태값을 바꾸도록 했다.
값을 바꾸기 위해서는 makeVar로 생성한 변수 뒤에 ()와 괄호안에 바꿀 값을 넣어주면 된다.
사실 뒤에 괄호만 붙이고 괄호안에 값을 넣어주지 않으면 상태값을 리턴한다. 하지만 지금과 같이 값이 바로바로 바껴야하는 경우에는 useReactiveVar를 이용해서 값을 가져와야한다.
결과화면
정말이지 너무나도 쉬운 상태관리 라이브러리라는 생각이든다.
굳이 redux-thunk, redux-saga를 쓰는 경우가 아니라면 과연 무조건 redux가 맞는 선택지인지 고민해 볼 필요가 있는것 같다.
그럼 다음번에는 apollo를 GraphQl을 통해 서버 상태관리를 하는것도 한번 다뤄보도록 하자.
참조
https://krpeppermint100.medium.com/ts-react-apollo-client%EC%97%90%EC%84%9C-reactive-variables%EB%A1%9C-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0-d204130fea69
https://chanyeong.com/blog/post/45