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 | 31 |
Tags
- 백준
- 리액트
- babel.config.js
- 리액트 네이티브 에러
- 리엑트 네이티브 아이콘
- AWS Access Key
- fire base
- react native
- aws bucket 정책
- 리액트 네이티브
- react native 세팅
- js
- Project
- GIT
- React
- react native CLI
- AWS
- 문자열 대소문자
- Access Key 생성
- 문자열 대소문자 구별
- error
- img upload
- react native 개발
- s3 upload
- firebase 라이브러리
- react native font
- Next.js
- react native picker
- 에러
- PongWorld
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 프로젝트' 카테고리의 다른 글
Yarn Berry로 Monorepo 구성하기 (0) | 2024.12.21 |
---|---|
느린 네트워크 환경에서 PhotoSwipe 이미지 로딩 속도 개선 (2) | 2024.09.09 |
[ Next.JS ] userAgent를 효율적으로 관리해보자 (1) | 2024.09.04 |
내가 설정한 description이 안 나온다. (0) | 2024.08.28 |
[ React ] Custom Confirm Modal 만들기 (0) | 2024.08.24 |