사용자 환경에 따른 유동적인 컴포넌트 로딩하기
Dynamic Import로 컴포넌트 분할하고, 필요할 때만 로딩해봐요.
최근에 내 블로그 구성하는 것을 즐거워하며 개발 중이다.
이모지를 애용하는 나는 이번 블로그 카테고리에는 이모지를 넣었다.
맥에서는 잘 표시되던 이모지가 이상하게 표시되고 있다. 찾아보니까 Microsoft에서도 이 이모지 지원하더라... 왜 저렇게 나오는걸까?
어찌됐던 그러한 이유로, Twitter에서 오픈소스로 공개하고 있는 Twemoji 프로젝트를 이용하여 이모지를 대체하기로 마음 먹었다.
Twemoji 프로젝트에서 제공하는 js는 document.body
에 있는 모든 이모지를 대체할 수 있다.
idea from ridibooks
나는 윈도우와 맥 환경을 자주 왔다갔다한다. 윈도우에서 리디북스를 접속할 때는 분명히 웹폰트로 "나눔고딕"을 사용했었는데, 맥 환경에서는 아무 폰트도 로딩하지 않는게 아니겠는가?
리디는 navigator.userAgent
를 기준으로 폰트 로딩 여부를 가져오는 듯 보인다. 만약 로딩 조건(윈도우) 환경이라면 추가적인 번들 파일을 로딩한다. 해당 번들 파일은 webfontloader를 이용하여 나눔고딕을 로딩하는 것으로 보인다.
이런 방식으로 번들(컴포넌트)를 분리하여 조건에 충족될 때만 로딩할 수 있다면, 정상적으로 표시되는 환경에서도 리소스를 로딩하는 문제를 피할 수 있을 것이다.
Dynamic Import
필요할 때만 해당 컴포넌트를 로딩할 수 있는 기능, Dynamic Import에 대해 잠시 짚고 넘어가자.
내 블로그는 Next.js를 사용하고 있는데, Next.js에서는 Dynamic Import를 기본적으로 지원하고 있다. Dynamic Import는 컴포넌트를 Lazy Loading 해준다. 즉 컴포넌트가 필요할 때에 컴포넌트를 로딩하여 사용할 수 있다는 것이다.
더 많은 설명이 필요하다면, Next.js 공식 문서를 참고하자.
여담이지만, Next.js를 사용하지 않는 환경이라면 loadable components를 고려해보아도 된다. Dynamic Import와 비슷한 역할을 한다.
React.lazy?
React v16.6.0에는 React.lazy
가 추가되었고, 많은 곳에서 React.lazy의 사용을 권장하고 있다. Dynamic Import는 React.lazy와 비슷하게 작동하는 것으로 보인다. 단 다른 점이라면 React.lazy는 SSR을 지원하지 않는다는 정도?
SSR이 필요하지 않을 때 이것을 쓰면 될 것 같지만, Dynamic Import도 No SSR 옵션을 제공한다. 그래서 나는 Dynamic Import만 사용하고 있다.
어느 환경에서 Twemoji를 표시할 것인가?
사실, 내가 사용하는 환경에서는 윈도우 환경 이외에서는 이모지가 파괴되는(...) 일이 일어나지는 않지만, 모든 환경에서 테스트를 한 것은 아니므로 그냥 안드로이드와 애플 기기들을 제외하곤 모두 로딩하는 방안을 채택했다.
구현은 어떻게?
import dynamic from 'next/dynamic';
const TwemojiLoader: React.FC = () => { if ( typeof window !== 'undefined' && !/(Mac|iPhone|iPod|iPad)/i.test(navigator.platform) && navigator.userAgent.toLowerCase().indexOf('android') === -1 ) { // 조건 충족 시에 dynamic import const Twemoji = dynamic(() => import('~/components/features/global/twemoji')); return <Twemoji />; } return <></>; // fallback};
<TwemojiLoader />
그나마 나은 구현 방법이 무엇이 있을까 고민하다가, 이렇게 구현하게 되었다.
Next.js Dynamic Import의 사용법이 상당히 간단하게 구현 가능하게 되어 있어서 다행이였다.
결과 🧑💻
지금 여러분이 보고 있는 페이지도 결과물이긴 하나, 윈도우에서만 보일 것이다. 😅
이로써, 사용자 환경에 따라 유동적으로 lazy 로딩되는 컴포넌트를 만들 수 있었다. 끝!