apollo cache를 활용하는 pagination (offset 기반)

hyeok·2022년 7월 28일
0

페이지네이션에는 크게 cursor기반 페이지네이션과 offset기반 페이지네이션이 있다.
지금 진행중인 프로젝트에선 offset기반으로 페이지네이션을 하고 있으니 그것부터 다루겠다.
지금 부터 다루는 기준은 antdesign table에서의 pagination이다.

일반적인 pagination이 아닌 apollo cache를 활용한 pagination 구현을 이야기할 것이다.

pagination에서 apollo cache를 활용했을 때

장점
1. 업데이트시 리스트 정보를 리패치를 안해도 되서 서버 부하를 줄인다.
2. 페이지를 한번 받아오면 다시 받지 않아도 되기에 로딩이 없고 사용자 ux를 향상시킨다.

단점
1. 실시간으로 업데이트되는 정보를 확인할 수 없다. (refresh가 필요)
2. 캐쉬가 잘못 관리될 경우 보이는 데이터가 잘못된 데이터를 나타냄

지금 회사에서 운영하는 데이터는 실시간 데이터가 아니므로 cache를 활용해서 관리하는 게 좋다.
페이지네이션이 된 데이터 캐시는 하나로 merge해서 관리한다.

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        Admin_Event_find: {
          read(existing, { args: { offset, limit } }: any) {
            if (!existing) {
              return undefined;
            }
            const data = existing.items.slice(offset, Number(offset) + Number(limit));
            if (data.length === 0) {
              return undefined;
            }

            return existing;
          },
          keyArgs: [],
          merge(existing, incoming, { args: { offset = 0 } }: any) {
            const merged = existing ? existing.items.slice(0) : [];

            for (let i = 0; i < incoming.items.length; i += 1) {
              merged[Number(offset) + i] = incoming.items[i];
            }

            return {
              ...existing,
              totalCount: incoming.totalCount,
              items: merged,
            };
          },
        },
      },
    },
  },
});

위와 같이 apollo.ts에 적용시켰다.
merge는 가져온 Admin_Event_find 필드에 객체가 들어왔을때 어떻게 합치는지 설정하는 부분이다. Admin_Event_find는 event 리스트를 가져오는 query이다.
위의 merge function에 의하면 새로 들어왔을 때 해당 event들을 합쳐서 캐쉬에 저장한다.

read는 보여주는 부분이다. 우리는 offset 기반으로 pagination을 했으니 해당 페이지에 있는 캐시 리스트에서 에서 offset 기준 limit개수 만큼만 가져오면 된다.

이렇게 merge 하지 않으면 cache는 다른 offset마다 다르게 저장된다. 이렇게 merge를 해야 하나의 리스트 캐쉬에 추가적으로 붙는다.

create, update, read만 cache로 작업하고자한다. 왜냐하면 delete을 하게 될 경우 한칸씩 앞으로 밀리는데 이때 뒤의 것을 따로 가져와야한다. 이게 가능하려면 해당 offset에서 다음 offset의 데이터를 미리 가져와야하는데 이건 서버 리소스를 늘릴 뿐더러 버그를 일으킬 확율이 높다. 그래서 delete의 경우 refetch로 작업했다.

create의 경우

const createResult = await AdminCreateEvent({
      variables: { data: { ...variables } },
      update(cache, { data: eventData }) {
        
        // 기존 캐시 정보를 가져오고
        const eventsCache = cache.readQuery({
          query: GET_EVENT_LIST,
          variables: { ...optionFilter },
        });
        
        // 이벤트 리스트에 추가 한 뒤
        const newEvent = eventData.Admin_Event_create;
        const eventList = eventsCache.Admin_Event_find.items.slice(0, -1);
        const totalCount = Number(eventsCache.Admin_Event_find.totalCount);
        const addedEventList = [newEvent, ...eventList];
    	
        if (newEvent) {
        // 	캐시에 넣는다. 
          cache.writeQuery({
            // eslint-disable-next-line
            id: cache.identify(eventList as StoreObject),
            query: GET_EVENT_LIST,
            data: {
              Admin_Event_find: {
                items: addedEventList,
                totalCount,
              },
            },
          });
        }
      },
    });

update의 경우도 비슷하다.
물론 refetch로 모두 간단하게 해결될 수는 있다. 하지만 이렇게 캐시를 이용한 관리를 하면 좀더 사용자 경험을 높일 수 있고, 불 필요한 서버 리소스를 줄일 수 있다.

profile
내가 만든 소프트웨어가 사람들을 즐겁게 할 수 있기 바라는 개발자

0개의 댓글