Error: Text content does not match server-rendered HTML.

모찌모찌·2024년 5월 21일

오류해결

목록 보기
2/2

react-datepicker라이브러리를 사용하여 캘린더를 구현하는중
요일형식을 기본 'Su' 에서 'Sun'으로 바꾸고, 일요일부터 시작하게 하기위해 Locale값을 수정해서 locale="custom"값에 넘겨줬다.

하지만 오류가 떴다.

Error: Text content does not match server-rendered HTML.
See more info here: https://nextjs.org/docs/messages/react-hydration-error 

이 오류는 Next.js에서 서버 사이드 렌더링 시에 발생하는 것이다.
Next.js의 경우 서버 사이드 렌더링클라이언트 사이드 렌더링을 함께 사용할 때 일부 요소들의 초기 상태가 서로 다르면 이러한 오류가 발생한다고 한다.

즉, 나의 경우 DatePicker의 초기 상태와 서버 사이드 렌더링된 HTML이 다르기 때문에 오류가 발생한 것 이다.

useEffect를 이용하여 클라이언트 사이드에서만 datePicker을 초기화 하면 해결이 된단다.
처음 오류는 해결했는데 'Sun' 부분이 처음 로드때는 잘 되는데 로딩이 끝나면 기본 설정인 'Su'으로 돌아갔다.

지져스....😥


💡 해결방법

위 문제는 Next.js에서 서버 사이드 렌더링이 되는 동안에는 setDefaultOptions가 정상적으로 동작하지 않기 때문에 발생한다. 서버 사이드 렌더링에서는 일반적으로 브라우저의 환경과 관련된 API에 접근할 수 없기 때문에 setDefaultOptions가 무시되고, 클라이언트 사이드에서만 실행됩니다. 그 결과로 클라이언트 사이드에서는 초기화된 설정이 다시 초기값으로 돌아가게 됩니다.

이 문제를 해결하기 위해, 초기 설정을 서버 사이드 렌더링에 영향을 주지 않고 클라이언트 사이드에서만 적용되도록 하는 방법을 사용할 수 있습니다. 이를 위해 useEffect 훅을 사용하여 클라이언트 사이드실행되도록 클라이언트 측 로딩 상태를 clientLoaded 상태로 관리하고, 이 상태가 true가 되면 DatePicker를 렌더링되도록 하였다.

이렇게 함으로써 DatePicker가 클라이언트 측에서만 렌더링되고 초기값이 서버와 동일하게 유지됩니다.

😅수정
=> 밑에 코드 처럼 안해도 되고 그냥 파일 맨위에 'use client' 쓰면 클라이언트 사이드에서 실행된다...

import { enUS, Locale } from 'date-fns/locale';

const customLocale: Locale = {
  ...enUS,
  localize: {
    ...enUS.localize,
    day: (n: number) => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][n],
  },
  options: {
    ...enUS.options,
    weekStartsOn: 0, // 0은 일요일, 1은 월요일
  },
};

setDefaultOptions({ locale: customLocale });

export default function Calendar() {
  const [date, setDate] = useState(new Date());
  const [month, setMonth] = useState(new Date().getMonth());
  const [clientLoaded, setClientLoaded] = useState(false); // 클라이언트 측 로딩 상태

  const handleMonthChange = (date: Date) => {
    setMonth(date.getMonth());
  };

  useEffect(() => {
    if (typeof window !== 'undefined') {
      // 클라이언트 측에서만 실행
      setClientLoaded(true);
    }
  }, []);

  return (
    <div>
      {clientLoaded ? ( // 클라이언트 측 로딩이 완료된 후에만 DatePicker 렌더링
        <DatePicker
          selected={date}
          onChange={(date: Date) => setDate(date)}
          inline
          autoComplete="off"
          locale={customLocale} // customLocale을 직접 전달
          minDate={new Date()} // 이전날짜 선택못함
          onMonthChange={handleMonthChange}
          dayClassName={(d: Date) =>
            d.getMonth() === month ? 'custom-day' : 'custom-day gray-day'
          }
          renderCustomHeader={({ date, decreaseMonth, increaseMonth }) => (
            <CalendarHeader
              date={date}
              decreaseMonth={decreaseMonth}
              increaseMonth={increaseMonth}
            />
          )}
        />
      ) : null}
    </div>
  );
}

❗ 타입스크립트를 쓸경우 꼭 customLocale: Locale 로 타입을 명시적으로 지정해줘야한다.

서버사이드 렌더링에 대해서 좀더 공부해봐야겠다...
왜 안되는지는 알겠는데 해결방법이 이게 맞는지 잘모르겠다ㅜㅜ

profile
꼬꼬마 개발자 지망생

0개의 댓글