1. Hydration Error
이유:
- React의 Hydration 과정에서 서버와 클라이언트의 렌더링 결과가 일치하지 않을 때 발생.
- 주요 원인:
- 서버에서 생성된 HTML과 클라이언트에서 생성된 DOM이 다름.
- 클라이언트에서 동적 데이터가 렌더링 결과를 변경.
- useEffect, useState와 같은 클라이언트 전용 로직이 서버와 클라이언트의 초기 결과를 다르게 만듦.
해결 방안:
- 조건부 렌더링에 주의:
- 클라이언트 전용 코드(window, document)는 서버 렌더링에서 동작하지 않으므로 useEffect 안에서 처리.
- 예:
import { useEffect, useState } from "react"; function ExampleComponent() { const [isClient, setIsClient] = useState(false); useEffect(() => { setIsClient(true); }, []); if (!isClient) return null; return <div>클라이언트에서만 렌더링되는 컴포넌트</div>; }
- 동적 데이터 처리:
- 초기값을 서버와 동일하게 설정해 Hydration 불일치를 방지.
- 예:
const [value, setValue] = useState(() => serverValue || "");
- key 속성 활용:
- 상태 변화에 따라 고유한 key를 할당하여 컴포넌트를 강제로 재생성.
2. next-themes 라이브러리 관련 에러
이유:
- Next.js에서 next-themes를 사용할 때 서버-클라이언트 동기화 문제로 인해 테마 변경이 올바르게 작동하지 않을 수 있음.
- 주요 원인:
- 초기 theme 값이 클라이언트에서 렌더링되기 전에 undefined로 설정.
- ThemeProvider가 올바르게 설정되지 않음.
해결 방안:
- 초기 테마 설정:
- defaultTheme를 system 또는 원하는 값으로 설정.
import { ThemeProvider } from "next-themes"; function App({ children }: { children: React.ReactNode }) { return ( <ThemeProvider attribute="class" defaultTheme="light"> {children} </ThemeProvider> ); }
- useEffect로 초기 렌더링 보정:
- 클라이언트에서만 테마가 동작하도록 초기 렌더링 시점을 명확히 조정.
import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; function DarkModeToggle() { const { theme, setTheme } = useTheme(); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); if (!mounted) return null; return <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>Toggle</button>; }
- 필요한 Tailwind 설정 확인:
- tailwind.config.js에서 다크 모드 속성 설정 확인:
module.exports = { darkMode: "class", };
- tailwind.config.js에서 다크 모드 속성 설정 확인:
3. 불필요한 재렌더링 문제
이유:
- React 컴포넌트가 불필요하게 다시 렌더링되는 경우:
- 부모 컴포넌트의 상태나 props가 자식 컴포넌트에 영향을 미침.
- 무분별한 익명 함수(onClick={() => ...}) 사용.
- Context API를 과도하게 사용하여 상태 변경이 전파됨.
해결 방안:
- React.memo 활용:
- props 변경이 없는 경우 컴포넌트를 재렌더링하지 않도록 설정.
import React from "react"; const ChildComponent = React.memo(({ value }: { value: string }) => { console.log("렌더링!"); return <div>{value}</div>; });
- useCallback 사용:
- 함수 재생성을 방지해 자식 컴포넌트의 재렌더링을 최소화.
const handleClick = useCallback(() => { console.log("클릭!"); }, []);
- Context 분리:
- Context 범위를 좁혀 렌더링 영향을 줄임.
4. vercel.json 설정
이유:
- Vercel에서 빌드 설정, 리디렉션, API 처리 등 잘못된 설정으로 빌드가 실패하거나 예상치 못한 동작 발생.
해결 방안:
- 리디렉션 설정:
- 정적 페이지와 API 경로가 충돌하지 않도록 설정.
{ "redirects": [ { "source": "/old-path", "destination": "/new-path", "permanent": true } ] }
- 서버 함수 제한:
- API 경로가 너무 많을 경우 ignoreBuildErrors 옵션을 활성화하여 빌드 완료 보장.
5. Next.js와 Yarn 충돌
이유:
- Next.js 프로젝트에서 Yarn 버전 또는 설치된 패키지 충돌로 인해 의존성 문제가 발생.
해결 방안:
- Yarn 버전 확인:
- 최신 버전(1.x 또는 3.x)을 사용하는지 확인.
- yarn --version
- node_modules 초기화:
- rm -rf node_modules && yarn install
- 패키지 충돌 확인:
- yarn why 명령어를 사용해 충돌하는 패키지 확인:
yarn why next
- yarn why 명령어를 사용해 충돌하는 패키지 확인:
- 의존성 강제 설치:
- yarn add next@latest react@latest react-dom@latest