[react-globe.gl] 지구본을 반응형으로 정가운데 오도록 만들기

현재 개인프로젝트로, vite+typescript 프로젝트 환경에서 react-globe.gl 라이브러리를 이용하여

지구본으로 국가별 여행위험경보를 띄우고 있다.

 

문제상황

https://github.com/vasturiano/react-globe.gl

 

GitHub - vasturiano/react-globe.gl: React component for Globe Data Visualization using ThreeJS/WebGL

React component for Globe Data Visualization using ThreeJS/WebGL - vasturiano/react-globe.gl

github.com

공식깃헙에는 다양한 예제가 나와있는데, 그중에서 ' Choropleth' 예제 테마를 활용하였다.

테마와 더불어 내가 가진 여행경보 데이터를 활용하여 내 것으로 만들고 있었다.

만들다가 알게되었지만, 브라우저 창의 사이즈를 줄이거나 늘리면 지구본이 가운데에 오지 않았다. 

게다가, 지구본에 적용해준 배경이미지 또한 브라우저 창의 사이즈에 따라 반응하지 않고 항상 사이즈가 고정되어있었다.

어떻게든 css를 이용하여 부모컨테이너에 'width:100vw '등 별의별짓을 다해봤지만, <Globe />라는 특수한 컴포넌트를 활용하고 있기 때문에 아무래도 다른 접근법이 필요할것 같았다.

 

 

해결방안

공식 깃허브의 issue에서, 다음과 같은 글을 찾을 수 있었다.

https://github.com/vasturiano/react-globe.gl/issues/34

 

Make globe component inherit width and height from parent element · Issue #34 · vasturiano/react-globe.gl

Is your feature request related to a problem? Please describe. When creating responsive globes for devices with smaller screens it becomes quite complicated setting the width and height of the glob...

github.com

이 글의 답변에 따르면, `react-sizeme` 라이브러리를 활용하는 방법이 있지만,  패키지가  2021년에 마지막으로 게시되었기 때문에 이걸 설치하면 많은 호환되지 않는 문제가 발생할 것으로 예상됐다.

 

etc-image-0

그래서 다른 답변을 참고했다. 라이브러리를 추가적으로 설치할 필요도 없고, useRef와 <Globe /> 의 width,height 속성을 활용하면 되는 방법이었다.

 

하지만 내 프로젝트로 적용시키는 과정에서, 위 방법처럼 너비, 높이 초기값을 0으로 설정해주었더니 화면에 지구본이 렌더링되지 않는 문제가 발생했다.

그래서, 초기값을 0으로 하되 useEffect를 이용하여 사용자가 접속한 브라우저 너비와 높이를 받아와 초깃값으로 다시 활용하였다.

const [dimensions, setDimensions] = useState({
    width: 0,
    height: 0,
});
useEffect(() => {
    const { innerWidth, innerHeight } = window;
    setDimensions({ width: innerWidth, height: innerHeight });
}, []);

 

그 다음으로는, useRef로 globeRef를 생성해 지구본 <Globe /> 컴포넌트에 연결한 뒤,

부모 컨테이너인 div에도 별도의 globeContainerRef를 생성해 useEffect를 활용하여 지구본이 반응형으로 작동하도록 설정했다.

또한, 부모 컨테이너 div에 justifyContent와 alignItems 스타일을 적용해 지구본이 화면 중앙에 배치되도록 하였다.

const globeContainerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
    const updateDimensions = () => {
      if (globeContainerRef.current) {
        const { offsetWidth, offsetHeight } = globeContainerRef.current;
        setDimensions({ width: offsetWidth, height: offsetHeight });
      }
    };

    updateDimensions();
    window.addEventListener("resize", updateDimensions);

    return () => window.removeEventListener("resize", updateDimensions);
  }, []);
  
  ...
  
  return (
  	<div style={{width:"100vw"}}>
        <div
            ref={globeContainerRef}
            style={{
              width: "100%",
              height: "100vh",
              display: "flex",
              justifyContent: "center",
              alignItems: "center",
              position: "relative",
            }}
          >
            {dimensions.width > 0 &&
              dimensions.height > 0 && ( // 조건부 렌더링
                <Globe
                  ref={globeRef}
                  width={dimensions.width}
                  height={dimensions.height}
                  {...globeConfig.globeProps} // 기타 커스텀 props 들
                />
              )}
      	</div>
    </div>
  )

 

완성본

최종적으로 완성한 반응형 지구본이다.

_2024_09_27_11_36_49_255-ezgif.com-speed.gif

 

 

마무리하며, 깨달은 점

 

여러 문제에 부닺히면서, 다양한 화면 크기에서 지구본이 잘 보이도록 하는 반응형 디자인의 중요성을 더욱 깊이 깨닫게 되었다.

특히 react-globe.gl와 같은 특수한 컴포넌트를 다룰 때는, 단순한 CSS 조작만으로는 해결할 수 없는 문제가 있을 수 있음을 알게되었다. 이러한 문제를 해결하기 위해 useRef와 useEffect 같은 React의 기본 기능을 효과적으로 활용해 반응형 웹을 구현하는 방법을 찾을 수 있었다는 점에서 큰 의미가 있었던 것 같다.

 

앞으로도 어떻게하면 화면 크기나 디바이스에 관계없이 일관된 UX를 제공할 수 있을지에 관한 고민을 계속 이어나가야겠다. 공식 문서나 깃허브 이슈 등 다양한 리소스를 참고하면 언제든지 해결책을 찾을 수 있다는 자신감도 생긴 것 같다.