현재 개인프로젝트로, 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' 예제 테마를 활용하였다.
지구본이 첫 렌더링 됐을때의 화면을 초기 지구본 위치라고 한다면,
기본예제를 사용하는 경우 초기 지구본 위치는 아프리카 대륙으로 되어있었다.
내 프로젝트는 한국어로 되어있고 초기 지구본 위치가 대한민국이면 좋겠다는 생각에
초기 지구본위치를 설정해두었었다.
↓ useEffect()를 사용한 초기 지구본 위치 설정
useEffect(() => {
if (
countriesData &&
countriesData.features.length > 0 &&
globeRef.current
) {
// 데이터가 로드되고 Globe 컴포넌트가 마운트된 후 카메라 위치 설정
setTimeout(() => {
const isMobile = window.innerWidth <= 1000;
globeRef.current.pointOfView(
{
lat: 35.9078,
lng: 127.7669,
altitude: isMobile ? 2.5 : 3,
},
0
);
}, 100); // 약간의 지연을 주어 Globe 컴포넌트가 완전히 렌더링되도록 함
}
}, [countriesData]);
하지만 지구본이 있는 경로로 다시 접속하거나, 뒤로가기를 통해 다시 지구본 페이지로 돌아올 때,
미리 설정해두었던 초기 지구본 위치 설정이 적용되지 않는 문제가 발견되었다.
새로고침때는 초기 지구본 위치 설정이 다시 되는걸 보니, 아무래도 컴포넌트 마운트가 매번 되지 않아 적용되지 않는 문제인 것 같았다.
문제원인
컴포넌트의 마운트/언마운트 주기와 관련이 있을것이라는 내 예측이 맞았다.
찾아보니, 새로고침할 때는 컴포넌트가 완전히 새로 마운트되지만, 라우팅을 통해 "/" 경로로 돌아올 때는 컴포넌트가 이미 마운트된 상태로 남아있을 수 있다. 이로 인해 useEffect로 설정했던 초기 지구본 위치 설정이 예상대로 실행되지 않았던 것이다.
해결방안
이 문제를 해결하기 위해 일단 다음과 같은 단계적 방법으로 접근했다.
- useLocation 훅을 사용하여 현재 라우트 감지
- 컴포넌트에 key state를 추가하고, 라우트가 변경될 때마다 이를 업데이트하여 강제 리렌더링 트리거
- Globe 컴포넌트에 key prop을 추가하여 라우트 변경 시 완전히 새로운 인스턴스 생성
- useEffect 내에서 Globe 컴포넌트의 onGlobeReady 이벤트를 추가 사용하여 초기 뷰 설정의 타이밍 개선
↓변경 코드
useEffect(() => {
setKey((prevKey) => prevKey + 1); // 라우트 변경 시 강제 리렌더링
}, [location]);
useEffect(() => {
if ((countriesData?.features?.length ?? 0) > 0 && globeRef.current) {
const setInitialView = () => {
const isMobile = window.innerWidth <= 1000;
globeRef.current.pointOfView(
{
lat: 35.9078,
lng: 127.7669,
altitude: isMobile ? 2.5 : 3,
},
4000 // 전환 시간을 4초로 설정, 부드럽게 이동
);
};
// Globe 컴포넌트의 onGlobeReady 이벤트 사용
if (globeRef.current.scene) {
setInitialView();
} else {
globeRef.current.onGlobeReady(() => {
setInitialView();
});
}
}
}, [countriesData, key]); // key를 의존성 배열에 추가
...
return (
<Globe
key={key} // 강제 리렌더링을 위한 키 추가
ref={globeRef}
...
/>
)
key라는 변수를 추가적으로 활용하여, 라우트 변경시 key값이 변경되도록 하였다.
그리고 key 변수를 useEffect의 의존성 배열에 추가하여 강제 리렌더링 시에도 초기 뷰 설정이 실행되도록 하였다.
이렇게 하면 라우트가 변경될때마다 지구본이 리렌더링된다.
문제 상황이었던 지구본이 있는 경로로 다시 접속하거나, 뒤로가기를 통해 다시 지구본 페이지로 돌아올 때 지구본의 초기위치가 적용되지 않았던 점이 해결된다.
또한, Globe 컴포넌트의 상태 를체크하는 코드도 추가하였다.
기존에는 'setTimeout()'을 사용중이어서 임의의 지연을 사용하는 문제점이 있었다.
Globe 컴포넌트의 `onGlobeReady` 이벤트와 `globeRef.current.scene`을 체크하여 Globe가 이미 준비되었는지 확인하였다.
onGlobeReady로 Globe 컴포넌트의 준비 상태를 직접 확인하였고, Globe 컴포넌트의 현재 상태를 고려하여 적절한 시점에 초기 뷰를 설정하도록 하였다. 초기 뷰 설정에 setTimeout을 사용하는 것에서 비롯되는 '랜덤'이라는 상황을 제거하여 신뢰할 수 있는 코드와 페이지를 만들었다.
추가개선
지난 게시물에서 지구본 컴포넌트가 포함된 페이지를 반응형으로 만들었었다.
2024.09.27 - [FE 개발일지] - [react-globe.gl] 지구본을 반응형으로 정가운데 오도록 만들기
[react-globe.gl] 지구본을 반응형으로 정가운데 오도록 만들기
현재 개인프로젝트로, vite+typescript 프로젝트 환경에서 react-globe.gl 라이브러리를 이용하여지구본으로 국가별 여행위험경보를 띄우고 있다. 문제상황https://github.com/vasturiano/react-globe.gl GitHub - vast
developer-dalsu.tistory.com
하지만 useCallback()을 사용하지 않았기 때문에, 불필요한 재렌더링이 발생할 수 있다.
개선된 코드에서는 useCallback()으로 함수를 메모이제이션하여 컴포넌트의 불필요한 재생성을 방지하였고,
useCallback()이 사용되는 부분을 따로 함수를 사용하여 밖으로 빼내어 더 깔끔한 구조를 만들었다.
↓개선된 코드
const updateDimensions = useCallback(() => {
if (globeContainerRef.current) {
const { offsetWidth, offsetHeight } = globeContainerRef.current;
setDimensions({ width: offsetWidth, height: offsetHeight });
}
}, []);
useEffect(() => {
updateDimensions();
window.addEventListener("resize", updateDimensions);
return () => window.removeEventListener("resize", updateDimensions);
}, [updateDimensions]);
Performance 비교
실제로, 변경 전과 후의 코드를 chrome의 lighthouse를 돌려본 결과
performance의 Total Blocking Time, Speed Index, Largest Contentful Paint 등의 성능 지표가 미세하게나마 개선되었다.
total Blocking Time이 4,810ms에서 2,970ms 로 크게 줄었고 speed index로 4.2s에서 3.2s로 줄었다. largest contentful paint도 2.5s에서 1.8s로 줄었다.
특히 Total Blocking Time의 큰 감소가 있길래 Total Blocking Time에 대해 찾아보니,
"Total Blocking Time(TBT)은 메인 스레드가 차단된 시간을 나타내는 중요한 성능 지표이며 TBT가 줄어들었다는 것은 브라우저가 사용자 상호작용에 더 빠르게 반응할 수 있다는 것을 의미합니다. 즉, 사용자가 버튼을 클릭하거나 화면을 스크롤할 때, 웹 페이지가 이전보다 더 빨리 반응하고, 끊김 없이 부드럽게 동작하게 된다는 뜻입니다. 결과적으로 사용자 경험이 향상되어 더 매끄러운 인터페이스를 느낄 수 있게 됩니다." 라고 한다.
사용자 경험을 개선시킬수 있다니, 다행이었다.
결론적으로 performance가 개선된 이유라고 생각되는 점은
useCallback을 사용하여 updateDimensions 함수를 메모이제이션했기때문에 불필요한 함수 재생성과 리렌더링이 줄어든 것으로 추측된다.
또한 key state를 사용한 조건부 리렌더링으로, 라우트가 변경되는 등의 필요한 경우에만 Globe 컴포넌트를 리렌더링하도록 최적화한게 작용한 것으로 추측된다.
완성본
다시 지구본페이지로 돌아왔을때, 초기 지구본 위치가 더이상 풀리지않고 대한민국으로 설정되는 것을 확인할 수 있다.
마무리하며, 깨달은 점
페이지 재진입시 초기 지구본 위치 설정 문제는 컴포넌트의 마운트 주기와 밀접한 관련이 있었다. useEffect와 key를 활용해 강제 리렌더링을 트리거하는 방식으로 해결할 수 있었다.
또한, onGlobeReady 이벤트를 사용해 지구본이 준비된 정확한 시점을 파악하여 설정하는 것이 setTimeout()보다 더 안정적이었다.
마지막으로, useCallback을 통해 불필요한 렌더링을 줄이고 성능을 최적화할 수 있었다.
'FE 개발일지' 카테고리의 다른 글
[회고][데브코스 FE] 3차 프로젝트 회고 (1) | 2024.12.16 |
---|---|
[react-globe.gl] 지구본 시점 위치 기억해두기 by 세션스토리지 (3) | 2024.10.11 |
[react-globe.gl] 지구본을 반응형으로 정가운데 오도록 만들기 (3) | 2024.09.27 |
[Vercel & Express.js] Vercel을 사용해 Express.js 서버 기반 프로젝트 배포하기 (0) | 2024.08.22 |
환율계산기 구현하기 only html css js (0) | 2024.08.06 |