Next.js 프로젝트에서 발생한 document is not defined 에러 분석
회사에 합류한 지 약 일주일 째, 난해한 문제를 만났습니다.
사내 Next.js 프로젝트가 제 로컬환경에서만 ReferenceError: document is not defined를 나타내며 실행이 되지 않았습니다.
동료 개발자는 같은 코드를 로컬에서 실행하는 데 아무런 문제가 없었기 때문에 어떤 문제일까를 분석하는 과정에서 로컬 환경 자체의 비교를 진행하는 경험을 하게 되었습니다.
이 글은 lottie-react 라이브러리와 Node.js, 그리고 Next.js의 SSR이 얽혀있는 이슈였고 그 이슈를 분석하는 과정을 기록했습니다.
문제 상황
프로젝트를 클론하고 의존성을 설치한 후 pnpm 명령어를 통해 실행하자 / directory에서 리액트는 다음의 에러와 함께 에러 트레이스를 보여주었습니다.
ReferenceError: document is not defined
리액트가 보여준 에러 트레이스와 Next.js의 서버 로그를 들여다보니, 해당 프로젝트가 의존성으로 가지고 있는 사내 디자인 시스템의 빌드 파일 (index.es.js) 내부의 특정 부분에서 document를 참조하는 과정에서 발생한 문제였습니다. 이 부분이 서버사이드에서 실행되고 있었기에 발생한 문제로 보였습니다.
원인 분석 과정
빌드된 ESM 모듈 파일을 따라 읽어보았고 createTag라는 함수에서 document를 참조하며 발생한 문제로 보였습니다. 해당 함수는 모듈 내의 한 즉시실행 함수의 내부함수였고 이는 lotte-react 라이브러리의 빌드 결과인 것으로 보였습니다.
lottie, Next.js, document is not defined, SSR 등의 키워드를 기반으로 다음의 레퍼런스를 찾게 되었습니다.
스택오버플로우에 등록된 lottie-react의 Next.js 13버전에서의 ReferenceError에 대한 질문, lottie-react GitHub 레포에 등록된 ReferenceError 이슈입니다.
ReferenceError with lottie-react on Next.js 13 - SSR Incompatibility?
I'm encountering a bug in my Next.js 13 application after installing lottie-react. When I compile my project, it completes successfully, but I get a ReferenceError stating that document is not defi...
stackoverflow.com
https://github.com/Gamote/lottie-react/issues/101
ReferenceError: document is not defined at createTag... · Issue #101 · Gamote/lottie-react
Hello everyone, I had some issues with ReferenceError (with SSR). And yes I know, it could be solved by dynamic import, lazy or maybe some other ways. But the situation is like below: There is a ma...
github.com
레퍼런스들에서 해당 이슈가 Node.js 버전과 관계가 있을 수 있음을 알게 되었습니다.
Node.js 20.6부터는 일부 Web API들의 실험적인 지원이 시작되었고, Node.js 21 부터는 범위가 보다 확장되었습니다. 이런 과정에서 lottie-react가 서버사이드에서 navigator, window 등을 참조하며 실행을 시도하지만, 브라우저와는 환경이 다르기 때문에 오류가 발생하는 것일 수 있다는 것을 알았습니다. (다만 이는 Node.js 18로 다운그레이드 했을 때 이슈가 없어졌다라는 점에서 해당 지점들이 이슈일 수 있다는 의견에 기인합니다.) - 서버사이드/클라이언트사이드 구분이 Node.js 18버전에서 보다 용이했다는 언급들도 있습니다.
환경 통일의 중요성
프로젝트를 계속 함께해온 회사 동료들은 해당 프로젝트에 대해 Node.js 20.0 버전을 사용하고 있다는 것을 알았습니다.
반면 저는 일단 새로 받은 장비의 기본적인 환경 설정을 진행하며 volta를 이용해 최신 LTS 버전을 설치하여 Node.js 22.x를 사용하고 있었습니다. 이로 인해 위의 이슈를 마주치게 되었습니다.
프로젝트별 Node.js 버전 관리의 중요성
개인적으로는 최신 LTS 버전을 사용하는 것이 성능과 보안 양 측면에서 좋다고 생각해왔습니다
하지만 지금과 같은 이슈에서 알게 되었듯, Node.js가 가지는 기능이 늘어나며 기존 라이브러리와 충돌하는 경우가 발생할 수 있다는 것을 알았고, 프로젝트마다 명시된 Node 버전을 일관되게 사용하는 것이 중요함을 알았습니다.
이를 위해 nvm, volta 같은 노드 버전 매니저를 사용하고 package.json, .node-version, .nvmrc 와 같은 설정 파일에 이용 버전을 명시하는 것이 좋을 것이라 생각했습니다.
또 다른 접근
Next.js 서버사이드 환경에서 실행되는 것이 이슈였기 때문에 이를 불러오는 지점을 next/dynamic을 이용하여 클라이언트에서 별도로 불러 사용하도록 하면 어떨까도 싶었습니다. 하지만 빌드된 디자인시스템 모듈이 테마 컨텍스트 등을 주입하는 것에서부터 시작하여 _app.tsx 등이 구성되어 있었기 때문에 이는 짧은 시간의 분석으로는 효과적인 해결책을 고안하기는 어려워 보였습니다.
다만 Node.js 버전을 낮춤으로 해결은 되었지만 프로젝트와 라이브러리 구성을 수정하여 최신 Node.js의 장점을 함께 취할 수 있는 길이 있을 것이라 생각했습니다. 언젠가 양쪽을 모두 챙기며 프로젝트에 기여하는 팀원이 되어있으면 좋겠습니다.
마무리하며
생각해보면, 프로젝트에 본격적으로 기여하기 전 이런 이슈를 먼저 만난 건 오히려 다행이었을지도 모릅니다. 문제를 트레이싱하고 분석하면서, 단순히 코드를 실행하는 것을 넘어서 사내 코드 구조와 패키지 간의 연결 관계를 빠르게 이해할 수 있었기 때문입니다.
문제를 해결하는 과정은 때로 귀찮고 예상 못한 문제에 시간을 사용하게 되지만 이런 경험이 결국 프로젝트 이해도를 높이고 개발자로서 성장하는데 바탕이 되는 중요한 경험이 아닐까 싶기도 합니다.