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>
);
};
타입스크립트 너어~~ 정말 만만치 않구나? ^^