전역 상태 관리에 대한 고민 - 유저정보(로그인정보) (feat. recoil)

younoah·2022년 3월 15일
1

와플카드 서비스가 궁금하다면!
와플카드 서비스 둘러보기 : https://waffle-card.com/
와플카드 깃허브 둘러보기 : https://github.com/waffle-card


비동기 데이터를 전역상태로 관리해야할 때 어떻게 해야할까

기존에는 네트워크의 요청 결과로 받은 데이터를 전역상태로 관리할 때 아래와 같은 구조로 설계를 했었다.

컴포넌트 내부에서 api요청을 보내고 응답 데이터를 받아와서 스토어에 저장하는 방식이다.

그러던 중 원지혁님의 컴포넌트 다시생각하기 컨퍼런스 발표영상을 보고 생각을 바꾸게 되었다.

발표영상의 핵심은 관심사 분리는 아니지만 네트워크 요청과 전역상태에 저장하는 모든 로직을 컴포넌트에서 한번에 관리하는것은 권한이 너무 지나치고 컴포넌트가 너무 무거워 진다고 생각이 들었다. 따라서 관심사 분리가 필요하다고 생각이 들었고 아래와 같이 구조를 변경해야겠다고 생각이 들었다.

컴포넌트는 단순히 상태가 필요하다면 스토어에게 데이터를 요청한다. 스토어에 만약 캐싱된 데이터가 있다면 반환을 해주고 없다면 네트워크 요청을 하여 데이터를 받고 캐싱한뒤 데이터를 반환한다.

이 때의 이점은 컴포넌트는 뷰와 관련된 처리만을 집중하여 개발할 수 있고 스토어에서는 데이터와 관련된 로직을 한번에 관리할 수 있기 때문에 유지보수성이 증가할 수 있다고 생각한다.


와플카드의 유저정보

와플카드 서비스에서 로그인을 한 뒤 로그인 정보를 전역상태로 관리하려고 했다. 위에서 소개했던 구조로 설계하기 위해서 context를 활용하여 직접 커스텀하게 작성해도 되었지만 이번에는 recoil의 selector를 활용해보기로 했다.

recoil의 selector를 활용하면 비동기 처리를 한번에 쉽게 할 수 있기 때문이다.

import { atom, selector } from 'recoil';
import { authApi } from '@/apis';
import { TOKEN_NAME } from '@/constants';

export const userState = atom({
  key: 'user',
  default: selector({
    key: 'user/get',
    get: async () => {
      const token = sessionStorage.getItem(TOKEN_NAME);
      if (!token) return null;

      try {
        const {
          data: { id, email, name },
        } = await authApi.me();

        return { id, email, name };
      } catch (error: any) {
        sessionStorage.removeItem(TOKEN_NAME);
        console.error(`in User Recoil: ${error.message}`);
        return null;
      }
    },
  }),
});

위 코드의 내용은 보면, usetState에서 selector를 활용하면 비동기 로직을 처리하고 있다. 토큰을 검사하고 유저정보를 받아오는 네트워크 요청을 진행하고 해당 유저정보를 반환을 해준다.

selector는 기본적으로 값을 캐싱한다. 이로 인해 데이터가 존재한다면 네트워크 요청을 처리하지 않고 데이터를 즉시 반환하기 때문에 효율적으로 데이터를 관리하고 네트워크 요청을 관리할 수 있게 된다.


recoil 단순하고 너무 좋은데?? 그치만?

하지만 recoil에서 이 캐싱이 악이 될 수도 있다. 한번 캐싱이 되면 데이터가 바뀌었음을 감지하지 못하고 계속 저장된 데이터를 반환하는 문제점이 있다. 이를 처리하는 방법을 recoil에서 제공해줄것 같은데 아직 거기까지 알아보지 못했다.

만약 커스텀하게 캐싱을 관리하려면 context를 사용해야하나 고민이 들었는데 최근에 리액트 쿼리라는것을 알게되었다. 비동기 로직을 리액트스럽고 간단하게 관리하고 캐싱의 문제도 쉽게 해결할 수 있다고 한다. 기회가 된다면 리액트 쿼리에 대해서 알아 보도록 해야겠다.

profile
console.log(noah(🍕 , 🍺)); // true

0개의 댓글