에러 로그

로컬 개발 환경에서는 발생하지 않았지만 production 환경에서 위와 같은 에러 로그를 볼 수 있었습니다.
Minified React error #425 - Text content does not match server-rendered HTML.
3가지 에러 모두 공통적으로 hydration error와 관련이 있었습니다. 서버 사이드에서 렌더링된 결과와 클라이언트 사이드에서 렌더링된 결과가 일치하지 않아 문제가 발생하게 되었습니다.
(위 링크를 클릭하여 리액트 공식 문서 상의 전문을 확인할 수 있습니다.)
어디가 문제였을까?
문제가 발생하는 곳은 '리뷰 상세 페이지'와 '리뷰 목록 페이지'였습니다. 코드 상으로 잘못된 부분을 확인하기 어려웠기에 실제로 크롬 개발자 도구를 통하여 Network 탭에서 서버 사이드에서 미리 렌더링하여 전달해준 html 파일을 확인하였습니다.
확인해본 결과 리뷰의 작성 일시가 서버에서 렌더링되었을 때와 클라이언트에서 렌더링되었을 때가 서로 다른 것을 볼 수 있었습니다.
(ex: SSR에서는 24.12.29으로 표기 / CSR에서는 24.12.30으로 표기)
무엇이 문제였을까?
// createdAt은 리뷰의 작성 일자를 나타냄. 서버에서 받아오는 값
dayjs(createdAt).format('YYYY.MM.DD');
위는 dayjs를 사용하여 'YYYY.MM.DD' 형태로 포맷팅만 진행했을 뿐인데 어째서 서버와 클라이언트의 렌더링 결과가 다른 것일까요?
이것은 dayjs 라이브러리의 기본 동작에 대한 이해가 필요합니다. dayjs는 기본적으로 호스트 시스템의 타임존을 따라 갑니다.
위와 같은 이유로 로컬 서버에서는 하이드레이션 에러가 발생하지 않았지만 상용 환경에서는 에러가 발생한 것입니다.
로컬 서버: 클라이언트 timezone - Asia/Seoul , 서버 timezone - Asia/Seoul
상용 서버: 클라이언트 timezone - Asia/Seoul , 서버 timezone - UTC
아래와 같이 고쳐보자
// _app.tsx
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.locale('ko');
dayjs.tz.setDefault('Asia/Seoul');
// examplePage.tsx
// SSR을 사용하는 페이지 내에서 dayjs를 사용할 때에는 dayjs().tz()를 사용하도록 수정
dayjs(createdAt).tz().format('YYYY.MM.DD');
dayjs.tz.setDefault() 함수를 통하여 dayjs.tz의 기본 타임존을 설정할 수 있습니다(다만, dayjs의 타임존에는 아무런 영향을 주지 않는다는 부분은 주의해야 합니다).
SSR을 사용하는 페이지에서 dayjs를 사용할때는 dayjs.tz를 사용하도록 수정하면 하이드레이션 에러를 고칠 수 있었습니다.
'Web > React' 카테고리의 다른 글
| [React] React에서 API 호출을 효과적으로 막는 방법 - useRef 활용하기 (2) | 2024.11.22 |
|---|---|
| [React] Warning: React has detected a change in the order of Hooks 에러 해결하기 (3) | 2024.07.17 |
| [React] 접근 제한을 위한 Private Route 만들기 (4) | 2024.04.21 |
| [React] Invalid Hook Call Warning 해결하기 (1) | 2024.04.15 |
| [Recoil] React를 위한 상태 관리 라이브러리, Recoil에 대하여 알아보자 (7) | 2024.02.07 |