좋아요 프로세스 👍🏻
브라우저에서 좋아요를 누르면 백엔드에 likeBoard API를 요청하고 DB에 접근해서 기존의 좋아요 수에 +1을 한다.
DB에서 돌려받은 값을 백엔드를 거쳐 브라우저에서 받아서 화면에 보여준다.
👇🏻 Optimistic UI를 적용하면 ~
globalState에 좋아요의 값을 +1 해놓고 globalState를 화면에 먼저 보여준다.
DB에서 받은 값과 globalState의 값이 일치하는지 확인한다.
결제처럼 중요한 데이터가 아니고, 실패할 가능성이 낮은 경우에 요런 눈속임으로 더 빠른 경험을 제공할 수 있다.
(useMutation을 사용하면 apollo의 globalState에 자동으로 저장이 되므로, globalState에 따로 저장해 줄 필요는 없다.)
optimistic UI를 이용하면 1,2번 방법을 이용했을 때보다 속도가 훨씬 빠르다!!
/* refetch - api가 LIKE_BOARD 한 번, FETCH_BOARD 한 번 해서 총 두 번 요청된다. */
refetchQueries: [
{
query: FETCH_BOARD,
variables: { boardId: "6269ecf7a8255b002988d65e" },
},
],
어따 써..???
mutation 요청하는 함수의 variables 다음에,
로 이어서 객체 형태로 적으면 된다.
mutation 함수 안에 optimisticResponse
써주면 API 응답이 오기 전에 보여줄 값을 지정할 수 있고, 응답이 오면 진짜 값으로 바꿔준다!
(좋아요일 경우, 성공하면 기존의 좋아요 수 + 1, 실패하면 기존의 좋아요 수가 그대로 들어온다.)
optimisticResponse: {
likeBoard: (data.fetchBoard.likeCount || 0) + 1,
// 기존의 좋아요 수 + 1
// undefined일 때는 0 + 1
},
cacheState의 값을 직접 바꿔줄 수 있다.
update(cache, { data }) {
// data는 likeBoard API의 response로 받는 값이다.
cache.writeQuery({
// 기존의 데이터를 직접 바꾼다.
query: FETCH_BOARD, // 바꿀 쿼리
variables: { boardId: "6269ecf7a8255b002988d65e" },
// 위에서 useQuery 선언할 때 쓴 쿼리명과 variables를 그대로 똑같이 써야 한다.
data: {
// 조작할 것을 적는다. 여기서는 data를 조작한다.
fetchBoard: {
_id: "6269ecf7a8255b002988d65e",
__typename: "Board",
// id와 typename은 조건이다.
// 이 두개의 조건을 가지고 globalState에 저장된 것에서 찾아내기 때문에 id와 typename은 둘 다 꼭~ 입력해야 한다.
// likeCount:10, // 요로케 입력하면 기존에 fetch해온 값을 무시하고 여기에서 입력한 10이 된다.
// (백엔드의 값과는 상관 없이 조작만 하는 것이다.)
likeCount: data.likeBoard, // data.likeBoard : likeBoard의 결과이다.
},
},
});
},
전체 코드
import { gql, useMutation, useQuery } from "@apollo/client";
const FETCH_BOARD = gql`
query fetchBoard($boardId: ID!) {
fetchBoard(boardId: $boardId) {
_id
likeCount
}
}
`;
const LIKE_BOARD = gql`
mutation likeBoard($boardId: ID!) {
likeBoard(boardId: $boardId)
}
`;
export default function OptimisticUIPage() {
const { data } = useQuery(FETCH_BOARD, {
variables: { boardId: "6269ecf7a8255b002988d65e" },
});
const [likeBoard] = useMutation(LIKE_BOARD);
const onClickOptimisticUI = () => {
likeBoard({
variables: { boardId: "6269ecf7a8255b002988d65e" },
optimisticResponse: {
// API 응답이 오기 전에 일단 이 값을 보여주고,
// 응답이 오면 진짜 값으로 바꿔준다!
// 좋아요가 실패하면 이전 값으로 들어오게 된다.
likeBoard: (data.fetchBoard.likeCount || 0) + 1,
},
/* 직접 cache를 수정해서 globalState를 바꿔주는 방법, optimisticResponse를 같이 사용할 수 있다. */
update(cache, { data }) {
// data: likeBoard의 결과값
// data는 likeBoard API의 response로 받는 값이다.
cache.writeQuery({
// 기존의 데이터를 직접 바꾼다.
query: FETCH_BOARD,
variables: { boardId: "6269ecf7a8255b002988d65e" }, // 위에서 useQuery 선언할 때 쓴 쿼리명과 variables를 그대로 똑같이 써야 한다.
data: {
// 조작할 것을 적는다. 여기서는 data를 조작한다.
fetchBoard: {
_id: "6269ecf7a8255b002988d65e",
__typename: "Board", // 조건: 이 두개를 가지고 globalState에 저장된 것에서 찾아내기 때문에 id와 typename은 둘 다 꼭~ 입력해야 한다.
// likeCount:10, // 요로케 입력하면 기존에 fetch해온 값을 무시하고 여기에서 입력한 10이 된다. (백엔드의 값과는 상관 없이 조작만 하는 것이다.)
likeCount: data.likeBoard, // data.likeBoard : likeBoard의 결과이다.
},
},
});
},
});
};
return (
<>
<div>좋아요 {data?.fetchBoard?.likeCount}</div>
<button onClick={onClickOptimisticUI}>좋아요 올리기</button>
</>
);
}