Redux => React-Query

minseok baek·2024년 7월 8일

프로젝트

목록 보기
1/20

Redux-Thunk에서 React-Query와 Redux에서 Zustand로의 전환

1. 서론

2주차 동안 예상치 못한 이벤트가 많아 우선순위에 따라 일정 조정을 하다 보니 회고를 작성하지 못했다. 그러나 그 동안 인자강 프로젝트의 본격적인 리팩토링을 진행했다. 가장 먼저 진행한 작업은 Redux-Thunk를 React-Query로 변경하는 것이었고, Redux를 Zustand로 변경했다.

2. React-Query 적용 및 학습

React-Query는 이론적으로 학습하고 간단한 실습만 해본 상태였기에 정리가 필요했다. 이전에 X-Com 프로젝트를 수행할 당시 MSW를 사용하는 한계로 인해 mutation은 적용해보지 못했지만, 작은 경험 덕분에 React-Query의 적용이 의외로 간단하게 느껴졌다.

생각해봤을 때 핵심은 쿼리 키 관리였다. 인자강에서는 각 도메인별 queryKeys 객체를 만들어, mutation이 발생해 쿼리 키의 최신화가 필요한 경우 queryClient.invalidateQueries({ queryKey: coverLetter.all })와 같은 방식으로 해당 도메인의 메인 키를 초기화하는 방법을 채택했다.

3. Redux-Thunk에서 React-Query로 전환하며 느낀 점

아직은 기본적인 스펙만 적용해본 결과, React-Query의 학습 장벽은 높지 않다고 느꼈다. Thunk와 달리 보일러플레이트 코드가 필요 없고, 에러 처리나 로딩 상태를 쉽게 표현할 수 있는 점이 매우 좋았다.

예를 들어, Thunk에서는 업데이트 시 현재 Redux 상태를 최신화하기 위해 액션 자체에서 dispatch를 하거나 useEffect를 활용해 최신화하는 Fetch 함수를 실행해야 했다. 그러나 React-Query에서는 쿼리 키만 초기화하면 간편하게 최신화가 진행되며, isLoading, isError 같은 프로미스 상태도 반환해줘서 편리했다. 또한 Redux-Thunk와 달리 기본적인 캐싱을 지원하여 API 요청 횟수를 절감할 수 있었다.

4. 상태 관리 라이브러리 전환 시의 반성

컴포넌트뿐만 아니라 커스텀 훅을 생성할 때도 단일 책임 원칙(SRP)이나 규칙을 잘 정하여 적용하는 것이 중요하다는 점을 깨달았다.

프로젝트에서 React-Thunk에 대한 의존성을 끊기 위해 커스텀 훅을 적극적으로 활용했다. 하지만 리팩토링 과정에서 규칙의 범위가 너무 넓었던 오차가 있음을 깨달았다.

액션을 취할 때 커스텀 훅을 통해 useCallback으로 함수를 메모이징하여 사용했다. 이런 방식은 리팩토링 과정에서도 해당 커스텀 훅 내부만 교체해주면 되는 점에서 편리했다. 그러나 Thunk에서는 상태를 업데이트하면 해당 상태의 데이터를 useSelector를 통해 Redux에서 직접 가져와야 했고, 이 과정마저 같은 useDomainManager와 같은 커스텀 훅에 몰아넣었던 것이 큰 문제였다.

5. React-Query의 도입과 문제점

React-Query를 도입하면서 useSelector 부분은 useQuery로 대체되었다. 그러나 useMutation과 useQuery를 같은 커스텀 훅에 몰아넣으면, useQuery가 fetch될 때마다 커스텀 훅의 상태 변화로 인해 해당 훅을 사용하는 컴포넌트에서 무한 렌더링 같은 현상이 발생했다. 또한, useSelector의 상태를 기반으로 동작하는 함수를 한곳에 모아 서버 상태 관리를 분리하여 프로젝트가 라이브러리 자체에 의존하는 관계는 끊어냈지만, 라이브러리 관련 코드 내의 의존 관계는 끊어내지 못해 스파게티 코드가 되었다.

6. 결론

이번 리팩토링 작업을 통해 React-Query와 Zustand의 장점을 경험할 수 있었으며, 커스텀 훅과 상태 관리에 대한 깊은 이해를 도모할 수 있었다. 앞으로도 SRP 원칙을 철저히 지키고, 명확한 규칙을 설정하여 더 나은 코드를 작성할 수 있도록 노력하겠다.

번외 이슈

기존에 프로젝트에 카카오 기술 블로그를 보면서 CurrentUI를 도입하고 싶었지만, Thunk의 경우 상태 관리 라이브러리의 한계로 인해 Suspense나 ErrorBoundary를 처리하기 위해 Fetch 컴포넌트를 만들어야 했다. 이 컴포넌트에서는 isLoading 시 <div>스피너</div>을 리턴하고, 데이터가 준비되면 <children data>를 리턴하는 과정을 거쳤다.

이러한 처리 과정을 거친 후, CurrentUI를 도입하는 것은 중복 작업이라 생각되어 철회했었다. 이번 기회에 Template 관련 도메인에 CurrentUI 방식을 도입해 본 결과, 코드가 깔끔하고 선언적이어서 보기도 좋고, 신경 쓸 부분이 줄어들어 효율적이라는 생각이 들었다. 하지만, 프로젝트가 Redux-Thunk에 의존하고 있는 상태에서는 전체적인 전환이 어렵기 때문에, 도메인별 리팩토링을 통해 점진적으로 CurrentUI를 도입할 예정이다.

profile
성장은 점진적 과부하, 매주 회고를 목표로 시작했지만 그때 그때 컨셉이 달라요. 시행착오를 통해 저만의 방식을 찾아가는중입니다.

0개의 댓글