Redux에서 벗어나기

Gn0lee·2023년 5월 5일
0

Tech 이모저모

목록 보기
13/18
post-thumbnail

Redux를 사용해온 과정

처음 사내 프로젝트를 시작할때 redux를 사용했었다. 비동기 처리는 따로 하지 않아서 api의 결과물을 redux에 저장하고 필요한 컴포넌트에서 꺼내 사용했다. 그야말로 저장소에 가까웠다. 하지만 제품이 커지고 컴포넌트의 구성이 복잡해지면서 이런 방식은 불편한점이 많았다. 우선 컴포넌트 내의 코드양이 불필요하게 많아졌다. 컴포넌트 내에서 api를 호출하고 결과물을 저장하는 방식이었기 때문에 해당 동작 코드가 컴포넌트 내에 작성되어있었다. api를 1개만 사용하면 큰 문제 없겠지만 여러 api를 호출하면 그만큼 컴포넌트는 복잡해졌다.

그래서 리팩토링을 통해 redux-thunk를 도입하였다. api 관련 동작을 thunk 내부에 작성하여 컴포넌트 내에는 컴포넌트를 나타낼 코드만 남게 되었다. 하지만 api 하나를 새로 작성하기 위해서는 api, type, thunk 파일을 만들고 해당 slice에 등록해야한다. 등록하는 코드도 의외로 작성할 것이 많다. pending, fufiled, rejected 상태를 모두 직접 등록해 주어야 하며 loading, error에 따른 상태도 직접 제어해야한다. 예를 들면 isLoading state를 만들고 pending 때는 true로 변경하였다가 완료되면 false로 직접 변경해주어야 한다. 사용방법이 간단한 만큼 정교하게 사용하기 위해서는 사용자가 고생을 해야한다는 느낌이 강했다.
그리고 RTK의 redux-thunk는 작성할 코드가 적지만 redux-saga나 redux만 사용한다면 작성해야할 코드는 더욱 많아진다.

나는 업무를 계속하다보니 어느정도 익숙해져서 괜찮았지만 다른 팀원들은 thunk 사용하는것에 많은 피로를 느끼는것 같았다. 그래서 react-query를 사용하자는 의견이 나왔고 react-query에 대해 알아보고 점차 적용하기 시작했다

React-query의 장점

  1. 확실히 적은 코드
    기존 redux-thunk를 사용할 때는 thunk 파일 작성후 slice에 등록해야했다. 기존 slice에 등록하는 것이면 그나마 낫지만 새로운 slice를 만들어야 한다면 작성해야할 코드는 더욱 많다. 그리고 pending, fufiled, rejected 상태를 직접 구현해야하며 각 상태를 나타낼 변수를 직접 제어해야했다. 하지만 react-query는 이런 기능을 라이브러리 내부에서 지원했다. 파일 수는 비슷할 수 있지만 각 상태를 일일이 제어할 필요가 없어서 api 함수나 type에 더 집중할 수 있었다. 그리고 loading, error관련 state를 직접 설정할 필요가 없어서

  2. 서버와 동기화 하기위한 다양한 기능들
    react-query를 사용하다 보면 다양한 기능들이 있다. refetchWindowFocus, refetchInterval, refetchOnMount같은 기능들이다. 이외에도 더 많이 있지만 redux-thunk에는 해당 기능들이 없는것으로 알고 있다. 그래서 기존에 실시간 모니터링 컴포넌트를 구현할 때 setInterval을 이용하여 주기적으로 api를 요청하는 코드를 직접 구현했다. 하지만 react-query에는 해당 기능을 라이브러리에서 제공하여 내가 직접 구현할 필요가 없다.

    react-query 문서를 보면 react-query는 서버 상태를 동기화, 캐싱, 업데이트를 도와주는 라이브러리라 설명하고 있다. 위의 편의 기능들은 라이브러리의 방향을 잘 보여주는것 같았다. redux는 애초에 앱의 전역변수 관리에 목적이 있다보니 이런 기능이 있지 않았다. 그래서 한번 fetching 후 계속 사용해도 되는 데이터는 캐싱하여 사용하고 실시간성이 중요한 상태들은 위의 refetch기능들을 이용하여 서버와 계속 동기화 하여 사용하였다. 이런 편의 기능덕분에 내가 직접 작성하는 코드가 많이 줄었다.

  1. 리덕스를 더 리덕스답게
    위의 내용과 비슷한 내용이지만 redux에서 서버 상태가 빠지다보니 redux에는 정말 앱에서 사용하는 전역 상태(변수)만 남게 되었다. 예를 들면 SNB 열림,닫힘 상태나 모달의 렌더링여부 등 유저가 선택하고 변경하는 앱 내의 상태만 남게 되었다. 생각해보면 이것이 리덕스를 더욱 리덕스 답게 사용하는 것이라 생각한다.

React-query의 아쉬운점

사실 사용해 보니 그렇게 큰 단점이나 아쉬운 점은 없다. 거의 억지로 생각해낸 점들인것 같고 이것은 팀 내에서 잘 협의하면 큰 문제가 아니라고 생각한다.

  1. query key 관리방법
    react-query에는 query key라는 변수가 있다. 해당 키를 바탕으로 서버의 상태를 저장하고 라이브러리가 관리한다. 해당 값이 stale한지 아닌지 파악하고 캐시할지 말지 결정한다. 그리고 유저가 서버의 상태를 변경하는 경우 queryKey값을 무효화 시키기도 한다. 그런데 queryKey는 앱 내에서 유일해야한다. 예를 들면, "a"라는 쿼리키를 무효화 하면 ["a",1]과 ["a", 2]의 쿼리키값을 갖는 쿼리를 모두 무효화 시킨다. 내가 모든 쿼리키를 알고 있다면 상관 없지만 여러 팀원들이 같이 작업하면 이를 알기 힘들다. 그리고 앱의 크기가 커지면 유일한 변수 만들기도 힘들다. 나는 현재 앱의 크기가 그렇게 크지 않기 때문에 객체를 통해 쿼리키값을 관리하기로 했다. 객체의 key값은 중복될 수 없기 때문에 프로젝트 내에서 유일한 값을 가질 수 있다. 팀 내부에서 객체의 값은 키값과 동일하게 설정하기로 하였다. 이런 방식으로 관리하면 쿼리 설정할때 해당 객체에서 값을 꺼내 사용하기 때문에 작업자의 실수를 방지 할 수도 있다. 하지만 앱이 매우 거대한 경우에는 오히려 더 어려울 수 있다. 실제로 쿼리키를 api호출을 위한 변수들을 등록하여 사용하는 방식도 있다고 한다.

  2. 의외로 까다로운 debounce

    사용자가 textinput에 값을 입력하면 해당 값을 바탕으로 api를 호출하는 경우가 있다. 나는 useEffect와 setTimeout을 이용하여 간단하게 debounce처리를 했다. setTimeout의 callback함수 에서 thunk를 호출하는 방식이었다. 하지만 react query는 이 방법을 사용하기 어려웠다. useEffect안에서 다른 훅을 조회할 수 없기 때문이다. 그래서 다른 개발자들은 useDebounce라는 훅을 따로 만들어서 사용하였다. 하지만 나는 해당 쿼리의 enabled를 false로 설정하고 원하는 시점에 refetch 하여 사용하였다. 이런 방식은 기존 redux-thunk와 큰 차이점이 없는것 같아서 좋은 방법인지는 모르겠다.

마무리

redux-thunk를 react-query로 전환하는 작업은 현재 진행형이다. 내가 맡은 부분은 절반이상 전환한 것 같다. react-query의 모든 기능을 사용해 보지 않았지만 아직까진 매우 만족하고 있다. 다른 팀원과 이야기하다보니 같은 라이브러리지만 사용하는 방식은 개인마다 다른것 같다. 위의 debounce처럼 enable을 false로 설정하고 fetch하는 시점을 작업자가 모두 설정하는 방식으로 사용하였다. 개인적으로 이런 방식이 맞는지 의문이지만 react-query를 잘못 사용하면 불필요한 api 호출이 많아져 자원을 낭비하는것은 맞는것 같다. 도입하기 전에 react-query를 어떤 방식으로 사용할지도 미리 논의 후 사용해보는 것이 맞는것 같다. 언제나 그렇듯 새로운 방식으로 구현하는 것은 재밌는일인것 같다.

profile
정보보다는 경험을 공유하는 테크 블로그입니다.

0개의 댓글