WebGL을 기반으로 한 JavaScript 라이브러리로, 웹 브라우저에서 3D 그래픽을 쉽게 렌더링할 수 있도록 도와준다. 기본적인 원리로는WebGL을 직접 다루는 복잡한 과정을 추상화하여 사용자가 간단한 코드로도 강력한 3D 환경을 구현할 수 있게 해준다.
📎 Three.js의 핵심 개념
Scene: 모든 3D 객체가 배치되는 공간Camera: 장면을 어떤 위치와 구조로 담아낼지 결정Renderer: 3D 장면을 캔버스에 그리는 역할Light: 3D 객체에 빛을 추가Mesh: 3D 모델을 구성하는 기본 단위로, Geometry와 Material로 구성Controls: 사용자 입력으로 카메라를 조작

@react-three.drei의useGLTF훅을 활용했다.
📎ㅤ@react-three/drei 란?
Three.js를React에서 더 쉽게 사용할 수 있도록 도와주는 유틸리티 라이브러리이다.useGLTF뿐만 아니라 카메라 컨트롤, 환경 설정 등을 손쉽게 구현할 수 있다.
const { scene } = useGLTF(modelPath) as GLTFResult;
<primitive object={scene} scale={0.68} rotation={[0, -Math.PI / 4, 0]} />
modelPath 경로의 GLTF 모델을 로드하여 scene 으로 저장한다.primitive 를 사용하여 GLTF 모델을 Three.js 의 object 형태로 변환 후 렌더링📎ㅤGLTF 란?
: 3D 모델을 압축하여 웹에서 빠르게 로드할 수 있도록 도와주는 포맷이다.
useEffect(() => {
if (scene) {
scene.position.set(0, 0, 0);
scene.traverse((object) => {
if (object.isMesh) {
object.castShadow = true;
object.receiveShadow = true;
}
});
}
}, [scene]);
scene.traverse 를 이용하여 모델 내의 모든 Mesh 요소에 그림자 설정을 적용📎ㅤtraverse 란?
:
scene내부의 모든 객체를 순회하면서 특정 동작을 수행하도록 해준다.
📎ㅤcastShadow와 receiveShadow 란?
castShadow: 해당 객체가 빛을 받아 그림자를 만들도록 설정receiveShadow: 해당 객체가 다른 객체의 그림자를 받을 수 있도록 설정- 이 두 속성을 모두 활성화해야 현실적인 그림자 표현이 가능하다
<Canvas shadows camera={CAMERA_CONFIG}>
<RoomLighting />
<Suspense fallback={null}>
<Center>
<primitive object={scene} scale={0.68} rotation={[0, -Math.PI / 4, 0]} />
</Center>
</Suspense>
<OrbitControls enableRotate={false} enablePan={false} minDistance={5} maxDistance={12} />
</Canvas>
Canvas 컴포넌트를 사용하여 렌더링primitive 를 통해 Three.js의 scene 객체를 직접 렌더링OrbitControls 를 추가하여 카메라 이동 범위를 제한📎ㅤOrbitControls 란?
:
Three.js에서 제공하는 컨트롤러로, 사용자가 마우스로 카메라를 회전, 이동, 줌할 수 있도록 도와주며 다양한 속성들로 설정하거나 막을 수 있다.
enableRotate={false}: 회전 기능 비활성화enablePan={false}: 카메라 이동 비활성화minDistance및maxDistance: 줌 범위 조절
재사용성과 관리의 용이성을 고려해서 별도의
constants파일로 분리해서 사용하였다.
export const CAMERA_CONFIG = {
position: new Vector3(0, 4, 10),
fov: 30,
};
📎ㅤFov 란?
: Field of View로 즉, 카메라가 한 번에 볼 수 있는 시야각을 의미한다. 값이 클수록 넓게 보이고, 작을수록 좁게 보인다. 이를 통해 원근감을 조절할 수 있다.
렌더링에서 가장 중요한 건 조명이다. 왜냐하면 조명의 유/무의 차이로 퀄리티가 달라보이기 때문에, 조명 설정이 가장 중요했다.
export function RoomLighting() {
const { mainLight, ambient, fill } = LIGHT_CONFIG;
return (
<>
<ambientLight intensity={ambient.intensity} color={ambient.color} />
<directionalLight
position={[mainLight.position[0], mainLight.position[1], mainLight.position[2]]}
intensity={mainLight.intensity}
color={mainLight.color}
castShadow
shadow-mapSize-width={mainLight.shadowConfig.mapSize[0]}
shadow-mapSize-height={mainLight.shadowConfig.mapSize[1]}
shadow-camera-near={mainLight.shadowConfig.camera.near}
shadow-camera-far={mainLight.shadowConfig.camera.far}
shadow-bias={mainLight.shadowConfig.bias}
/>
<pointLight
position={[fill.position[0], fill.position[1], fill.position[2]]}
intensity={fill.intensity}
color={fill.color}
/>
</>
);
}
📎ㅤLight 종류
ambientLight: 전체적인 기본 조명으로, 모든 방향에서 부드럽게 빛을 퍼뜨린다.directionalLighy: 태양빛과 유사한 조명으로, 특정 방향에서 평행하게 빛을 비춘다.pointLight: 특정 지점에서 모든 방향으로 빛을 방출하는 조명
방 내부에 배치되는 가구(책장, CD플레이어, 저금통, 방명록)은
useRoomItems훅으로 따로 만들어 관리하고 있다.
🛋️ㅤ가구의 종류
- 저금통, 방명록 : 사용자가 편집할 수 없는 가구 ❌
- CD 플레이어, 책장 : 사용자가 편집할 수 있는 가구 ⭕️

➡️ㅤ사용자는 가구 토글 기능을 이용해 CD 플레이어, 책장를 배치하거나 삭제할 수 있다.
const { items } = useRoomItems({ roomId, furnitures });
roomId 에 따라 동적으로 구성{items.map((item) => (
<Suspense key={item.id}>
<Furnitures item={item} onInteract={handleInteraction} onHover={handleHover} />
</Suspense>
))}
items 배열을 순회하여 Furnitures 컴포넌트를 렌더링onInteract 와 onHover 이벤트 핸들러를 통해 가구와의 상호작용을 구현
사용자가 원하는 테마로 변경 가능하다
| 방명록 | 책장(도서) |
|---|---|
![]() | ![]() |
| CD플레이어(음악) | 저금통(포인트) |
|---|---|
![]() | ![]() |
const handlePointerOver = (e: MouseEvent) => {
e.stopPropagation();
scene.traverse((object) => {
if (object.isMesh) {
object.material.emissive = object.material.color;
object.material.emissiveIntensity = 0.5;
}
});
};
const handlePointerOut = (e: MouseEvent) => {
e.stopPropagation();
scene.traverse((object) => {
if (object.isMesh) {
object.material.emissiveIntensity = 0;
}
});
};
emissiveIntensity를 0으로 설정하여 원래 상태로 복구📎ㅤEmissive 란?
- 모델이 자체적으로 빛을 내는 효과를 줄 수 있도록 하는 속성
- 호버 시,
emissiveIntensity값을 높여 하이라이트 효과를 부여
처음 사이트에 모델링을 렌더링 했을 때는 보이지도 않아서 뭐가 잘못되었는지... 카메라 세팅 끝에 방을 정중앙에 불러올 수 있었다.

이제 가구들은 먼지처럼 그저 흩어져 있었다. 사실 이 부분은 노가다로 위치를 찾고 회전을 시키는 것 밖에는 없는 것 같아서 일일히 맞추었다.
