Notice
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- babel.config.js
- img upload
- 리엑트 네이티브 아이콘
- 리액트 네이티브 에러
- error
- 문자열 대소문자 구별
- react native picker
- firebase 라이브러리
- js
- react native
- 리액트
- fire base
- react native 개발
- aws bucket 정책
- Access Key 생성
- AWS Access Key
- react native 세팅
- 리액트 네이티브
- 백준
- Next.js
- 에러
- AWS
- s3 upload
- react native CLI
- React
- Project
- 문자열 대소문자
- react native font
- PongWorld
- GIT
Archives
- Today
- Total
밝을희 클태
react-three/drei Environment 태그 렌더링 블로킹 본문
Blender로 만든 3D 모델을 GLB 파일로 export하여 프로젝트의 메인 페이지에 넣으려고 했다.
그러나 GLB 파일의 크기가 큰 편이라, 네트워크가 조금이라도 느린 상황에서는 빈 화면이 오래 노출될 수 있었다. 이를 방지하기 위해, GLB 파일 다운로드 상태를 실시간으로 표시하는 기능을 구현했다. 진행 상황을 1%부터 100%까지 표현하려고 했다.
전체 코드
'use client'; import React, { useState, useEffect } from 'react'; import { Canvas } from '@react-three/fiber'; import { Environment, OrbitControls, useGLTF } from '@react-three/drei'; function Model({ url }: { url: string }) { const { scene } = useGLTF(url); return <primitive object={scene} />; } export default function ModelViewer() { const [modelUrl, setModelUrl] = useState<string | null>(null); const [progress, setProgress] = useState<number>(0); useEffect(() => { const downloadModel = async () => { try { const response = await fetch(`${process.env.NEXT_PUBLIC_IMAGE_DOMAIN}/case_cherry.glb`); const reader = response.body?.getReader(); const contentLength = response.headers.get('Content-Length'); const total = contentLength ? Number(contentLength) : null; if (!reader || !total) { console.error('ReadableStream not supported or Content-Length unavailable.'); return; } let received = 0; const chunks: Uint8Array[] = []; while (true) { const { done, value } = await reader.read(); if (done) break; if (value) { chunks.push(value); received += value.length; setProgress(Math.round((received / total) * 100)); } } const blob = new Blob(chunks, { type: 'application/octet-stream' }); const blobUrl = URL.createObjectURL(blob); setModelUrl(blobUrl); } catch (error) { console.error('Failed to download model:', error); } }; downloadModel(); }, []); return ( <div className="w-full h-full relative flex justify-center items-center"> <Canvas camera={{ position: [-40, 25, 50], fov: 7 }} shadows> <Environment files="/textures/shanghai_bund_1k.hdr" /> {modelUrl && <Model url={modelUrl} />} {modelUrl && <OrbitControls autoRotate enableZoom={false} />} </Canvas> <div className={`absolute transition-opacity ${modelUrl ? 'opacity-0' : 'opacity-100'}`}> <progress value={progress} max="100" aria-valuenow={progress} aria-valuemin={0} aria-valuemax={100}></progress> </div> </div> ); }
문제
GLB 다운로드 진행 상황이 1%부터 매끄럽게 표시되지 않고, 중간부터 진행 상황이 표시되는 문제가 발생했다.

원인
<Environment> 태그에서 HDR 파일을 다운로드하는 동안 UI 리렌더링이 블로킹되는 것이 원인이었다.
React의 상태(setProgress)가 업데이트되어도, Environment의 비동기 작업이 진행 중일 때 UI가 즉시 반영되지 않았다.
해결
React의 <Suspense> 태그로 <Environment>를 감싸주어 해결했다.
<Suspense> <Environment files="/textures/shanghai_bund_1k.hdr" /> </Suspense>

Suspense가 해결한 이유 (예측)
<Environment> 태그가 HDR 파일을 다운로드하는 동안, 는 React에게 해당 작업이 비동기적임을 알려서 UI 업데이트 블로킹을 해결하는 거 같다?.
'KEYNUT 프로젝트' 카테고리의 다른 글
번들 사이즈 최적화 (0) | 2025.01.12 |
---|---|
Yarn Berry로 Monorepo 구성하기 (0) | 2024.12.21 |
느린 네트워크 환경에서 PhotoSwipe 이미지 로딩 속도 개선 (2) | 2024.09.09 |
[ Next.JS ] userAgent를 효율적으로 관리해보자 (1) | 2024.09.04 |
내가 설정한 description이 안 나온다. (0) | 2024.08.28 |