1. 발생한 이슈
프로젝트 중간 시점즈음에 빌드에러를 해결하려다 보니까 시간이 오래걸리고 잡기 힘들어지는 문제(빌드에러난 상태에서 추가적으로 코딩하다보니 계속 문제가 꼬인거같음)
2. 문제 탐색
: 소거법으로 폴더,파일,코드를 하나하나 비활성화 시켜가면서 문제 원인 탐색
- 위 방법을 바탕으로 오류가 나는 페이지 코드들을 분석 후 왜 오류가 나는지 원인 분석
•
소거법으로 폴더, 파일, 코드들을 하나하나 비활성화 시키면서 문제 원인 분석
•
문제 원인을 가지고 구글링한 정보를 바탕으로 리팩토링
3. 문제 원인 및 해결 방안
1. window 나 localstorage 와 같은 클라이언트 사이드 전용 객체에 서버 사이드 렌더링이나 빌드 타임에 접근하려고 할 때 발생합니다. Next.js는 빌드 타임에 모든 페이지를 사전에 렌더링(SSR) 하기 때문에, 이 시점에서 클라이언트 사이드 전용 객체를 참조하면 오류가 발생합니다.
•
기존 문제 코드
: 기존의 sessionStorage 또는 localStorage는 클라이언트에서 사용되는 window 전역 객체인데, SSR을 할 때 window를 인식하지 못해서 생기는 문제
/* src > store > useRoleStore */
export const useUserRoleStore = create<UserRoleState>((set) => ({
isTeacher: sessionStorage.getItem('isTeacher') === 'true' ,
setIsTeacher: (value: boolean) => {
set({ isTeacher: value });
sessionStorage.setItem('isTeacher', value.toString());
}
}
}));
TypeScript
복사
•
해결방안 : useEffect를 사용하여 렌더링 후 실행되도록 하거나, type window !== undefined 인 경우를 체크해주면된다
/* src > store > useRoleStore */
export const useUserRoleStore = create<UserRoleState>((set) => ({
isTeacher: typeof window !== 'undefined' ? sessionStorage.getItem('isTeacher') === 'true' : null,
setIsTeacher: (value: boolean) => {
set({ isTeacher: value });
// null 값이 아닐 때만 sessionStorage에 저장하도록 변경
if (typeof window !== 'undefined' && value !== null) {
sessionStorage.setItem('isTeacher', value.toString());
}
}
}));
TypeScript
복사
/* src > app > (clrm) > payment > page.tsx */
useEffect(() => {
if (typeof window !== 'undefined') {
const reservationId = window.localStorage.getItem('reservationId');
setReserveId(reservationId);
}
}, []);
TypeScript
복사
2.
useSearchParams을 사용할 경우 해당 페이지가 클라이언트 사이드 렌더링이되어 빌드시점에 html이 비어있을 수 있기 때문에 오류가 발생합니다.
•
해결방안 : 특정 구역의 컴포넌트 렌더링을 지연시킬 수 있는 기능을 가진 Suspense훅을 사용하여 데이터가 준비될 때까지 대기시키면 된다.
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
// import './globals.css';
import Header from '@/components/common/bars/Header';
import SideBar from '@/components/common/bars/SideBar';
import { ReactNode, Suspense } from 'react';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: '클룸',
description: 'Generated by create next app'
};
interface ClrmRootLayoutProps {
children: ReactNode;
}
export default function ClrmRootLayout({ children }: ClrmRootLayoutProps) {
return (
<div>
<SideBar>
<Header />
<Suspense>{children}</Suspense>
</SideBar>
</div>
);
}
JavaScript
복사
공식문서 참고