Next.js에서 SWR을 사용해 보자 - 2 (Mutate)

이준혁·2022년 5월 27일
1

next-tutorials

목록 보기
7/7
post-custom-banner

이 글은 Next.js에서 SWR을 이용해 데이터를 변경하는 방법에 대해 설명합니다.
다음 지식들을 알고 이전 포스트를 읽으셨다면 문제없이 읽을 수 있습니다.

  • React 기초 문법
  • Next.js 기초 문법
  • Typescript
  • ES6

이 글은 Next JS 강의를 참고해 작성되었습니다.


Mutate

이전 포스트에서는 SWR을 이용해 데이터를 가져오는 작업을 진행했습니다. 그러나 만약 가져온 데이터를 변경해야한다면 어떻게 해야 할까요? SWRmutate라는 함수를 제공함으로 인해 데이터를 변경할 수 있도록 제공합니다.

Example

다음 그림과 같은 좋아요 기능을 추가한다고 생각해 봅시다. velog에도 있는 기능이죠.

1. 좋아요 상태가 아닐때 버튼을 클릭하면, 좋아요 상태로 바꾸고 좋아요 수 1 증가
2. 좋아요 상태일 때 버튼을 클릭하면, 좋아요가 아닌 상태로 바꾸고 좋아요 수 1 감소

아래 코드는 위 상태 변화를 구현한 코드 일부입니다. API 및 프론트엔드 부분에 대해서는 코드 전문을 참고해 주시기 바랍니다.

interface HeartInfo {
  isLiked: boolean;
  likes: number;
}

export default function ShopIdElement() {
  const { data, mutate } = useSWR<HeartInfo>("/api/heart");

  const onClicked = async () => {
    if (!data) return;
    
    // Change data
    mutate({
      isLiked: !data.isLiked,
      likes: data.isLiked ? data.likes - 1 : data.likes + 1,
    });
	
    // Send API request for heart
    await axios.post(`/api/heart`);
  };

여기서 onClick 함수 내에서 사용한 mutate 함수는 SWR에서 받아온 data를 변경하는 역할을 합니다. mutate 함수의 첫 번째 인자data가 변경되게 됩니다.

그러나 위 코드를 실행하면 잠시 변경되었다가 다시 원래대로 돌아올 거나, 아예 변하지 않을 것입니다. 왜 그럴까요?

Revalidate

위 코드를 다시 살펴 봅시다.

// 1. Change local data first
mutate({
  isLiked: !data.isLiked,
  likes: data.isLiked ? data.likes - 1 : data.likes + 1,
});

// 2. Send API request for make/delete heart
await axios.post(`/api/heart`);

mutate 함수의 두 번째 인자true 혹은 빈 값을 준 경우, 다음과 같이 동작합니다.

1. 로컬 데이터를 변경한다.
2. SWR에서 데이터를 fetch 한다. (fetcher 함수 이용)
3. fetch한 값으로 데이터를 다시 변경한다.

이렇게 하는 이유는 우리가 로컬에서 변경한 데이터가 정말 유효한지 확인하기 위해서 검증(revalidate)하는 것입니다. 하지만 2번 과정에서 데이터를 가져올 때, 과거 데이터를 가져올 수 있게 됩니다. POST 요청을 통해 변경된 내역이 아직 기록되지 않았을 수 있으니까요. 따라서 과거 데이터로 돌아가는 듯한 모습을 보여주게 됩니다.

mutate(
  {
    isLiked: !data.isLiked,
    likes: data.isLiked ? data.likes - 1 : data.likes + 1,
  },
  false
);

위와 같이 두 번째 인자로 false를 주게 되면, 2,3번 검증 과정을 생략합니다. 이 방법은 제대로 동작할 뿐만 아니라, 실제 DB에 값이 반영되는 걸 기다리기 전에 미리 local의 값을 변경하므로 굉장히 빠르다고 느낄 수 있습니다.

Unbound

지금까지 저희가 mutate를 진행했던 data/api/heart로 가져온 data입니다. 즉, data/api/heart 라는 keybind 되어 있다고 보시면 됩니다.

하지만 만약 좋아요 버튼을 눌렀을 때, 현재 페이지에서 사용되지 않는 데이터도 변경하고 싶다면 어떻게 해야할까요? 이를테면 유저 정보를 보는 페이지에서 사용하는 /api/user 에서 가져온 데이터도 바꾸고 싶다면 어떻게 해야 할까요?

다음과 같이 mutate 함수를 useSWRConfig에서 가져오면 됩니다.

const { data, mutate:boundMutate } = useSWR<HeartInfo>("/api/heart");
const { mutate: unboundMutate } = useSWRConfig();

const onClicked = async () => {
  if (!data) return;
  boundMutate(
    {
      isLiked: !data.isLiked,
      likes: data.isLiked ? data.likes - 1 : data.likes + 1,
    },
    false
  );
  unboundMutate("/api/user", (prev:any) => ({ ok: !prev.ok }), false);

  await axios.post(`/api/heart`);
};

이름이 겹치기 때문에 /api/heart에서 가져온 mutate 함수는 boundMutate, useSWRConfig에서 가져온 함수는 unboundMutate라고 명명합시다. unboundMutate 함수는 boundMutate 함수와 다음과 같은 차이점이 있습니다.

1. 첫 번째 인자로 mutate할 data의 key값을 준다. (어떤 data를 바꿀지 모르기 때문)
2. 두 번째 인자로 data가 아닌, data를 변경하는 함수를 준다. (data가 없기 때문)

unboundMutate 함수를 이용하면, 현재 페이지에서 가져오지 않은 데이터를 변경할 수 있게 됩니다.


마치며

코드 원본은 여기를 참고해 주시면 됩니다.

참고자료

  1. SWR 공식 페이지
  2. SWR Mutation
  3. Next JS 강의
profile
만들고 개선하는 것을 좋아하는 개발자
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 11월 9일

정리를 정말 잘하시네요!

답글 달기