SSR, react-draft-wysiwyg, 그리고 ‘window is not defined.’

song for the mute
4 min readJul 12, 2023
Photo by Lautaro Andreani on Unsplash

‘window is not defined.’

React에서, 혹은 Next.js에서 타입스크립트를 사용하거나, 서버 사이드 렌더링 옵션을 채택했을 때 종종 마주칠 수 있는 오류다. 이는 대개 브라우저 API가 필요하거나 브라우저에서만 동작하도록 작성된 라이브러리가 서버에서 호출되어 그럴 확률이 높다.

if (typeof window !== 'undefined') {
/* ... */
}

if (typeof window === 'object') {
/* ... */
}

서버 사이드 렌더링 방식을 채택하지 않았음에도 타입스크립트를 사용해서 개발하고 있는 경우에도 위의 에러를 마주칠 수 있는데, 그럴 때는 위의 방식을 사용해서 window 객체가 없다는 에러를 회피할 수 있다. 하지만, 서두에서 서술한 경우라면 이런 방식을 이용해선 오류를 해결할 수 없다.

이번 프로젝트에서는 Facebook(현 Meta)에서 draft-js를 기반으로 작성한 오픈소스 라이브러리인 ‘react-draft-wysiwyg’를 이용하여 WYSIWYG 에디터를 개발하다 마주쳤다.

기존에 작성된 포스트를 수정하는 방법에는 여러 가지가 있겠지만, 그중 하나는 렌더링하고 있는 HTML, 또는 관리 중인 state의 HTML 문자열을 EditorState 객체로 다시 변환해 에디터 내에서 수정 가능한 상태로 보여주는 방법이 있다.

import htmlToDraft from 'html-to-draftjs';

그 과정 중, 가장 먼저 할 일은 위의 htmlToDraft 함수를 호출해서 Draft 객체로 변환하는 것이다. 그런데 이 라이브러리 또한 ‘react-draft-wysiwyg’ 라이브러리의 Editor 컴포넌트를 dynamic 함수를 통해 동적 호출을 해오듯, 브라우저 상에서 호출을 할 수 있다.

그리고 dependency list를 비운 useEffect 훅을 이용한다면 컴포넌트가 마운트 될 때 단 한 번 훅 내의 문이 실행된다. 그렇지만 이 방식을 이용하더라도, 서버 사이드 렌더링을 통해 서버에서 호출되고 있으므로 오류가 여전히 발생할 수 있다. 이번 경우가 이런 경우였다. 브라우저 상에서 호출되어야 할 부분이 서버 사이드 렌더링을 위해 서버에서 호출되어 window 객체를 찾을 수 없었기 때문이다.

따로 훅으로 분리하기도 하고, SSR 옵션을 켜고 꺼보기도 하는 등의 여러 방법을 시도해 봤지만 해결하지 못했다. 그리고 ‘이 함수의 import 마저 컴포넌트의 마운트 후에 시도하면 어떨까?’라는 생각으로 시도해 본 useEffect 훅 내부의 require 함수를 통한 import는 다행히 초기에 계획한 프로젝트의 기술적 방향을 지킬 수 있게 해주었다.

useEffect(() => {
if (typeof window !== 'undefined') {
const htmlToDraft = require(”html-to-draftjs”)?.default;
const defaultDraft = htmlToDraft(props.post);
/* ... */
}
}, []);

여전히 useEffect 훅 내부에서 위의 함수를 사용하긴 하되, 컴포넌트의 최상단에서 import 문을 통해 호출하는 것이 아닌, useEffect 훅 내부에서 사용하기 직전에 require 함수를 통해 import 하는 것이다. 그렇게 된다면 import 마저 컴포넌트 마운트 이후 시도하게 되므로, 서버에서의 호출을 막고 브라우저에서 수행할 수 있도록 방어하여 이 오류에서 회피할 수 있게 된다.

그리고 역시, html-to-draftjs 패키지의 깃허브 레포지토리의 issue란에도 이미 3년 전에 똑같은 상황에서 같은 에러를 마주친 사람들이 많았고, 같은 방식으로 해결한 사람이 존재했다. (조금 더 일찍 찾아볼 걸 그랬다…)

참고로, 서버 사이드 렌더링 방식을 채택하며 ‘react-draft-wysiwyg’을 이용해 에디터를 개발하는 와중에 EditorState 혹은 ContentState 클래스를 인식하지 못하는 경우, ‘draft-js’ 패키지에서 해당 클래스를 import 해온다면 에러를 회피하면서 정상적으로 해당 클래스나 메서드를 호출할 수 있다.

--

--

song for the mute

A FE developer who dreams of sustainable development and is interested in user interfaces and user experiences.