프로젝트를 진행하면서, 프로젝트의 볼륨이 커질수록 관리해야 하는 상태가 많아지고, 컴포넌트 중첩 구조가 발생함에 따라 관련 state를 상위 컴포넌트에서 하위컴포넌트로 반복적으로 전달하게 되는 props drilling 문제가 발생했다. 따라서 코드 유지보수를 원활하게 관리하고, 상태관리를 간편하게 하기위해 상태관리 라이브러리를 도입하게 되었는데, 이전 프로젝트에서는 redux를 사용하여 상태관리를 진행했었지만, 보일러 플레이트가 적고 비교적 간단한 recoil을 도입하게 되었고, 해당 과정을 포스팅하려고 한다.
1. recoil 라이브러리 설치
일단 recoil 패키지를 설치한다.
// npm
npm install recoil
// yarn
yarn add recoil
2. recoil 세팅하기
recoil을 애플리케이션에서 사용하기 위해서는 recoil에서 제공하는 recoilRoot 컴포넌트를 필요로한다. recoilRoot 컴포넌트는 항상 최상위 컴포넌트에 위치하여 하위 컴포넌트에서 해당 컴포넌트에 정의된 recoil 상태를 사용할 수 있도록 한다. 따라서 nextjs에서는 루트 컴포넌트를 담당하는 _app.tsx 파일 내에서 다음과 같이 recoilRoot를 선언해주면 된다.
import '@styles/globals.css';
import Layout from '@components/layout/Layout';
import type { AppProps } from 'next/app';
import { NextPage } from 'next';
import { ReactElement, ReactNode } from 'react';
import { Hydrate, QueryClient, QueryClientProvider } from 'react-query';
import { RecoilRoot } from 'recoil';
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
};
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
const queryClient = new QueryClient();
export default function App({ Component, pageProps }: AppPropsWithLayout) {
return (
<RecoilRoot>
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Layout>
<Component {...pageProps} />
</Layout>
</Hydrate>
</QueryClientProvider>
</RecoilRoot>
);
}
export { queryClient };
3. recoil 사용하기
recoilRoot를 최상위 컴포넌트에서 선언했기 때문에, 하위 컴포넌트에서는 이제 사용할 수 있게된다. 기본적인 recoil 사용방법에 대해서 알아보자.
recoil은 atom이라는 상태 단위를 사용하는데, atom은 모든 컴포넌트에서 읽고 쓸 수 있고, atom 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독하고 있음을 나타낸다. 따라서 atom에 변화가 발생한다면, atom을 구독하고 있는 즉, atom 값을 읽는 컴포넌트들은 리렌더링이 발생한다.
// recoil/countState.ts
import { atom } from 'recoil';
export const modalState = atom<number>({
key: 'countState',
default: 0,
});
위와 같이 관리하려고 하는 상태 값 atom을 생성한다. key는 atom의 고유 식별자를 의미하고, default는 atom의 초기 디폴트 값을 의미한다. 따라서 위와 같이 생성했다면, countState라는 고유 식별자를 갖는 초기 값 0의 atom(상태)가 만들어졌다.
import { useRecoilState } from 'recoil';
import { countState } from '@recoil/modalAtom';
const Page = () => {
const [count, setCount] = useRecoilState(countState);
const plus = () => {
setCount((prev) => prev + 1)
}
const minus = () => {
setCount((prev) => prev - 1)
}
return (
<PageLayout>
<div>
<button onClick={plus}>1 더하기</button>
<button onClick={minus}>1 빼기</button>
<div>{count}</div>
</div>
</PageLayout>
);
}
atom을 구독하는 컴포넌트가 atom을 읽고 쓰게 하기위해서는 위와 같이 useRecoilState를 사용한다. useRecoilState는 useState와 같이 상태 값과 상태 값 변경 함수가 존재하고, useRecoilState의 인자로는 atom 함수를 사용하여 생성한 전역 상태 값을 넣어준다. 상태 값 변경함수를 통해 전역 상태 값을 변경하면 recoil은 변경사항을 감지해서 해당 atom을 구독하고 있는 모든 컴포넌트를 리렌더링한다. 위의 예시에서 보여준 useRecoilState 이외에도, 전역 상태 값만 가져와 렌더링하는 useRecoilValue, 상태 값을 업데이트하는 useSetRecoilState 훅 등이 존재한다.
이렇게 오늘은 recoil의 기본 세팅과 사용 방법에 대해서 알아보았다. 나도 처음 사용해보는 상태 관리 라이브러리이기 때문에, 전부 자세하게 설명할 수는 없지만, 사용하면서 알게되는 부분들, 새로 사용한 recoil의 훅들이 생기면 포스팅 해보도록 하겠다. 다음 포스팅은 recoil을 통해 애플리케이션의 전반적인 모달을 관리했던 방법에 대해서 소개해보려고 한다.
'개발 지식 정리' 카테고리의 다른 글
렌더링 방식(CSR, SSR, SSG) (2) | 2023.10.17 |
---|---|
주소창에 URL을 입력하면 무슨 일이 일어날까? (2) | 2023.09.04 |
외부 이미지 가져오기 (feat. nextjs) (0) | 2023.04.30 |
CORS 그만 마주치자! (0) | 2023.03.19 |
[NextJS] per-page Layout을 사용한 공통 레이아웃 적용 (0) | 2023.03.14 |