food compass 프로젝트 트러블 슈팅
💣 댓글 pagination 이슈
댓글에서 pagination을 구현하던 중 1페이지에서 2페이지로 넘어가도 1페이지의 댓글들만 보이는 이슈가 생겼다.

코드는 아래와 같다.
// 페이징처리 댓글 가져오는 요청
export const getComments = async (postId, page, limit) => {
const response = await commentInstance.get('', {
params: {
postId,
_page: page,
_limit: limit
}
});
return response.data;
}
const [page, setPage] = useState(1);
const limit = 5;
// postId에 해당하는 댓글들 페이지처리 해서 가져오는 useQuery
const {
data: comments,
isLoading: isCommentLoading,
isError: isCommentError
} = useQuery({
queryKey: ['comment', postId, page],
queryFn: () => getComments(postId, page, limit)
});
json-server의 공식문서 내용은 아래와 같다.

한 페이지에 5개씩 보여주기 위해 limit을 5로 설정하고 page와 limit을 요청할 때 같이 보내면 잘 될 것이라고 생각했다.
댓글을 6개 작성해두고 테스트를 해봤을 때, 1페이지에서 댓글 5개가 보이고 2페이지로 이동하면 나머지 1개의 댓글이 보여야 하는데 페이지가 넘어가도 1페이지에 있는 5개의 댓글만 보였다.
😂 문제는 한개가 아니었다.
🧨 우선 첫번째 문제는 useQuery의 queryKey가 겹치는 것!
// postId에 해당하는 댓글들 개수 가져오는 useQuery
const {
data: totalCommentsCount,
isLoading: isCountLoading,
isError: isCountError
} = useQuery({
queryKey: ['comments', postId],
queryFn: () => getCommentCount(postId)
});
const totalPages = Math.ceil(totalCommentsCount / limit);
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// postId에 해당하는 댓글들 페이지처리 해서 가져오는 useQuery
const {
data: comments,
isLoading: isCommentLoading,
isError: isCommentError
} = useQuery({
queryKey: ['comments', postId, page],
queryFn: () => getComments(postId, page, limit)
});
위 코드에서 보이는 두 개의 useQuery에서 key가 같은 걸 볼 수 있다.
두 useQuery는 서로 다른 데이터를 캐시하고 관리하는 용도이기 때문에 쿼리 키를 분리하는 것이 적절하다.
- 캐싱 충돌
- 댓글 목록을 가져오는 쿼리와 댓글 수를 가져오는 쿼리가 동일한 쿼리 키를 사용하면, 캐시 된 데이터가 충돌할 수 있다. 댓글 목록 데이터를 가져온 후, 댓글 수를 가져오는 요청이 실행되면 기존 댓글 목록 데이터를 덮어쓸 수 있다.
- 데이터 무결성 문제
- 댓글 목록과 댓글 수는 각각 독립적으로 관리되어야 한다. 만약 쿼리 키를 동일하게 설정하면, react-query가 데이터 무결성을 유지하지 못할 수 있다. 즉, 두 요청이 동일한 캐시를 공유하게 되어 의도하지 않은 데이터가 렌더링 될 수 있다.
💡 쿼리 키를 구분하지 않으면 위와 같은 문제들이 생길 수 있기 때문이다.
// postId에 해당하는 댓글들 개수 가져오는 useQuery
const {
data: totalCommentsCount,
isLoading: isCountLoading,
isError: isCountError
} = useQuery({
queryKey: ['commentCount', postId],
queryFn: () => getCommentCount(postId)
});
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
// postId에 해당하는 댓글들 페이지처리 해서 가져오는 useQuery
const {
data: comments,
isLoading: isCommentLoading,
isError: isCommentError
} = useQuery({
queryKey: ['comments', postId, page],
queryFn: () => getComments(postId, page, limit)
});
위 코드는 각각 쿼리키를 구분해 놓은 코드이다.
🧨 두 번째 문제는 요청을 하는 코드의 문제이다.
// 페이징처리 댓글 가져오는 요청
export const getComments = async (postId, page, limit) => {
const response = await commentInstance.get('', {
params: {
postId,
_page: page,
_limit: limit
}
});
return response.data;
}
글 초반부에도 첨부하였지만 다시 가져와봤다. 위 코드가 문제의 요청 부분 코드이다.
위 코드에서 보면 params에 _limit: limit라고 되어있는 부분에 문제가 있었다.
공식문서에서 Paginate에서는 _page와 _per_page를 받는다고 되어있는데 뜬금없이 _limit라는 param을 보내버린 것이다.
🔥 그리고 또!
리턴 부분에서 response.data를 리턴하고 있지만 response.data.data를 해줘야 내가 의도한 대로 응답을 처리할 수 있었다.

// 페이징처리 댓글 가져오는 요청
export const getComments = async (postId, page, limit) => {
const response = await commentInstance.get('', {
params: {
postId,
_page: page,
_per_page: limit
}
});
return response.data.data;
}
위는 수정된 코드이다.

수정 후 이제야 잘 작동하는 모습!!
공식 문서에서 이미 잘 알려주고 있지만 제대로 사용하지 못한 나의 잘못과 응답이 어떻게 들어오고 내 의도대로 사용하려면 어떻게 처리를 해야 하는지를 제대로 확인하지 않아 발생했던 이슈였다😅