본문으로 건너뛰기
개발 머꼬
개발 노트Next.js
hohyeon.dev46

브라우저 전용 라이브러리를 넣었더니 서버에서 window is not defined로 죽은 이유

  • #Common Pitfall
  • #Engineering Note
  • #Next.js

문제 발생

지도 라이브러리를 페이지에 넣자 빌드가 죽었습니다.

ReferenceError: window is not defined

ssr: false를 주면 된다고 해서 페이지(Server Component)에 그대로 썼더니 이번에는 다른 오류가 났습니다.

`ssr: false` is not allowed with `next/dynamic` in Server Components.

원인 분석

두 가지가 얽혀 있습니다.

첫째, Client Component도 서버에서 한 번 렌더됩니다. Next.js 문서가 명확히 합니다 — React.lazy()와 Suspense를 쓰면 Client Component가 기본적으로 프리렌더(SSR)됩니다. 그래서 window를 모듈 최상단에서 만지는 라이브러리는 그 시점에 죽습니다.

둘째, ssr: false는 Client Component 전용입니다. 문서의 경고 그대로 — ssr: false 옵션은 Server Component에서 지원되지 않으며, 시도하면 오류가 납니다. Client Component로 옮기십시오.

문서는 지연 로딩의 적용 범위도 정리합니다 — Server Component는 자동으로 코드 분할되며, 지연 로딩은 Client Component에 적용됩니다.

해결 방안

  1. 클라이언트 경계를 하나 만들고 그 안에서 동적 로딩합니다.
// components/map-view.tsx
"use client";
import dynamic from "next/dynamic";

const Map = dynamic(() => import("./map"), {
  ssr: false,
  loading: () => <MapSkeleton />,
});

export function MapView(props: MapProps) {
  return <Map {...props} />;
}

페이지(Server Component)는 <MapView />만 렌더합니다.

  1. loading 옵션으로 자리를 잡아둡니다. 지도처럼 큰 영역은 스켈레톤이 없으면 레이아웃이 크게 흔들립니다.

  2. 조건부로 열리는 것에 우선 적용합니다. 문서가 드는 예처럼 사용자가 클릭해서 열기 전까지 모달 로딩을 미루는 경우가 대표적입니다.

  3. 외부 라이브러리는 함수 안에서 import() 합니다. 문서 예제처럼 입력이 시작될 때 검색 라이브러리를 불러오면, 그 전까지는 번들에 포함되지 않습니다.

  4. SSR을 끄는 대가를 인지합니다. 그 영역은 초기 HTML에 없습니다 — 검색 유입이 필요한 콘텐츠에는 쓰지 않습니다.

  5. ssr: false 없이 되는지 먼저 봅니다. 브라우저 API 접근을 useEffect 안으로 옮기는 것만으로 해결되는 경우가 많고, 그러면 서버 렌더 결과를 유지할 수 있습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.