문제 상황: 댓글 기능을 만드는 중 댓글이 등록되어 해당 댓글이 속한 리스트 데이터가 변경 되었는데, 변경된 데이터가 클라이언트 측으로 바로 갱신되지 않는 문제가 발생했다.
이 때문에 사용자가 댓글을 쓴 바로 직후 화면이 업데이트 되지 않고 새로고침을 하고 나서야 화면에 반영되는 것이다.
SWR 을 이용해서, 서버에 보낸 변경된 데이터가 실시간으로 클라이언트에도 적용되도록 하고싶은데
바로 반영이 안되었던 이유는, SWR 이 캐싱과 재사용을 기반으로 동작하기 때문에 데이터의 변경이 즉시 반영되지 않을 때가 있기 때문이었다. 😱
그렇기에 데이터를 변경한 후 캐시를 새로 갱신해야했고, SWR 에서 제공하는 mutate
라는 함수를 사용하여 데이터를 캐싱 및 업데이트 할 수 있었다.
mutate
함수는 캐시된 데이터를 직접 갱신하고 컴포넌트를 리렌더링 해준다. 예를 들어, 데이터를 변경한 후에 mutate 함수를 호출하여 캐시된 데이터를 갱신할 수 있다.
// mutate 함수를 import
import useSWR, { mutate } from 'swr';
// 데이터 변경 후의 시점에 mutate 함수 호출
mutate('/api/data', updatedData);
이렇게 mutate
함수를 호출하면 SWR은 해당 키의 데이터를 다시 가져오고 캐시된 데이터를 갱신한다. 컴포넌트도 자동으로 리렌더링되어 변경된 데이터가 반영된다.
위의 방식은 SWR 의 함수인 mutate
를 전역으로 사용하는 방식이고, 그 안에 특정 경로를 넣으면 된다.
우리는 아래처럼 useSWR
훅을 사용하는 방식으로 해결했다. useSWR
훅은 데이터를 가져오기 위해 사용 되지만 반환값으로 mutate
함수 또한 제공된다. 이 방식은 우리가 현재 useSWR
훅을 통해 가져온 데이터를 갱신하고자 할 때 사용 되는데, 일반적으로 해당 방법이 더 권장되고 있다고 한다!
...
const { data: commentListData, mutate, isLoading } = useSWR(
router.isReady &&
router.pathname.split('/')[1] === 'daily' &&
router.query.id
? `/api/manager-comment?${$queryToString(router.query)}¤tPage=1&dutyDiarySeq=${router.query.id}`
: null
)
...
const handleCommentSubmit = async (body) => {
try {
...
)
if (code === 'SUC001') {
await alertPop('success', '댓글이 작성되었습니다.')
await mutate
} else {
...
mutate()
함수는 경로를 명시적으로 지정하지 않았을 때 기본적으로 이전에 사용한 경로(useSWR
훅을 호출할 때 전달한 경로와 동일한 경로)에 대한 캐시를 갱신하고 갱신 후 컴포넌트가 다시 렌더링된다.
mutate()
함수는 호출한 컴포넌트의 경로에 대한 캐시를 갱신하므로 다른 컴포넌트의 캐시는 영향을 받지 않는다. 경로가 다른 컴포넌트의 데이터를 갱신하려면 해당 컴포넌트에서 mutate()
함수를 호출해야 한다.