[TIL 0417] refetch의 문제점과 개선 방법 / cache-state 직접 업데이트 해보기

zitto·2023년 4월 18일
0

TIL

목록 보기
56/77
post-thumbnail

다음에 또 요청할 수 있기 때문에 여러컴포넌트에서 재사용할 수 있도록 글로벌 스테이트에 저장했다.
그렇게 되면 바로 API 요청하지 않고 글로벌 스테이트에서 확인 후 가져온다.(요청 수 감소 목적)
fetch-policy!!

DB에 추가됬는데 화면에 안보여서 refetch라는 걸 했고
조회된 목록을 다시 조회해서 변경했다.

useQuery()는 수행 후 cache-state에 저장되는데,
refetch를 사용하게 되면 새롭게 다시 받아오기 때문에 비효율 적인 구조가 되므로 좋은 방법이 아니다.

따라서 가급적 요청을 줄이기 위해 cache-state 직접 업데이트 해보기로 한다.

참고
https://velog.io/@zitto/TIL-0411-글로벌-스테이트


💡 apollo-cache-state를 직접 업데이트하는 방법

[실습 section 26]

import { useQuery, gql, useMutation } from "@apollo/client";
import type {
  IQuery,
  IQueryFetchBoardsArgs,
} from "../../../src/commons/types/generated/types";
const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
    fetchBoards(page: $page) {
      _id
      writer
      title
      contents
    }
  }
`;
// 캐시에 저장되는 데이터와 요청 후 받아오는 값이 일치되야 한다.
const CREATE_BOARD = gql`
  mutation createBoard($createBoardInput: CreateBoardInput!) {
    createBoard(createBoardInput: $createBoardInput) {
      _id
      writer
      title
      contents
    }
  }
`;
const DELETE_BOARDS = gql`
  mutation deleteBoard($boardId: ID!) {
    deleteBoard(boardId: $boardId)
  }
`;
export default function StaticRoutingPage(): JSX.Element {
  const { data } = useQuery<Pick<IQuery, "fetchBoards">, IQueryFetchBoardsArgs>(
    FETCH_BOARDS
  );
  console.log(data);
  const [나의함수] = useMutation(CREATE_BOARD);
  const [deleteBoard] = useMutation(DELETE_BOARDS);
  interface IPrev {
    // _id: string; //에러남? 왜?
    __ref: string;
  }
  const onClickDelete = (boardId: string) => () => {
    void deleteBoard({
      variables: {
        boardId,
      },
      // refetchQueries: [{ query: FETCH_BOARDS }], //성능 비효율적
      update(cache, { data }) {
        // 캐시를 수정한다는 뜻의 cache.modify
        cache.modify({
          // 캐시에있는 어떤 필드를 수정할 것 인지 key-value 형태로 적어준다.
          fields: {
            fetchBoards: (prev: IPrev[], { readField }) => {
              //여기서 prev는 fetchBoards!
              const deletedId = data.deleteBoard; // 삭제완료된 ID
              // const fileterdPrev = prev.filter((el) => el._id !== deletedId); //el은 게시글 하나씩 삭제된 아이디가 맞는지 확인하여 아닌애들 필터링(9개)
              const fileterdPrev = prev.filter(
                (el) => readField("_id", el) !== deletedId
              ); //el은 게시글 하나씩 삭제된 아이디가 맞는지 확인하여 아닌애들 필터링(9개)
              return [...fileterdPrev]; //삭제된 아이디를 제외한 나머지 9개를 리턴한다.
            },
          },
        });
        //삭제된 아이디 반환
      },
    });
  };
  const onClickSubmit = () => {
    void 나의함수({
      variables: {
        createBoardInput: {
          password: "1234",
          writer: "writer",
          title: "title",
          contents: "contents",
        },
      },
      // refetchQueries:[{query:FETCH_BOARDS}]  // 비효율적
      update(cache, { data }) {
        //구조분해할당  data는 11개의 데이터
        //globalstate의 cache임 (cache에는 기존의 데이터)
        cache.modify({
          fields: {
            fetchBoards: (prev) => {
              //fetchBoards에 있는 것이 return값으로 수정됨 10개 데이터를 prev로 받아옴
              return [data.createBoard, ...prev]; //fetchBoards 11개로 업데이트
            },
          },
        });
      },
    });
    console.log(result);
  };
  return (
    <div>
      {data?.fetchBoards.map((el) => (
        <div key={el._id}>
          <span style={{ margin: "10px" }}>{el.title}</span>
          <span style={{ margin: "10px" }}>{el.writer}</span>
          <button onClick={onClickDelete(el._id)}>삭제</button>
        </div>
      ))}
      <button onClick={onClickSubmit}>등록</button>
    </div>
  );
}


캐시를 직접 수정하기 위해 update(){} 라는 기능을 이용한다.

update 기능을 사용할때 파라미터로 데리고 온 cacheapollo-cache-state에 있는 global state!

그리고 업데이트 된 결과 는 파리미터의 {data} 에 들어오게 된다.

el._id 로 해주지 않는 이유는,
ref를 따라가야 나오기 때문임!
id에는 ref가 들어가 있음!

readField사용하면 ref를 참조해서 id를 뽑아올 수 있다.

state 바뀌어서 화면이 바뀜
추가적인 요청이 필요없음!

✔️ fields의 prev와 return값

  • fields는 캐시에 있는 어떤 데이터를 수정할 것 인지를 알려준다.
  • fields 의 prev : 이전데이터를 불러온다.
  • fields의 return값 : 이전 데이터를 return 값으로 바꿔준다 업데이트 된 결과가 {data} 이므로 결국 {data} 내부의 값이 된다

하지만 상황에 따라 다르다.
작은 규모의 서비스의 경우에는 굳이 API요청을 줄이기 위해
소스코드를 줄일 필요가 없다.
이런 걸 오버엔지니어링이라고 함!
굳이 필요하지 않은데 최적의 성능을 뽑으려고 하는 것

큰 규모의 서비스인 경우에는 API요청을 줄이는 것이
비용면에서도 효율적이다!

profile
JUST DO WHATEVER

0개의 댓글