본문 바로가기

Web.d

[리팩토링][북스테어즈] 9개월 묵은 코드 뜯어내기 (2) Recoil

반응형

https://snupi.tistory.com/213

 

[리팩토링][북스테어즈] 9개월 묵은 코드 뜯어내기 (1) 데이터 구조 변경

서론 나의 첫 협업 프로젝트인 북스테어즈는 지난 9개월 가까이 개발 진척이 잘 안되었다. 4 Depth 까지의 꼬리 질문/답변 기능이 주범이다. 절대적인 리소스 투입도 적었고, 클라이언트 팀에서의

snupi.tistory.com

 

(위 링크의 글을 보시면, 흐름 이해가 쉽습니다)

 

하나의 atom 상태를 가지고 컴포넌트마다의 커스텀 훅을 불러오려니, 중복적인 서버 통신이 일어난다.

Recoil 로써 캐싱을 할 방법은 없을지 알아보자.

 

 


Recoil Selector + Loadable

1. 효율적인 서버 통신

각 reviewId 에 맞는 책 정보 데이터를 비동기로 불러와 관리하고,

각 컴포넌트에서의 중복적인 데이터 패칭을 막기 위해 selectorFamily를 이용하였다.

이 때 selector는 캐싱 기능을 제공하며, 한번의 api call으로 처리할 수 있도록 도와준다.

 

Recoil은 📎 selector로 비동기 패칭을 지원한다.

구현 방법은 간단하니, 문서를 참고하면 좋을 것 같다.

Recoil 를 이용하여 비동기로 데이터를 패칭하는 여러 방법이 있다.

 

https://github.com/facebookexperimental/Recoil/issues/879

 

“fetch only one at the time, using a selectorFamily”

앞 포스팅에서 보였던 꼬리 질문/답변 기능은 “비동기로 패칭한 데이터를 기반한 전역 state”가 필요했다.

selectorFamily로 비동기 패칭을 구현하고, 이를 기반한 전역 state는 atom 으로써 동작하게끔 한다.

 

Recoil Selector

 

Selector 로 비동기 패칭이 완료된 데이터는 Atom 에 업데이트 한다.

이와 같은 구조로 설계함으로써 아래와 같이 수많은 중복 통신을 방지할 수 있었다.

 

Before (중복 통신)

 

After (Selector Async Fetching)

 

2. Suspense 처리?

⛔️

Recoil 은 Facebook에서 만들어진 라이브러리로, Suspense와의 호환성이 좋다.

하지만 작업을 하다보니

Next app 을 build 할 때, static page 를 생성할 때에 계속해서 timeout이 발생해 build가 되지 않는 이슈가 발생하기도 한다 (static-page-generation-timeout)

찾아보니, Next 에서 Recoil의 selector 와 Suspense 를 함께 사용할 수 없는 📎 이슈가 있었다.

 

왜 그럴까?

>

RQ, SWR과 같은 패칭 라이브러리는 fetch-on-render 의 방식으로,

컴포넌트 렌더링 이후에 네트워크 요청을 한다.

>

React18의 Suspense 기능은 render as you fetch 을 지원하는데,

네트워크 요청을 하고 컴포넌트 렌더링을 이뤄지도록 한다. 이 때, 데이터가 다 불러와짐(stale data)을 가정하고 fallback으로 넘겨준다.

에러 대신 Promise를 throw 하는 것을 Suspense가 감지할 수 있게 된다.

>

이를 이용해 Recoil async selector는 Promise를 throw 하여 Suspense를 지원하도록 하지만,

Next app의 SSR에서는 데이터 패칭이 끝나지 않아 timeout 이 일어나는 게 아닐까 하는 📎 의견이 있다.

 

이를 해결하기 위해 Recoil은 fetch-on-render 방식의 Loadable을 지원한다.

(아쉽지만 로딩처리에 대한 로직 구현을 Suspense 로의 추상화는 다음 과제로 남겨놓고,)

📎 useRecoilValueLoadable 훅으로 세가지 상태에 대한 예외 처리를 해주자. (이 역시 공식문서의 예제가 잘 나와있기에, 설명을 대신 해주리라 생각해요)

useRecoilValueLoadable 훅은 Suspense를 위해 비동기로 값을 읽어올 때 Error 혹은 Promise를 던지지 않고, “Loadable” 객체를 리턴한다.

hasValue, loading, hasError 3가지 상태의 Loadable 객체로써 컴포넌트를 분기 처리하여 나타낸다.

 

Next app Build

 

📎 Build 까지 완료 🔐

 


글을 줄이며

하여,

Recoil 을 활용해 atomic 한 전역적인 상태를 관리할 수 있었고,

해당 페이지에 대해 중복되는 서버통신을 캐싱하여 막아주었다

 

설계 양, 사용법이 다소 간단한 Recoil 을 사용하여 작업 시간이 그렇게 길지는 않았지만, 사용할 수록 아쉬운 부분들도 많았다

특히, 서버 사이드 데이터 관련한 기능이 react-query, swr에 비교도 안 될만큼 부족하고,

데이터 갱신과 같이 불안정한 기능들이 많다.. 📎 수많은 ISSUE들이 대기 중이다

그러나 간단하게 전역으로 상태를 관리하고, 로직을 효율적으로 설계하여 관리하기에는 굉장히 편리하였기에, 서비스에 맞게끔 맞는 기술을 사용하면 좋겠다.

 

북스테어즈는 이 작업을 시발점으로 다시금 작업을 시작하게 되었다.

개발을 처음 접하던 우리는 기본기에 충실하며 라이브러리를 최소화하는 개발을 하였다.

심지어 객체의 깊은 복사, Debounce, Unmount Animation 모두 직접 구현하였다.

그리고 지금, 로딩 및 에러처리나, 반응형 작업이나, 부분부분의 리팩토링, … 뜯어낼 구석이 많지만 :( …~

오히려 이 과정이 개념을 더 깊게 와닿게 해주었고 더 멀리 나아갈 수 있다는 자신감을 주었다.

이 모두가 팀원들에게 성장의 발판이 되기를 바란다.

반응형