React Three Fiber

2024. 6. 13. 07:50UX Engineer 이야기
hongdoyoung

들어가며

WebGL 기반의 자바스크립트 라이브러리 three.js를 react에서 손쉽게 사용할 수 있도록 도와주는 라이브러리인 react-three-fiber(이하 RTF)에 대해 간단하게 알아봅니다.

 

특징

컴포넌트 기반

3D 장면을 웹상에서 구현하기 위해서는 수반되어야 하는 많은 지식이 있습니다. 카메라, 조명, 텍스처, scene 등을 하나하나 구현해나가야 하는데 three.js를 사용해 본 사람은 알겠지만 단순한 mesh 하나를 만드는데도 많은 양의 코드가 필요합니다. 복잡한 내용일수록 코드 가독성은 떨어지고 피로감은 올라가죠.

RTF는 react 개발자라면 익숙한 커스텀 태그와 컴포넌트 형식으로 각종 API를 제공합니다. 또한 react 훅과도 잘 맞고 useFrame, useLoader 같은 다양한 훅도 제공하고 있어 개발자들이 쉽게 3D 장면을 구현할 수 있도록 돕고 초반 러닝 커브를 대폭 낮춰줍니다. 아래의 두 코드는 모두 동일한 결과를 출력하지만 코드의 간결함은 시각적으로도 차이가 많습니다.

<Canvas>
  <mesh>
    <boxGeometry />
    <meshStandardMaterial />
  </mesh>
</Canvas>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.querySelector('#canvas-container').appendChild(renderer.domElement);

const mesh = new THREE.Mesh();
mesh.geometry = new THREE.BoxGeometry();
mesh.material = new THREE.MeshStandardMaterial();

scene.add(mesh);

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}

animate();

코드 발췌 : React three fiber documentation

 

최적화

RTF는 react의 리랜더링에 똑똑하게 반응하고 컴포넌트 언마운트 단계에서 알아서 메모리를 비워줍니다. 바닐라 자바스크립트로 작업할 때는 개발자가 일일이 신경 써야 하는 부분을 알아서 잡아주기 때문에 혹시 모를 실수를 미연에 방지하여 프로그래밍 완성도를 높일 수 있습니다.

 

SSR 지원

SSR 호환성도 갖추고 있어서 next.js에서도 잘 작동합니다. next13 이상 버전에서는 next.config.js에 다음 코드를 추가하면 됩니다.

transpilePackages: ['three']

 

시작하기

RTF의 모든 시작은 3D 장면이 그려질 Canvas를 선언하는 것으로 시작합니다.

<Canvas>
  // *
</Canvas>

코드 발췌 : React three fiber documentation

 

RTF는 three.js의 모든 객체를 카멜 케이스 형식의 태그로 선언하여 사용합니다.

// three.js
const mesh = new THREE.Mesh();
mesh.geometry = new THREE.BoxGeometry();

// RTF
<mesh>
  <boxGeometry />
</mesh>

코드 발췌 : React three fiber documentation

 

만약 객체에 인수를 넘기는 경우라면 args props를 이용합니다. 여러 개의 인수는 배열 형식의 props로 넘기게 됩니다.

// three.js
new THREE.BoxGeometry(2, 2, 2);

// RTF
<boxGeometry args={[2, 2, 2]} />

코드 발췌 : React three fiber documentation

 

객체 속성은 동일한 props를 넘기면 됩니다.

// three.js
const light = new THREE.AmbientLight();
light.intensity = 0.1;

// RTF
<ambientLight intensity={0.1} />

코드 발췌 : React three fiber documentation

 

에코시스템

RTF에는 방대한 관련 생태계가 잘 구축되어 있습니다. XE 블로그에서도 다룬 적이 있는 framer-motion의 헬퍼 라이브러리, 물리 엔진 적용, 후처리, 성능 컨트롤을 위한 손쉬운 GUI 적용까지 다양한 헬퍼 라이브러리로 개발 시간을 단축시키고 결과물의 퀄리티를 높일 수 있습니다.

 

@react-three/drei

자주 필요로 하는 기능을 미리 구현해놓은 헬퍼 라이브러리입니다.

개인적으로 3D 작업을 할 때 가장 어려웠던 부분은 구현하고자 하는 모양을 위해서 어떤 기능을 써야 하는지 알기 어렵다는 것이었습니다. 또한, 3D에 대한 개념이 부족한 상황에서 방대한 three.js의 API를 일일이 알아보기란 쉬운 일이 아니었죠. 그런 면에서 이 라이브러리는 3D 개념에 익숙하지 않은 개발자들에게 매우 큰 도움이 됩니다. 기본 mesh, 카메라 설정, 셰이더, 질감 표현, 각종 상호작용이 가능해지는 컨트롤 적용 등을 개발자에게 익숙한 방식으로 제공하고 있으며 적용 방법도 훨씬 직관적입니다.

아래는 간단한 정사각형 박스를 구현하는 코드입니다.

import { useRef } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';

function Box(props) {
  const ref = useRef();
  return (
    <mesh
      {...props}
      ref={ref}
    >
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={'orange'} />
    </mesh>
  )
}

export default function App() {
  return (
    <Canvas>
      <ambientLight intensity={Math.PI / 2} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
      <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
      <Box position={[0, 0, 0]} />
      <OrbitControls />
    </Canvas>
  )
}

drei 라이브러리에서 Box 컴포넌트를 임포트 하면 간편하게 구현됩니다.

import { useRef } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls, Box } from '@react-three/drei';

export default function App() {
  return (
    <Canvas>
      <ambientLight intensity={Math.PI / 2} />
      <spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />
      <pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />
      <Box position={[0, 0, 0]} material-color="orange"/>
      <OrbitControls />
    </Canvas>
  )
}

또한, 공식 깃헙 페이지에서는 가능한 모든 API 들에 대해 스토리북도 제공하고 있습니다.

 

@react-three/gltfjsx

보통 웹상에 3D 모델 형식인 glTF (GL Transmission Format)를 사용하려면 신경 써야 할 부분이 많고 적용 방식도 복잡한데 이를 jsx 형식으로 간편하게 사용할 수 있도록 돕는 라이브러리입니다. 모델의 재사용성, 동적 처리, 압축률, 최적화 등이 크게 상승합니다.

 

@react-three/postprocessing

노이즈, 비네팅 등 후처리를 쉽게 적용하게 도와주는 라이브러리입니다.

 

@react-three/flex

three.js로 생성한 오브젝트들에 css flex 방식을 적용할 수 있게 해주어 배치를 손쉽게 할 수 있도록 돕는 라이브러리입니다.

 

더 많은 라이브러리들을 공식 documentation에서 확인하세요!

 

마치며

bx 팀과 모션 스터디를 진행하면서 그동안 관심은 있었지만 선뜻 시작하지 못했던 three.js에 대해 찾아보다가 자주 사용하고 있는 react 기반의 라이브러리를 사용해 보고 소개하는 글을 작성하게 되었습니다. 앞으로 관련 스펙을 이용해서 실제 프로젝트에 반영해 볼 기회도 생길 텐데 좀 더 심화적인 부분도 공부해서 소개할 수 있는 시간을 마련해 보겠습니다.