[React Three Fiber] TypeScript 적용 삽질기

2024. 4. 4. 09:42카테고리 없음

 

항상 느끼는 거지만, 외부 라이브러리를 붙일 때 TypeScript 적용이 힘들다.

그래서 TypeScript 적용을 미뤄 JavaScript 로만 작성했는데, 어느날인가 문득 도전의식이 샘솟은 것이 아닌가.

그 결과, 삽질하는 과정 - 시이작.


 

✔️ 내 코드의 구조는 대략 이렇다.

Canvas 안에서 Bottles 라는 컴포넌트를 호출하고,
Bottles는 Bottle이라는 컴포넌트에 렌더될 3D 모델의 node의 키 값을 Prop으로 전달한다.
Bottle이 3D 모델에서 전달받은 glas와 cap이 사용된 node의 키를 geometry로 렌더한다.

 

= 이것은 하나의 3D 모델 안에 여러개의 node가 존재할 때, 동적 렌더링을 할 수 있는 완벽한 계획

 

.js 파일을 .tsx 파일로 바꾸고 나니, 역시나 빨간줄의 공포가 시작되었다. 😇

nodes[glas].geometry 와 nodes[cap].geometry 부분에서 빨간줄이 생기면서 합법적 괴롭힙이 시작됐다.

<Canvas>
  <Bottles />
</Canvas>

function Bottles() {
  return (
    <group dispose={null} scale={[0.1, 0.1, 0.1]}>
      <Bottle position={[140, 0, 0]} glas="Untitled018" cap="Untitled018_1" />
      <Bottle position={[80, 0, 0]} glas="Untitled078" cap="Untitled078_1" />
      <Bottle position={[-2, 0, 0]} glas="Untitled064" cap="Untitled064_1" />
      <Bottle position={[-90, 0, 0]} glas="Untitled052" cap="Untitled052_1" />
      <Bottle position={[-140, 0, 0]} glas="Untitled072" cap="Untitled072_1" />
      <Bottle position={[-180, 0, 0]} glas="Untitled007" cap="Untitled007_1" />
    </group>
  )
}


function Bottle({ position, glas, cap }) {
  const { nodes } = useGLTF("/models/bottles.glb");
  return (
    <group dispose={null}>
      <group
        rotation={[Math.PI / 2, 0, 3]}
        position={position}
      >
        <mesh
          castShadow
          receiveShadow
          geometry={nodes[glas].geometry}
          material={bottleMaterial}
        />
        <mesh
          castShadow
          receiveShadow
          geometry={nodes[cap].geometry}
          material={capMaterial}
        />
      </group>
    </group>
  )
}

 

아래와 같은 에러문이 발생했다.

Type error: Property 'geometry' does not exist on type 'Object3D<Object3DEventMap>'.

 


 

✔️ 그렇다면, 지금부터 원인 분석

React Three Fiber에서 3D 모델을 렌더링 할 때, useGLTF 라는 drei 에서 제공하는 예쁜 Hook의 도움을 받아 코드를 작성한다.

useGLTF 안을 들여다보면, 첫 번째 라인에 three-stdlib 에서 정의된 타입을 사용하는 것을 확인할 수 있다.

 

 

바로 확인 들어간다.

 

GLTF라는 인터페이스안에는 useGLTF hook 문법에서 자주 쓰는

const { nodes, materials } = useGLTF(modelpath);

에서의 nodes 와 materials 가 보이지 않는다.

 

왜냐?

내 추측으로는 3D 모델에서 nodes 가 어떤 타입이며, materials 가 어떤 정보를 담고있을지 추측할 수 없기 때문이다.

 

 

 


 

 

✔️ 그래서 어떻게 했냐면,

기존에 three-stdlib 에서 지원하는 GLTF 타입과 새로운 타입을 인터섹션 타입으로 정의해서 useGLTF 타입으로 임명해줬습니다.

여기서 키 포인트는, 우리는 GLTFResult에서 nodes 안에 Index Signature에 대한 타입을 선언해야 에러가 없다!

예를 들어 Untitled018 : THREE.Mesh 이렇게 작성하면, 아래와 같은 무시무시한 에러를 영접할 수 있다.
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type

코드로 표현하자면 이런식으로 할 수 있다.

import { GLTF } from "three-stdlib";

type GLTFResult = GLTF & {
  nodes: {
	[key : string] : THREE.Mesh;
  };
  materials: {
    ["default"]: THREE.MeshStandardMaterial;
  };
};

const Bottle = ({ position, glas, cap }) => {
  const { nodes } = useGLTF("/models/bottles.glb") as GLTFResult;
  return (
    <group dispose={null}>
      <group
        rotation={[Math.PI / 2, 0, 3]}
        position={position}
      >
        <mesh
          castShadow
          receiveShadow
          geometry={nodes[glas].geometry}
          material={bottleMaterial}
        />
        <mesh
          castShadow
          receiveShadow
          geometry={nodes[cap].geometry}
          material={capMaterial}
        />
      </group>
    </group>
  );
};

 

타입스크립트 너어~~ 정말 만만치 않구나? ^^

반응형