서버 측에서 data를 fetch 하도록 한 이후 vercel에 배포하니 아래와 같은 에러가 날 맞이 했다 ㅎㅎ
(정말 아직도 의문인건 왜 개발단에서 발견은 안되고 배포 시에만 console 창에 나타나는 걸까...? )
에러 상황
에러의 내용은 다음과 같았다.
Uncaught Error: Minified React error #418;
서버에서 렌더링 된 HTML이 클라이언트와 일치하지 않아 Hydration에 실패했습니다. 그 결과 클라이언트에서 다시 생성됩니다. 이는 서버 렌더링된 클라이언트 컴포넌트가 다음을 사용한 경우 발생할 수 있습니다.
- 서버/클라이언트 분기 if (typeof window !== 'undefined')
- Date.now()나 Math.random()을 호출할 때마다 변경되는 변수 입력
- 서버와 일치하지 않은 사용자 로컬의 날짜
- HTML과 함께 스냅샷을 보내지 않고 외부에서 변경되는 데이터
- 잘못된 HTML 태그 중첩
Uncaught Error: Minified React error #423;
Hydration 중 오류가 발생했지만, React는 클라이언트 렌더링을 통해 전체 루트를 복구할 수 있었습니다.
나는 Date.now(), Math.random(), 날짜 등은 사용하지 않았고 잘못된 HTML 중첩..? 아닐 거 같았다.
이 에러는 내가 기존 client에서 하던 데이터 fetch를 서버에서 한 이후로 발생했기 때문이다.
일단 느낌은 서버에서 렌더링 된 html과 클라이언트가 일치하지 않아... 발생한 거 같은데 어디지?!!! 싶어서 하나씩 다 지워봤다ㅋㅋ
아무리 봐도 모르겠다면 무식하게라도 시도 해보는게 좋다고 생각한다!
(이 현상이 배포환경에서만 발생해서 계속 하나씩 지우며 배포했다 ㅎ)
그래서!! 찾았다!ㅎㅎ
이전 소스 (Hydration 에러가 발생하던 소스)
'use client'
import PortfolioItem from '@/components/portfolio/PortfolioItem'
import PortfolioItemSkeleton from '@/components/portfolio/PortfolioItemSkeleton'
import { useInfinitePortfolioQuery } from '@/hooks/queries/portfolio'
import useInfiniteScroll from '@/hooks/useInfiniteScroll'
import { Fragment, useRef } from 'react'
function RecentPortfolioList() {
const { data, fetchNextPage, isFetching, hasNextPage } =
useInfinitePortfolioQuery()
const fetchMorePortfolio = () => {
if (!isFetching && hasNextPage) {
fetchNextPage()
}
}
const loaderRef = useRef<HTMLDivElement>(null)
useInfiniteScroll(loaderRef, fetchMorePortfolio)
return (
<>
<ul className="grid gap-6 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 lg:px-20 px-10">
{data?.pages.map((page, index) => (
<Fragment key={index}>
{page.data.map((portfolio) => (
<PortfolioItem key={portfolio.id} portfolio={portfolio} />
))}
</Fragment>
))}
{isFetching &&
Array(12)
.fill(0)
.map((_, index) => <PortfolioItemSkeleton key={index} />)}
</ul>
<div ref={loaderRef} className="h-8" />
</>
)
}
export default RecentPortfolioList
에러 원인 및 해결
무한 스크롤 시 추가적으로 더 받아오는 데이터의 로딩처리를 위해 isFetching으로 스켈레톤 로딩처리를 했었는데
isFetching의 값이 서버와 클라이언트 측에서 상태값이 달라 Hydration 과정에 불일치가 발생해 에러가 발생한 걸로 추측된다.
isFetching이 처음 api를 요청했을때도 true이기 때문에 이후 클라이언트에서 캐싱된 데이터를 쓸 때 문제가 되는 게 아닌가 싶다.
그래서 어떻게 수정했냐고?!
생각해보니 isFetching은 요청 시마다 true로 변경이 되었다가 요청이 끝나면 false로 바뀐다.
나의 경우 처음 로딩 시에는 필요가 없었고 스크롤을 내리다가 다음 페이지 요청 시부터 스켈레톤 로딩처리가 필요했다.
다른 방법을 찾던 중...! react-query에 useInfiniteQuery에 isFetchingNextPage라는 게 있었다 ㅎㅎ
isFetchingNextPage
다음 페이지를 fetch해오는 동안 true로 변경된다.
이걸 사용하니 Hydration 에러가 말끔히 사라졌다 ㅎㅎ
에러가 날때는 다 이유가 있는 법이다. 컴퓨터는 거짓말을 하지 않는다.
수정된 소스는 아래와 같다.
'use client'
import PortfolioItem from '@/components/portfolio/PortfolioItem'
import PortfolioItemSkeleton from '@/components/portfolio/PortfolioItemSkeleton'
import { useInfinitePortfolioQuery } from '@/hooks/queries/portfolio'
import useInfiniteScroll from '@/hooks/useInfiniteScroll'
import { Fragment, useRef } from 'react'
function RecentPortfolioList() {
const { data, fetchNextPage, isFetchingNextPage, hasNextPage } =
useInfinitePortfolioQuery()
const fetchMorePortfolio = () => {
if (!isFetchingNextPage && hasNextPage) {
fetchNextPage()
}
}
const loaderRef = useRef<HTMLDivElement>(null)
useInfiniteScroll(loaderRef, fetchMorePortfolio)
return (
<>
<ul className="grid gap-6 xl:grid-cols-4 lg:grid-cols-3 md:grid-cols-2 sm:grid-cols-1 lg:px-20 px-10">
{data?.pages.map((page, index) => (
<Fragment key={index}>
{page.data.map((portfolio) => (
<PortfolioItem key={portfolio.id} portfolio={portfolio} />
))}
</Fragment>
))}
{
isFetchingNextPage &&
Array(12)
.fill(0)
.map((_, index) => <PortfolioItemSkeleton key={index} />)}
</ul>
<div ref={loaderRef} className="h-8" />
</>
)
}
export default RecentPortfolioList
오늘의 멍청일기는 이렇게 끝난다 ㅎㅎ
'Project > foliohub' 카테고리의 다른 글
[react-query] 서버에서 prefetch한 데이터 사용하기 (prefetchInfiniteQuery) (0) | 2024.05.10 |
---|---|
[Next.js] fill을 사용한 이미지에 sizes 추가 (Image with src "~~" has "fill" but is missing "sizes" prop. Please add it to improve page performance.) (0) | 2024.04.14 |
[Next.js] 이미지 next/image 적용해보기 (0) | 2024.04.12 |
[Express] Custom Error Handling (0) | 2024.04.06 |
[Next.js] NotFound / Error 페이지 Custom하기 (0) | 2024.04.06 |