ํ๋ก์ ํธ ๋ง๋ฌด๋ฆฌ๋ฅผ ํ๊ณ ์๋ ์ค, ์ ๋๋ก ๋ชจ๋ ๋์๊ฐ๊ณ ์๋ ํ์ธํ๋ค๊ฐ
์๋ ํ์ ํ๊ณ ์๋ ์ปดํฌ๋ํธ์ธ Globe์ ๋์์ด ์ด์ํ๋ค๋ ๊ฒ์ ๋ฐ๊ฒฌํ๋ค.
์ค๋์ ๊ฐ๋จํ๊ฒ useRef์ useEffect์ผ๋ก threejs ์ ๋๋ฉ์ด์ ์ ์ถ๊ฐํ๋ ์ปดํฌ๋ํธ๊ฐ
reload/refresh ๋์ด๋ ์ ๋๋ก ๋์ํ๋๋ก polling์ ํ์ฉํ ๊ฒ์ ๋ค๋ฃจ๊ณ ์ ํ๋ค.
์ด์ ์ฝ๋
'use client';
import classNames from 'classnames/bind';
import COLORS from 'constants/colors';
import dynamic from 'next/dynamic';
import { useEffect, useRef } from 'react';
import { GlobeMethods } from 'react-globe.gl';
import Countries from './countries.json';
import Style from './globe.module.scss';
const cx = classNames.bind(Style);
// https://github.com/vasturiano/react-globe.gl/issues/1#issuecomment-554459831
// https://github.com/vasturiano/react-globe.gl/issues/1#issuecomment-1710898408
const Globe = dynamic(() => import('react-globe.gl').then(mod => mod.default), {
ssr: false,
});
const RotatingGlobe = () => {
const globeRef = useRef<GlobeMethods>();
useEffect(() => {
const globe = globeRef.current;
if (globe != null) {
globe.controls().autoRotate = true;
globe.controls().autoRotateSpeed = 2.5;
globe.controls().enableZoom = false;
}
}, []);
return (
<div className={cx('globe')}>
<Globe
height={500}
ref={globeRef}
backgroundColor={COLORS['100000']}
globeImageUrl="/earth-dark.jpeg"
hexPolygonsData={Countries.features}
hexPolygonColor={() => COLORS[50]}
atmosphereColor={COLORS[50]}
/>
</div>
);
};
export default RotatingGlobe;
๋ฌธ์
๋ค์๊ณผ ๊ฐ์ด ref ์ํ๋ฅผ ํ์ธํด ๋ณด๋ undefined์๋ค.
useEffect(() => {
console.log('Globe component mounted');
console.log('Globe', globeRef);
return () => {
console.log('Globe component unmounted');
};
}, []);
useEffect๊ฐ ์คํ๋๊ธฐ ์ ์ ref๋ฅผ ์ load ๋์๋์ง ํ์ธํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด 100ms setInterval์ ์ฌ์ฉํด ๋ณด์๋ค.
์ด๋ ๊ฒ ์ปดํจํฐ๋ ์ปจํธ๋กค ์ฅ์น๊ฐ ์ธ๋ถ์ ์ฅ์น๊ฐ ์ค๋น๋ ์ํ ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๋ฐฉ์์ polling์ด๋ผ๊ณ ํ๋ค.
ํด๋น ์ฝ๋๋ window์ setInterval ์ธํฐํ์ด์ค๊ฐ ref์ ์ค๋น ์ฌ๋ถ๋ฅผ ํ์ธํ๋ ๊ฒ์ด๋ค.
// Globe ๊ฐ ์ถฉ๋ถํ ๋ก๋๋ ๋๊น์ง ์ด๊ธฐํ ๋๊ธฐ
// React ref ๊ฐ ์์ง undefined์ธ ๋ฌธ์ ๊ฐ ์์์. globeRef ๊ฐ ์ค๋น ๋๊ธฐ ์ ํ์ธํ๊ธฐ
useEffect(() => {
const interval = setInterval(() => {
if (globeRef.current) {
const globe = globeRef.current;
const controls = globe.controls();
controls.enableZoom = false; // zoom ๋นํ์ฑํ
controls.autoRotate = true; // ์๋ํ
controls.autoRotateSpeed = 2.5;
clearInterval(interval); // ref๊ฐ ์ค๋น๋๋ฉด polling mechanism ์ค๋จ
}
}, 100); // 100ms ๋ง๋ค ํ์ธ
return () => clearInterval(interval);
}, []);
100ms ์ธํฐ๋ฒ์ ๊ฐ๊ณ ํ์ธํ๋ฉฐ globeRef.current๊ฐ ์ ๋๋ก load ๋๋ฉด threejs ์ ๋๋ฉ์ด์ ์ ์ถ๊ฐํด ์ฃผ๊ณ ,
์ธํฐ๋ฒ์ ์ ๊ฑฐํด ์ค๋ค. ํด๋ฆฐ์ ํจ์๋ก RotatingGlobe ์ปดํฌ๋ํธ๊ฐ ์ธ๋ง์ดํธํ๋ค๋ฉด ์ธํฐ๋ฒ์ ์ ๊ฑฐํด ์ฃผ๋๋ก ํ๋ค.
์์ฑ์ฝ๋
'use client';
import classNames from 'classnames/bind';
import COLORS from 'constants/colors';
import dynamic from 'next/dynamic';
import { Suspense, useEffect, useRef } from 'react';
import { GlobeMethods } from 'react-globe.gl';
import Countries from './countries.json';
import Style from './globe.module.scss';
const cx = classNames.bind(Style);
// https://github.com/vasturiano/react-globe.gl/issues/1#issuecomment-1710898408
const Globe = dynamic(() => import('react-globe.gl').then(mod => mod.default), {
ssr: false,
});
const RotatingGlobe = () => {
const globeRef = useRef<GlobeMethods>();
// Globe ๊ฐ ์ถฉ๋ถํ ๋ก๋๋ ๋๊น์ง ์ด๊ธฐํ ๋๊ธฐ
// React ref ๊ฐ ์์ง undefined์ธ ๋ฌธ์ ๊ฐ ์์์. globeRef ๊ฐ ์ค๋น ๋๊ธฐ ์ ํ์ธํ๊ธฐ
useEffect(() => {
const interval = setInterval(() => {
if (globeRef.current) {
const globe = globeRef.current;
const controls = globe.controls();
controls.enableZoom = false; // zoom ๋นํ์ฑํ
controls.autoRotate = true; // ์๋ํ
controls.autoRotateSpeed = 2.5;
clearInterval(interval); // ref๊ฐ ์ค๋น๋๋ฉด polling mechanism ์ค๋จ
}
}, 100); // Check every 100ms
return () => clearInterval(interval);
}, []);
return (
<div className={cx('globe')}>
<Suspense fallback={null}>
<Globe
height={500}
ref={globeRef}
backgroundColor={COLORS['100000']}
globeImageUrl="/earth-dark.jpeg"
hexPolygonsData={Countries.features}
hexPolygonColor={() => COLORS[50]}
atmosphereColor={COLORS[50]}
/>
</Suspense>
</div>
);
};
export default RotatingGlobe;