로컬 상태 관리 Apollo Client에 위임하기 'local-only fields'

Hyodduru ·2022년 12월 23일
0

TIL

목록 보기
8/8
post-thumbnail

client측에서만 사용하는 상태관리를 해야할 때가 있다. 따로 서버 측으로부터 데이터를 요청하는 것이 아니라, client 측에서 localStorage, cookie 등으로 관리를 해주어야 할 때가 있는데, Apollo Client에서는 이때 로컬 상태 관리를 직접 해주는 'local-only fields'를 제공한다!

apollographql - local-only fields 출처
https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies

최근에 사용자들의 게시글 노출 유무를 localStorage에 저장하여 게시글을 보여줄지 말지를 결정하는 업무를 진행한 적이 있는데, 이 때 알게 된 local-only fields라는게 넘 유용하고 신기해서 공유해보긔 ^-^*

예시를 들어보면,

내가 하고자하는 것

  1. 게시글의 닫힘 버튼을 클릭했을 때 localStorage에 해당 사용자의 id와 속한 페이지의 id를 저장하고자 함.
  2. 저장된 localStorage의 value에 사용자의 정보가 있을 시, 해당 페이지의 사용자에게는 해당 게시글을 노출하지 않음.

방법

1. 로컬로 관리할 field 뒤에 @client 라는 기호를 붙혀준다.

나는 getCheckedPost 전체를 로컬로 관리해주기 위해 그 뒤에 @client 를 붙혀주었다.
Post.query.graphql

query CheckedPostQuery($userId: String!) {
  checkedPost: getCheckedPost(userId: $userId)
    @client {
    pageIds
  }

2. 쿼리의 타입을 지정해준다.

graphql/client-schema.graphql 내에서 따로 client용 쿼리들의 타입을 관리해준다.

type CheckedPost {
  pageIds: [String!]
}

type Query {
  getCheckedPost(userId: String!): CheckedPost
}

3. makeVar를 활용하여 로컬 변수를 초기화해준다.

apollo client에서 제공하는 makeVar를 통해 변수를 만들 수 있는데, 이때 초기값을 설정하고, 어떤 식으로 해당 변수를 업데이트 시킬지를 만들어줄 수가 있다.

나는 localStorage에 유저와 해당 페이지 정보를 담아주고 싶어서 아래와 같이 만들어주었다.

import { makeVar } from '@apollo/client';

interface CheckedPost {
  pageIds: string[];
}

const CHECKED_POST_KEY = 'CHECKED_POST';

const initialCheckedPostMapVar =
  JSON.parse( window.localStorage.getItem(CHECKED_POST_KEY) ?? '{}'
  ) || {};

export const checkedPostMapVar = makeVar<
  Record<string, CheckedPost>
>(initialCheckedPostMapVar);

export const updateCheckedPost = (
  userId: string,
  pageId: string
) => {
  const current = checkedPostMapVar();
  const updated = {
    ...current,
    [userId]: {
      pageIds: [...(current?.[userId]?.pageIds ?? ''), pageId],
    },
  };

  window.localStorage.setItem(
    CHECKED_POST_KEY,
    JSON.stringify(updated)
  );

  checkedPostMapVar(updated);
};

4. apollo cache에서 내가 받고 싶은 데이터 형식대로 조작해줌.

apolloClient 파일 내에서 조작하고 싶은 데이터 형태로 만들어줌.

아래 getCheckedPost는 userId의 유무를 통해 값을 return할지 말지를 결정해주고 싶기 때문에 다음과 같이 작성하였다.

const cache = new InMemoryCache({

getCheckedPost: {
       read(_, { variables }) {
         if (!variables?.userId) {
            return;
          }

 const checkedPostUserId = checkedPostMapVar()[
              variables?.userId
            ];
   
   // 게시글을 check했는지를 확인해주는 state 만들어주기
 const isChecked = checkedOnboardingPinPostUserId?.classroomIds?.includes(
              variables?.classroomId
            );

        return {...checkedPostUserId, isChecked,} ?? null;
          },
        },

4. 만든 쿼리 써먹기!

써먹는게 젤 쉽죠잉

import { updateCheckedPost } from './CheckedOnboardingPinPostVar';
import {
  useCheckedPostQuery,
} from 'common/apollo/graphql';

// 생략

const { data: postData } = useCheckedPostQuery({
    variables: {
      userId,
    },
  });

// 위의 useCheckedPostQuery로부터 받아온 데이터로, 
// 게시글을 보여줄지 말지를 결정하는 변수
const hidePost = CheckedPinPost.isChecked 


// 버튼에 onClick으로 달아주면, 자동으로 
// localStorage에 바로바로 update 시킬 수 있다! 
 const handlePostClose = () => {
    updateCheckedPost(userId, pageId);
  };

암튼 사실 써먹느라 힘들었지만 넘 신기해서 기억하려고 적어두긔 ~!

graphql과 apollo client를 사용할 때, 로컬에서만 상태관리를 해주고 싶을 때 사용하면 좋은 local-only fields 였습니다!

profile
꾸준히 성장하기🦋 https://hyodduru.tistory.com/ 로 블로그 옮겼습니다

0개의 댓글