✍️ What I Learned/트러블슈팅

[트러블슈팅] Error: document is not defined

Jiwon() 2023. 8. 21. 22:15

🚨 발생 문제

Unhandled Runtime Error

Next.js에서 텍스트 에디터 라이브러리인 react-quill을 불러오는데 document is not defined라는 에러 메시지 발생

 

 


🤔 문제 원인

react-quill 라이브러리는 SSR을 지원하지 않음

  • Next.js는 기본적으로 서버 사이드에서 렌더링을 하는데, react-quill 라이브러리는 SSR을 지원하지 않고, 클라이언트에서만 동작
  • quill editor는 document 객체를 조작해 동작하므 document 객체가 로드 된 이후 quill 에디터를 import 하도록 해야 함

 

 


💊 해결 방법

Lazy Loading

  • Next.js의 Lazy loading은 경로를 렌더링하는 데 필요한 JavaScript의 양을 줄여 애플리케이션의 초기 로딩 성능을 개선하는 데 도움됨
  • 이를 통해 클라이언트 컴포넌트와 가져온 라이브러리의 로딩을 지연시키고 필요할 때만 클라이언트 번들에 포함할 수 있다. 예를 들어 사용자가 클릭하여 모달을 열 때까지 로딩을 지연시키고 싶을 수 있음
  • Next.js에서 Lazy loading을 구현할 수 있는 방법 두 가지
    • next/dynamic을 이용한 동적 가져오기(Dynamic Imports)
    • Suspense와 함께 React.lazy()를 이용하는 방법

 

Dynamic Import

  • Dynamic import는 개발자가 모든 것을 미리 로드하는 대신 필요에 따라 구성 요소 또는 모듈을 로드할 수 있도록 하는 기능
  • 자바스크립트 모듈 또는 컴포넌트를 동적으로 로드하는데 사용됨
  • 코드 분할이 가능하므로 필요할 때 코드의 필요한 부분만 로드되고, 초기 로딩 시간이 줄어들어 웹사이트가 더 빠르고 반응성이 좋아짐
import dynamic from 'next/dynamic';
import { useMemo, useRef } from 'react';

import Loading from '@/app/loading';
import ReactQuill, { ReactQuillProps } from 'react-quill';
import 'react-quill/dist/quill.snow.css';

interface ForwardedQuillComponent extends ReactQuillProps {
  forwardedRef: React.Ref<ReactQuill>;
}

// dynamic import
const QuillWrapper = dynamic(
  async () => {
    const { default: QuillComponent } = await import('react-quill');
    const Quill = ({ forwardedRef, ...props }: ForwardedQuillComponent) => (
      <QuillComponent ref={forwardedRef} {...props} />
    );
    return Quill;
  },
  { loading: () => <Loading />, ssr: false },
);

// ... 중간 생략

const TextEditor = ({content, setContent}: EditorProps) => {
  const quillInstance = useRef<ReactQuill>(null);

  // ... const modules, const formats 중간 생략

  return (
    <QuillWrapper
      className="h-5/6"
      forwardedRef={quillInstance}
      value={content}
      onChange={setContent}
      modules={modules}
      formats={formats}
      theme="snow"
      placeholder="내용을 입력해주세요."
    />
  )
}

export default TextEditor

 

"use client';와 차이점

  • Nextjs에서는 import로 모듈을 불러오는 것도 서버 사이드로 이루어진다.
  • use client
    • 서버 측 렌더링을 아예 비활성화 하는 경우에 사용
    • “use client”;을 입력해주는 것은 그 파일을 클라이언트 컴포넌트로 취급하고 컴파일하겠다는 의미
  • dynamic import
    • SSR과 호환되지 않는 라이브러리를 클라이언트 측에서만 로드하고 실행할 때 사용하는 일반적인 방법
  • "use client"를 사용하면 컴포넌트가 클라이언트 측에서만 렌더링되도록 제한할 수 있지만 react-quill은 초기 렌더링 시 서버 측에서는 사용할 수 없음
  • “use client”를 사용해도 react-quill 라이브러리 자체가 서버에서 지원하지 않기 때문에 SSR 처리를 해주어야 하므로 근본적인 해결 방안이 아님

 

 


참고 사이트

Lazy Loading | Next.js Docs

nextjs에서 dynamic import로 quill editor 사용하기