티스토리 뷰

게시물에 좋아요 버튼을 Server Action으로 구현했습니다

서버컴포넌트 서버액션은 많은 장점이 있지만 유저 인터렉션이 필요한 부분에서는 클라이언트 컴포넌트를 사용해야 합니다.

위 코드는 서버에서 함수를 실행하고 Js 번들에 포함되지 않고 서버에서 한 번만 랜더링 되겠지만

postLikeSA(서버액션)이 실행되고 성공하면 revalidateTag()로 getIsLiked() fetch 함수를 초기화해서 다시 상태를 업데이트합니다.

아무리 빨라도 당연히 딜레이가 생기게 됩니다.

위 이미지는 클라이언트와 서버가 api엔드포인트로 직접 상태를 업데이트하는 과정을 나타낸 그림이지만

왼쪽에 모니터 이미지를 Nextjs 서버로 생각하시면 되겠습니다.

백엔드 서버에서 200 응답을 받으면 getIsLiked()를 호출해서 새 결과를 받은 후에 재랜더링을 해서 내려주게 됩니다.

그러면 아래와 같이 약간의 딜레이가 느껴지고 답답한 상황이 나타나게 됩니다.

 

계속클릭을 해도 서버의 응답을 기다려야 하기 때문에 바로바로 업데이트가 안되고 심지어 화면에서는 서버에러도 나네요...

당연히 서버컴포넌트 서버액션을 쓰지 않아도 마찬가지입니다. 이것은 csr, ssr, 서버컴포넌트의 문제가 아니라 프론트엔드 개발을 하면 누구나 겪게 되는 불편한 상황입니다. 그래서 react 18.2 버전 현재 라이브러리 자체적으로 optimistic update기능을 구현할 수 있도록 시험적인 기능으로 훅이 만들어져 있었습니다. 

예전에 next12 버전에서는 비동기 통신을 위해 react-query를 사용할때는 react-query에서 제공하는 optimistic update기능을 사용했었습니다.

const queryClient = useQueryClient()

useMutation({
  mutationFn: updateTodo,
  // When mutate is called:
  onMutate: async (newTodo) => {
    // Cancel any outgoing refetches
    // (so they don't overwrite our optimistic update)
    await queryClient.cancelQueries({ queryKey: ['todos'] })

    // Snapshot the previous value
    const previousTodos = queryClient.getQueryData(['todos'])

    // Optimistically update to the new value
    queryClient.setQueryData(['todos'], (old) => [...old, newTodo])

    // Return a context object with the snapshotted value
    return { previousTodos }
  },
  // If the mutation fails,
  // use the context returned from onMutate to roll back
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previousTodos)
  },
  // Always refetch after error or success:
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})

그다지 복잡하진 않습니다. onMutate될때는 쿼리데이터를 가져와서 직접 업데이트를 해주면 되는구나.

에러가 발생하면 수정 전의 데이터를 세팅해주면 되돌아가는구나. 

근데 이게 데이터가 복잡해지면 은근 코드가 많아지고 복잡해지더라고요. 

 

이번에 Next14 버전을 사용하면서 react-query를 중간에 삭제했습니다. 왜냐하면 전혀 안 쓰고 있었거든요.

fetch함수와 server action으로 모든 것을 처리할 수 있었습니다. Next하나만으로도 캐시도 잘 되고요! 최적화도 잘 되어 있습니다.

근데 이번에 useOptimistic을 사용해 보니 아 정말 앞으로도 쓸 일이 없겠는데..?라는 생각을 하게 되었어요. 

프론트엔드 개발자가 구현하고 싶은 optimistic update기능이 뭔지 아주 잘 알고 구현하기 너무 쉽게 잘 만들어져 있습니다.

마치 '네가 필요한 거 이거지? 이거 찾는 거지?'라고 속삭이는 것 같습니다.

useOptimistic은 리액트 문서를 참고해도 되고 Nextjs 문서를 참고해도 좋습니다.

저는 이렇게 바꿨습니다. 'use client'지시문을 통해 클라이언트 컴포넌트로 바꿨습니다. useOptimistic훅을 사용해서 사용자의 클릭이벤트에 낙관적 상태를 추적할 수 있습니다.

그리고 addOptimisticLike라는 액션함수에게 현재 좋아요 상태를 나타내는 boolean값을 전달해서 true면 count를 1 빼주고 isLiked를 false로 만들어서 새 낙관적 상태를 반환했습니다. 

onClick 함수에서 중괄호를 쳐서 서버액션과 optimistic action함수를 둘 다 호출해 주면 됩니다. 저는 개인적으로 이 부분이 좀 수정되었으면 좋겠어요. 뭔가 좀... 어색한 것 같아요.

 

 

연달아 클릭하면 클라이언트 상태를 낙관적으로 업데이트하면서 리랜더링만 시켜주다가 

마지막에 한 번만 서버컴포넌트를 리랜더링 하고 상태를 동기화하는 모양이네요. 

서버에서 에러를 리턴하도록 코드를 수정하고 테스트해 보면 서버요청 실패 시 롤백이 되면서 서버상태와 일치시킵니다.

이제 낙관적 상태업데이트도 react 라이브러리 수준에서 훅으로 구현할 수 있게 되었네요!

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함