[GatsbyJS] 'document is not defined' 해결하기

예도리·2021년 8월 17일
0


나한테 이럴순 없지,,, 얼마 안남았는데~ ~~ ~!

✍🏻 사건 배경

며칠 전, 메인 페이지 디자인 구현이 거의 끝났고 (사실 다 끝났다고 생각했는데 쫌쫌따리로 계속 수정할 것이 생긴다 🥲), 반응형 적용도 90% 이상 끝난 상태였다. 하지만 뭔가,,, 더 손을 대고 싶다고 생각하고 있던 중, 문득 헤더를 수정해야겠다는 생각이 들었다.
헤더 위치가 고정이어도 반투명하게 만들었기 때문에 글을 읽을 때 딱히 방해가 되진 않았지만, 스크롤을 내리면 아예 화면에서 사라지는게 전체적인 가독성도 좋을 거 같았고 더 깔끔해 보일 거 같았다.
스크롤 방향에 반응하는 헤더를 참고해서 순탄히 잘 적용하는 듯 했으나,,,! 예기치 못한 문제가 발생한다. 모바일에서 홈페이지에 들어가보고 싶어서 빌드하고 github page에 올리려고 했는데 yarn build를 하자 에러가 발생했다.

아니 이럴리가 없는데?? 1초 전까지만 해도 잘됐었는데,,,,?? 라는 생각과 배신감이 들었다. yarn develop으로는 아무런 에러 없이 잘 작동하고 있었기 때문에 당연히 빌드될 줄 알았기 때문이다.

🕵🏻‍♀️ 원인 분석

gatsby develop & gatsby build

develop은 빠른 피드백을 위한 개발 모드이고 build는 개발이 끝난걸 패키징하는 걸로만 알았는데, gatsby develop과 gatsby build에는 명확한 차이점이 존재했다. 생각보다 꽤 많기 때문에 일단 여기서는 에러가 발생하게 된 가장 큰 요인만 다뤄보고 나머지는 따로 글을 작성해야겠다.

결론부터 말하자면 gatsby develop은 runtime 방식으로 동작하고, gatsby build는 build time 방식으로 동작한다.

Build time vs Runtime

그렇다면 각각 build time, runtime으로 동작한다는 것은 무슨 뜻일까?

웹 브라우저에서 사용자가 클릭을 하는 등의 상호작용이 일어나는 과정을 browser runtime이라고 한다. JavaScript 코드는 브라우저와 상호작용 할 수 있고 브라우저가 제공하는 API(ex. window.loation나 AJAX, DOM에 마크업을 동적으로 삽입 등)를 사용할 수 있다.

반대로, build time은 server process를 사용해서 사이트를 나중에 웹 브라우저에 전달할 수 있는 파일로 컴파일하는 과정을 말한다. 따라서 이때는 window처럼 브라우저가 제공하는 API는 사용할 수 없다. Node.js 서버에는 그런게 존재하질 않아요~!

gastby develop을 실행하면 webpack server가 실행되고 runtime 방식으로 브라우저에서 사이트를 미리 확인할 수 있게 된다. (브라우저 API도 사용 가능 → document 접근 가능) 반면, gatsby build는 build time으로 동작해서 브라우저를 사용할 수 없기 때문에 브라우저가 제공하는 API를 호출하지 않도록 신경써야한다(?) → 이 부분 번역이 맞는지 잘 모르겠다 🥲

SSR

Node.js 같은 server-side language로 페이지와 assets을 브라우저에서 렌더링 될 수 있는 HTML로 변환하는 과정을 server-side rendering, SSR이라고 한다. Gastby는 렌더링 되기 전, 즉 build time에 사이트 전체를 만들어낸다. 이미 모든게 합쳐지고 컴파일되었기 때문에 사이트가 배포됐을 때 server-side process를 구동할 필요가 없다.

에러가 발생한 이유

그렇다면 에러에 나와있는 "document" is not available during server side rendering의 이유를 이제는 알 수 있다. SSR은 build time에 HTML이 만들어지고 gatsby build를 사용하면 해당 방식으로 동작하기 때문에 Node.js에는 존재하지 않는, 즉 브라우저 API로 제공되는 document에 접근할 수 없기 때문이다.

최근에 추가한 스크롤 감지 헤더 코드에서 document에 접근했기 때문에 에러가 발생한 것이었다.

const documentRef = useRef(document);

해당 코드에서 useRef를 사용해 ref 객체를 만들고, 해당 객체에 document를 설정해 줬다. gatsby build를 실행했을 때, build time이기 때문에 브라우저에 렌더링돼서 DOM이 생성되기 전, 즉 document가 아직 정의되지 않은 상태에서 document에 접근해서 에러가 발생한 것이다. → 8/26일 추가 : 이렇다기 보다는 그냥 server side language인 Node.js에는 window, document가 없으니깐,,, 정의되지 않은 걸로 뜬 게 맞는 것 같다

📝 해결 방법

다행히도 Gatsby에서 해결법 몇 가지를 제안하고있다.

  1. window/document가 생성됐는지 먼저 확인한다.
  2. 해당 코드를 useEffect로 감싼다.
  3. 모듈 문제라면 gatsby-node.js를 수정한다.

내 경우 3번은 해당이 안되므로 1번과 2번을 시도해봤다.

if (typeof document !== 'undefined') {
  const documentRef = useRef(document);
  ...
}

1번을 적용하니 바로 해결됐다,,, ㅋㅋㅋ 하지만 너무 쉽게 해결이 돼서 그런지 찝찝한 느낌에 2번을 시도해봤다. useEffect에 전달된 함수는 렌더링이 완료된 후 수행되기 때문에 더 안전하다고 생각했기 때문이다. 하지만 Hook 사용이 미숙해서 그런지 실패했다,,,

일단은 1번으로 해결한 것에 만족하고 넘어가기로 했지만 진짜 이 방법으로 해도 문제가 없는건지 의문이 든다.

출처

0개의 댓글