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 로 타입을 명시적으로 지정해줘야한다.
서버사이드 렌더링에 대해서 좀더 공부해봐야겠다...
왜 안되는지는 알겠는데 해결방법이 이게 맞는지 잘모르겠다ㅜㅜ