본문 바로가기
React

Recoil에서 Redux로 마이그레이션 하게 된 이유

by 도현위키 2022. 1. 16.

 

 

배경

현재 나는 IT 연합동아리 Yapp에서 활동을 하고 있다. 우리 팀은 북마크를 좀 더 쉽고 편리하게 관리하기 위한 서비스를 기획하게 되었는데, 첫 기획 당시에는 단순히 북마크 관리 페이지(검색, 휴지통), 로그인, 회원가입 페이지 이게 전부였다. 그래서 기술 스택을 정할 당시에 전역 상태가 그렇게 많을 거 같지 않아서, 러닝 커브가 매우 적고 상태를 react의 useState처럼 쉽게 관리할 수 있는 recoil을 하기로 정했었다. 

 

동아리에서 진행하는 1차 기획캠프를 하게 되었는데, 기획 캠프는 각 팀별로 기획 내용을 발표한 후에 현업 기획자 분들에게 피드백을 받을 수 있는 시간이었다. 우리가 기획한 북마크 관리 서비스에 대해서도 피드백을 많이 받았는데, 다른 북마크 관리 서비스와의 차별점이 없다는 점에서 지적을 받았다. 그래서 우리끼리 긴급회의로 기획을 수정하게 되었는데, 차별점을 주기 위해서 '북마크 리마인드 기능', '북마크 워크스페이스(멤버 초대) 기능'이 추가되었다.

 

1차 기획캠프로 추가된 기능들로 인해서 관리해야 할 상태가 많아지게 되었고, 기존에는 useState로 관리했었던 폴더 리스트, 북마크 리스트들 컴포넌트가 복잡해지면서 전역 상태로 바꾸게 되었다. 이렇게 상태가 많아지게 되니 recoil의 아쉬운 점들이 많이 나타나게 되어서 1차 범위 개발까지는 recoil로 진행을 하고 2차 범위 개발 시작 전에 redux로 새롭게 작성하였다. 그럼 내가 느꼈을 때 recoil의 아쉬웠던 점들이 어떤 게 있는지 한번 작성해보겠다. 

 

 

Recoil 도입 이유

우리가 Recoil을 도입한 이유는 러닝 커브가 매우적고, atom 단위로 쉽게 사용이 가능하다는 점 때문에 선택하게 되었다. 그리고 무엇보다 공식문서에서 한글을 지원하고 있고, 튜토리얼이 매우 쉬워서 팀원 모두 금방 익힐 수 있다는 점에서 너무 좋았다.  Recoil이 얼마나 간단한지 다음 예시들을 통해 확인해보자

 

Recoil의 초기 셋팅은 index.tsx에  다음과 같이 RecoilRoot로 감싸주기만 하면 된다. 

// index.tsx
ReactDOM.render(
  <React.StrictMode>
    <RecoilRoot>
        <App />
    </RecoilRoot>
  </React.StrictMode>,
  document.getElementById('root'),
);

 

상태를 정의하기 위한 기본적인 atom 생성 방법은 아래와 같다.

import { atom } from "recoil";

export const countState = atom({
	key: "countState",
   	default: 0
})

 

이렇게 정의한 전역 상태는 다음과 같이 사용할 수 있다.

function Counter() {
    const [count, setCount] = useRecoilState(countState)
    
    return (
    	<div>
            <p>{countState}</p>
            <button onClick={() => setCount(count + 1)}>증가</button>
        </div>
    )
}

 

간단한 예시를 봤지만 사용이 엄청 간단하다. 그 외에 API 캐싱, 상태 초기화 등 다양한 문법이 있지만 해당 내용은 공식문서에서 확인하길 바란다. 

 

이렇게 사용하기 쉽고 여러 기능들이 hooks로 제공해주고 있어서 recoil을 사용하기로 했지만, 다소 아쉬운 부분들이 많았다.

 

Recoil 아쉬운 점

프로젝트를 진행하면서 recoil의 아쉬운 점들에 대해서 적어보려한다. 

API 캐싱

recoil은 selector라는 순수함수를 통해서 비동기 처리로 api를 호출할 수 있다. 상품 데이터를 불러오는 예시를 보면 아래와 같은데

const productsAsyncState = selector({
  key: 'productsAsyncState',
  get: async ({ get }) => {
    return await getProductsAPI();
  },
});

recoil은 여기서 getProductsAPI()를 캐싱한다. 또한 각 id마다 api주소가 달라지는 경우에는 selectorFamily 함수를 통해서 각 id별로 api를 캐싱 처리도 가능하다.

 

const productAsyncState = selectorFamily({
  key: "productAsyncState",
  get: ({ id }) => async ({ get }) => {
    return await getProductDetail(id)
  }
})

 

그러나 recoil의 캐싱에는 불편한 점이 있었는데, 한번 캐싱된 api는 추가적으로 갱신이 안된다는 점이다. 우리 서비스로 예를 들어보면 북마크를 저장하는 각 폴더페이지에서 북마크 리스트를 캐싱 처리해두면, 다른 멤버가 그 폴더에 북마크를 추가했을 때 새로고침 하지 않는 이상 갱신이 되지 않는다.  

 

이러한 점 때문에 우리는 성능 향상을 위한 캐싱도 되어야하고 그 캐싱에 시간을 옵션으로 줄 수 있는 react-query 라이브러리를 추가적으로 이용하게 됐다. 

 

 

상태 변경 로직이 흩어져있음

상태 관리의 가장 유명한 redux를 먼저 예시로 들어보겠다. redux의 redux-toolkit에서는 상태 정의와 함께 상태 변경 함수들(reducer)을 같이 적게 되어있다. 

const user = createSlice({
  name: "userReducer",
  initialState,
  reducers: {
    setUser: (state, action) => action.payload
    removeUser: () => initialState
  }
})

이렇게 초기 상태(initialState)상태 변경 로직(reducers)을 한 곳이 적게 되어 있어서 같이 프로젝트 협업하는 입장에서 상태가 어떤 식으로 변경되는지 한눈에 파악할 수 있다. 

 

하지만 recoil에서는 atom으로 상태를 정의하고 해당 상태를 변경해주는 것은 변경이 필요한 부분에서 setRecoilState를 이용해서 언제든 변경할 수 있다. 이런 점에서 같이 협업 프로젝트를 진행하면서 동료와 같은 상태를 가지고 개발을 할 때, 오류가 나면 상태 변화가 어디서 되고 있는지 디버깅이 불편하다.

 

이 부분은 우리가 개발하기 앞서 상태 변경 로직을 한 곳에 모아두기로 했으면 괜찮았을 거 같은데, 처음에는 useState 처럼 언제 어디서든 쉽게 가져다 쓰고 수정할 수 있다는 점에서 막 사용했던 거 같다.. 다음 recoil 프로젝트에서는 이런 상태 변경 로직을 관리하기 쉽게 모아서 사용하면 좋을 거 같다.

 

 

Devtools를 지원하지 않는다.

recoil에서 상태가 잘 관리되고 있는지, 잘 변경이 되었는지를 확인 하기 위해서는 console.log를 찍어서 확인해야 한다.  recoil 공식문서에서 useRecoilSnapshot() 이라는 hooks를 제공해주고 있지만, 아래와 같이 문구가 나와있다.

아직 완벽히 지원하는 기능은 아닌거 같아서 계속 console.log를 통해서 디버깅을 하였다.

 

기존 redux를 이용해 개발한 사람들을 알겠지만, devtools가 굉장히 잘되어있어서 디버깅이 엄청 쉽다. 미래의 recoil에서 devtools를 지원하면 너무 좋겠지만, 당장은 지원하지 않기 때문에 많이 불편함을 느꼈다.

 

 

너무나 부족한 자료

recoil은 아직 버전이 1도 되지 않고 실무에 사용하는 회사도 많지 않다. (토스에서는 recoil을 사용하는 걸로 알고 있다) 그래서 실무에서는 어떤 식의 폴더구조를 사용하고 있는지, 어떤 식으로 함수를 만들면 좋을지에 대한 자료가 많이 부족하다. 

 

 

 

마무리

이러한 이유로 우리 서비스에서는 redux를 사용하기로 하였다. 간단한 상태관리나 개인 프로젝트에서는 recoil을 사용하면 엄청 편하게 개발할 수 있을 거 같은데, 우리 같이 실무 경험이 없는 초보 개발자들끼리 프로젝트를 할 때는 아직 불편한 점들이 있는 거 같다.  

댓글