HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🚀
개발 노트
/
🎨
아트집 - 본론
/
📜
<아트집> 기술 문서
/
📌
버그 해결 - SWR의 캐시 이슈(2)
📌

버그 해결 - SWR의 캐시 이슈(2)

 
지난 이야기SWR의 mutate란? 프로젝트 적용CommunityPageReviewFeedReviewDetail
 

지난 이야기

SWR의 캐시 이슈 2탄이다.
1탄에서는 SWR의 최신 데이터가 반영되지 않는 버그가 존재하였고,
useEffect를 통해 최신 데이터를 직접 반영해주었다.
 
useEffect(() => { setIsLikedFeed(isLiked); setFeedLikeCount(likeCount); }, [isLiked, likeCount]);
 
그러나 이 방법은 임시방편이며, 불완전하다는 느낌을 지울 수 없다.
특히 페이지 마운트 이후, 변경사항을 직접 반영해주는 방식이므로
이전 데이터가 잠깐이지만 보이는 현상이 발생한다.
아래 영상을 참고해주시기 바란다.
 
 
좋아요가 깜빡거리며 바뀌는 현상이 관찰된다.
어떻게보면 사소하다고 할 순 있지만, UX 측면에서 좋다고 할 수 없는 문제이다.
무엇보다, SWR의 캐시 데이터를 제대로 활용하지 못하고 있다는 생각이 든다.
 

SWR의 mutate란?

 
notion image
 
SWR의 mutate는 캐시된 데이터를 mutate(변화)할 수 있는 함수이다.
useSWRConfig로부터 mutate 함수를 얻을 수 있으며,
mutate(key)를 호출하여 동일한 키를 사용하는
다른 SWR hook에게 전역적으로 갱신 명령을 내릴 수 있다.
 
예를 들어, ’/reviews/123’이라는 키를 갖는 SWR hook이 여러 개 있다면
mutate(’/reviews/123’)를 사용함으로써
해당 키를 가진 hook들의 캐시 데이터를 모두 갱신할 수 있는 것이다.
 
mutate(key, data, options)
 
mutate의 첫 번째 인자는 key값이다.
두 번째 인자로 갱신할 데이터가 있다면 넣어준다.
세 번째 인자는 옵션으로, revalidate 및 optimisticData 등의 옵션이 존재한다.
자세한 것은 SWR의 공식문서를 참고해주기 바란다.
 
뮤테이션 - SWR
optimisticData: 클라이언트 캐시를 즉시 업데이트하기 위한 데이터. 일반적으로 낙관적인 UI에서 사용됩니다. revalidate: 비동기 업데이트가 해소되면 캐시를 갱신합니다. populateCache: should the result of the remote mutation be written to the cache, or a function that receives new result and current result as arguments and returns the mutation result.
https://swr.vercel.app/ko/docs/mutation
뮤테이션 - SWR
 

프로젝트 적용

문제가 발생한 CommunityPage, ReviewFeed, ReviewDetail에 적용할 것이다.
CommunityPage에 여러 개의 ReviewFeed가 있는 형태이며,
각각의 ReviewFeed를 클릭하면 ReviewDetail로 이동하는 구조다.
이 세 개의 컴포넌트는 서로 연결되어 있어서
캐시 데이터를 함께 관리해야 하는 다소의 어려움이 존재하였다.
 

CommunityPage

notion image
 

ReviewFeed

notion image
 
우선 useSWRConfig 함수를 통해 mutate를 가져온다.
const { mutate } = useSWRConfig();
 
좋아요가 클릭되면 handleLikeClick 함수가 호출된다.
여기에 mutate를 추가해보자.
const handleLikeClick = async (reviewId: number) => { // 낙관적 업데이트 setIsLikedFeed(!isLikeFeed); setFeedLikeCount(isLikeFeed ? feedLikeCount - 1 : feedLikeCount + 1); const { data } = await reviewAPI.likeToggle(reviewId); const { likeCount, isLiked } = data.data; setIsLikedFeed(isLiked); setFeedLikeCount(likeCount); // mutate 추가 mutate(`api/v1/reviews/${reviewId}`, undefined, { revalidate: true }); };
 
api/v1/reviews/${reviewId}라는 key를 가진
SWR hook에게 캐시 유효성 검증 명령을 내린다.
이것은 ReviewDetail에서 사용하는 key이다.
즉, ReviewFeed의 데이터가 갱신되었으므로
ReviewDetail에도 캐시 데이터의 갱신 명령을 내리는 것이라고 할 수 있다.
 

ReviewDetail

notion image
 
ReviewFeed를 클릭하면 ReviewDetail로 이동한다.
여기에도 mutate를 적용해보자.
역시 useSWRConfig 함수를 통해 mutate를 가져온다.
const { mutate } = useSWRConfig();
 
ReviewDetail에서도 좋아요를 클릭하면
handleLikeClick이라는 함수가 호출된다.
const handleLikeClick = async (reviewId: number) => { setIsLikedFeed(!isLikeDetail); setDetailLikeCount(isLikeDetail ? detailLikeCount - 1 : detailLikeCount + 1); const { data } = await reviewAPI.likeToggle(reviewId); const { likeCount, isLiked } = data.data; setIsLikedFeed(isLiked); setDetailLikeCount(likeCount); // 추가한 부분 mutate( `api/v1/reviews/${reviewId}`, { ...reviewDetail, isLiked, likeCount, }, { revalidate: false }, ); };
 
이번에도 api/v1/reviews/${reviewId}라는 key를 가진
SWR hook에게 캐시 갱신 명령을 내린다.
그러나 이번에는 클라이언트에서 갱신할 데이터를 가지고 있는 상황이다.
reviewDetail이 그렇고, isLiked와 likeCount도 가지고 있다.
따라서 굳이 서버에서 유효성 검증을 할 필요는 없다.
두 번째 인자의 데이터로 캐시를 갱신하라고 넣어주고,
세 번째 인자에는 revalidate: false를 통해 유효성 검증을 하지 않도록 한다.
 
 
 
사전에 말해두고 싶은 것은,
이 페이지 및 컴포넌트들은 팀 동료의 작업물이란 사실이다.
그렇기 때문에 왠만하면 코드를 너무 많이 수정하고 싶지는 않았다.
필요한 곳에만 mutate를 적재적소에 사용하여 문제를 해결하고 싶었다.
 
 
 
그래서 muate를
 
 
 
코드의 로직을 많이 수정하지 않고 mutate를 적용하도록 하겠다.