[개발 일지] 이슈 사항 - 서버 & 클라이언트 렌더링 구성 요소 비교 에러 (feat. Text content does not match server-rendered HTML)

yesung·2024년 1월 12일
2

클라이언트 전역 상태 라이브러리 zustand를 사용하다가 에러가 발생했다.

원인

supabase Authentication session API를 사용해서 조건부 렌더링을 하던 도중 해당 에러를 마주하게 됐다.

zustand persist

const useAuthStore = create<AuthState>()(
  persist(
    set => ({
      session: null,
      setSession: session => set({ session }),
    }),
    { name: 'session-status' },
  ),
);

스토리지의 저장하기 위해 persist 를 사용했고 처음엔 로직 상 문제는 없었보였지만 에러에서 착하게도 던져준 링크와 zustand 공식 문서에 나와있는 내용을 확인해보니 프레임워크 상 Next.js는 SSR을 사용하니까 서버에서 렌더링된 요소와 클라이언트에서 렌더링된 구성 요소를 비교하게 되는데 구성 요소를 변경하기 위해 브라우저의 데이터를 사용하고 있어서 두 렌더링이 다르게 되니까 Next에서 경고가 표시됐었다.

zustand에서 말하는 에러 메세지는 총 3개이고, 우리 팀원 분도 2번 에러가 발생했었다.

  1. Text content does not match server-rendered HTML

  2. Hydration failed because the initial UI does not match what was rendered on the server

  3. There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering

해결

에러를 해결하기 위해서 Next에서 제시한 방법은 useEffect를 클라이언트에서만 사용하는 방법과
사전 렌더링 비활성화 를 하는 동적 import 방법을 제시했다.

근데 사전 렌더링 비활성화를 하는 방법을 사용하면 서버 사이드와 클라이언트 사이드에 상태가 다를 수도 있을 거 같아서 사용하지 않았고 에러 발생 지점 컴포넌트에서 useEffect를 사용하여 로딩을 걸어줬다.

const StickBar = () => {
  const { logout } = useAuth();
  const { session } = useAuthStore();
  const [isLoaded, setIsLoaded] = useState(false);


  useEffect(() => {
    setIsLoaded(true);
  }, []);

  return (
    <>
      {isLoaded && (
        <div className={styles.wrapper}>
          // ...

          <div className={styles.tabArea}>
            {session === null ? (
              <>
                <Link href="/auth/signup">회원가입</Link>
                <Link href="/auth/login">로그인</Link>
                </>
            ) : (
              <>
                <Link href="/" onClick={() => logout()}>
                  로그아웃
                </Link>
                </>
            )}
          </div>
        </div>
      )}
      </>
  );
};

export default StickBar;

zustand 에서도 요소를 비교하고 변경하기 전에 잠시 기다리라는 커스텀 훅을 만들라고 한다. 그런데 특정 한 컴포넌트에서만 그러니 별도로 만들지는 않았다.


Next.js 공식 문서 참고: https://nextjs.org/docs/messages/react-hydration-error
zustand: 공식 문서 참고: https://docs.pmnd.rs/zustand/integrations/persisting-store-data#hydration-and-asynchronous-storages

profile
Frontend Developer

0개의 댓글