- Published on
웹의 3D 개발 2026 — Three.js·R3F·WebGPU·Gaussian Splatting까지 (현대 3D 웹 스택 심층 가이드)
- Authors

- Name
- Youngju Kim
- @fjvbn20031
프롤로그 — WebGL은 어른이 되었고, WebGPU는 어제 일이 되었다
2010년대 후반, 웹에서 3D를 한다는 건 "WebGL"을 한다는 뜻이었다. Three.js가 그 위에 사람답게 쓸 수 있는 추상을 얹었고, 우리는 GLSL 셰이더를 두 벌(WebGL용·WebGPU용)로 유지하며 살았다.
2026년 1월, 그 풍경이 완전히 바뀌었다. Safari 26이 macOS Tahoe와 iOS에서 WebGPU를 정식 출시하면서, WebGPU는 Baseline이 되었다. Chrome·Edge·Firefox·Safari 모두 기본으로 켜져 있고, 전역 커버리지는 약 95%다. 나머지 5%는 Three.js의 자동 폴백이 WebGL 2로 처리한다.
이 한 줄로 끝나는 변화가 아니다.
- Three.js r182(2025년 12월 릴리스)가
WebGPURenderer를 권장 렌더러로 올렸다. - TSL(Three Shading Language) — 한 번 짠 셰이더가 WGSL과 GLSL로 동시에 컴파일된다. 두 벌 유지가 끝났다.
- React Three Fiber v9이 비동기
glprop을 받아 WebGPU 초기화를 자연스럽게 처리한다. - Gaussian Splatting이 폴리곤 메시와 나란히 — 폴리곤 없이 — 사진실적 씬을 실시간으로 그리는 새로운 표현이 되었다.
- Meshy·Tripo·Rodin은 텍스트 한 줄로 PBR 텍스처 입은 메시를 뽑는다.
이 글은 2026년의 웹 3D 스택을 한 호흡으로 정리한다. 처음 씬을 띄우는 코드부터 R3F·WebGPU·gsplat·AI 3D까지, 그리고 그 사이에서 "어디서 어떤 도구를 쓸지" 결정하는 매트릭스까지.
1장 · 렌더링 파이프라인 — 3D는 어떻게 그려지는가
먼저 그림 한 장. 도구를 알기 전에, 도구가 무엇을 하는지부터 봐야 한다.
[Scene Graph]
| (mesh / light / camera 트리)
v
[CPU: JS] -- 매트릭스·컬링·정렬 ----+
|
v
[Draw Call]
|
GPU 파이프라인 ──────────────────┴────────────────
| |
v v
Vertex Shader -> Rasterizer -> Fragment Shader
(정점 변환) (픽셀로 자르기) (픽셀 색)
| |
v v
Z-buffer / Blend / Output
|
v
[Framebuffer]
|
v
<canvas>
이 파이프라인의 어디를 누가 책임지는가가 곧 스택 선택의 기준이 된다.
- CPU 쪽 — 씬 그래프, 변환 행렬, 컬링, 정렬. 여기서 끝까지 책임지는 건 Three.js다. R3F는 그 위에 React식 선언으로 얹는다.
- 드로우 콜 — GPU로 던지는 명령 단위. 이게 적을수록 빠르다. 인스턴싱·머지·아틀라스가 다 이걸 줄이는 기술이다.
- 셰이더(Vertex·Fragment) — GPU에서 도는 작은 프로그램. WebGL은 GLSL, WebGPU는 WGSL. TSL이 둘을 하나로 묶는다.
- 출력 합성 — Z-buffer·블렌딩·후처리(post-processing).
기억할 한 줄: "드로우 콜이 모든 것의 절반이고, 셰이더가 나머지 절반이다."
2장 · Three.js — 웹 3D의 사실상 표준
숫자부터. Three.js는 npm 주간 다운로드 270만으로 Babylon.js의 약 270배, PlayCanvas의 약 337배다. "사실상 표준"이 아니라 그냥 표준이다.
가장 작은 씬을 띄우는 코드. 세 가지가 필요하다: 씬·카메라·렌더러, 그리고 안에 들어갈 메시.
import * as THREE from 'three'
// 1. 씬 — 모든 것이 들어가는 컨테이너
const scene = new THREE.Scene()
// 2. 카메라 — 어디서 보는가
const camera = new THREE.PerspectiveCamera(
75, // fov(시야각, degree)
window.innerWidth / window.innerHeight,
0.1, // near clip
1000 // far clip
)
camera.position.z = 5
// 3. 렌더러 — 2026년 기본은 WebGPURenderer
import { WebGPURenderer } from 'three/webgpu'
const renderer = new WebGPURenderer({ antialias: true })
await renderer.init() // 비동기 초기화 — 중요
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// 4. 메시 = Geometry + Material
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshStandardMaterial({ color: 0x44aa88 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)
// 5. 라이트 — 표준 머티리얼은 빛이 없으면 검정으로 보인다
scene.add(new THREE.AmbientLight(0xffffff, 0.4))
const dir = new THREE.DirectionalLight(0xffffff, 1.0)
dir.position.set(5, 10, 7.5)
scene.add(dir)
// 6. 루프 — requestAnimationFrame이 아니라 setAnimationLoop
renderer.setAnimationLoop(() => {
cube.rotation.x += 0.01
cube.rotation.y += 0.01
renderer.render(scene, camera)
})
세 가지를 짚는다.
await renderer.init()— WebGPU는 비동기다. 이 한 줄을 잊으면 첫 프레임이 검정이다.MeshStandardMaterial은 라이트가 필요하다 — 화면이 검정이면 라이트부터 의심한다.setAnimationLoop—requestAnimationFrame대신. WebXR이 자동으로 잡힌다.
이게 모든 Three.js 코드의 뼈대다. 나머지는 이 위에 얹는 것이다.
3장 · React Three Fiber + drei — React식 3D
명령형 코드는 작을 때는 깔끔하지만, 씬이 50개 노드를 넘어가면 빠르게 누더기가 된다. React Three Fiber(R3F) 는 Three.js를 React 컴포넌트 트리로 표현한다.
같은 큐브를 R3F로 다시 쓰면:
import { Canvas } from '@react-three/fiber'
import { OrbitControls, Environment } from '@react-three/drei'
function Cube() {
return (
<mesh rotation={[0, 0.4, 0]}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="hotpink" />
</mesh>
)
}
export default function Scene() {
return (
<Canvas camera={{ position: [0, 0, 5], fov: 75 }}>
<ambientLight intensity={0.4} />
<directionalLight position={[5, 10, 7.5]} intensity={1} />
<Cube />
<OrbitControls />
<Environment preset="city" />
</Canvas>
)
}
같은 일을 한다. 그런데:
- 씬 그래프가 React 트리다. 조건부 렌더링·상태 관리·Hooks가 그대로 통한다.
<Canvas>는 리사이즈·렌더 루프·픽셀 비율을 알아서 잡는다.- drei — Poimandres 팀의 헬퍼 라이브러리.
OrbitControls·Environment·useGLTF·Html·Text같은 매일 쓰는 것들이 다 있다.
R3F v9의 WebGPU — 비동기 gl prop
R3F v9이 gl prop을 비동기 팩토리로 받을 수 있게 됐다. WebGPU 초기화가 자연스럽게 묶인다.
import { Canvas } from '@react-three/fiber'
import { WebGPURenderer } from 'three/webgpu'
<Canvas
gl={async (props) => {
const renderer = new WebGPURenderer(props)
await renderer.init()
return renderer
}}
>
{/* ...씬... */}
</Canvas>
2026년 5월 현재, R3F의 WebGPU 통합은 아직 완전히 매끄럽지는 않지만 — Poimandres 팀이 적극적으로 다듬는 중이고 — 위 패턴은 프로덕션에서 충분히 돈다. WebGL 2 폴백이 필요하면 WebGLRenderer를 던지면 된다.
useFrame — 매 프레임 훅
R3F의 가장 React스러운 부분. 컴포넌트가 매 프레임 호출되는 콜백을 등록한다.
import { useRef } from 'react'
import { useFrame } from '@react-three/fiber'
function Spinner() {
const ref = useRef(null)
useFrame((state, delta) => {
if (ref.current) ref.current.rotation.y += delta
})
return (
<mesh ref={ref}>
<torusKnotGeometry args={[1, 0.3, 128, 32]} />
<meshStandardMaterial color="orange" />
</mesh>
)
}
delta는 이전 프레임으로부터의 초 단위 경과. 프레임률에 안 흔들리는 애니메이션의 출발점.
4장 · WebGPU와 TSL — 셰이더 두 벌이 한 벌로
WebGL과 WebGPU의 차이를 한 문장: WebGL은 OpenGL ES 2.0의 웹 포팅, WebGPU는 Vulkan·Metal·DX12 시대의 현대적 GPU API.
실무적으로 무엇이 달라지는가:
- 드로우 콜 비용이 낮다 — 드로우 콜이 많은 씬(파티클·인스턴스 다수)에서 2~10배 빨라진다.
- 컴퓨트 셰이더가 1급 시민 — GPGPU(파티클·물리·시뮬레이션·포스트프로세싱)가 메인 파이프라인 안에서 자연스럽다.
- WGSL — WebGL의 GLSL 대신 새로운 셰이더 언어.
마지막이 항상 골치였다. WebGL 시대에 GLSL을 짰는데, WebGPU로 가려면 WGSL로 다시 써야 했다. TSL이 이걸 끝낸다.
TSL = Three Shading Language. 노드 기반 셰이더 추상. 한 번 짜면 Three.js가 내부적으로 WGSL(WebGPU용)·GLSL(WebGL용) 양쪽으로 컴파일한다.
간단한 노이즈 셰이더(머티리얼 색을 노이즈로 흔드는 예):
import { MeshStandardNodeMaterial } from 'three/webgpu'
import { uniform, vec3, mix, sin, time, positionLocal } from 'three/tsl'
const speed = uniform(1.0)
const wave = sin(positionLocal.y.mul(8.0).add(time.mul(speed)))
const color = mix(vec3(0.1, 0.4, 0.9), vec3(1.0, 0.4, 0.2), wave.mul(0.5).add(0.5))
const material = new MeshStandardNodeMaterial()
material.colorNode = color
positionLocal·time·mix·sin 다 노드다. JS로 셰이더를 조립한다. GLSL 텍스트도 WGSL 텍스트도 손으로 안 짠다.
작은 WGSL 한 조각이 어떻게 생겼는지만 참고로 (TSL이 내부에서 비슷한 걸 만든다):
@fragment
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
let c = mix(vec3(0.1, 0.4, 0.9), vec3(1.0, 0.4, 0.2), sin(uv.y * 8.0) * 0.5 + 0.5);
return vec4(c, 1.0);
}
요는 — 2026년에는 직접 짤 일이 거의 없다. TSL이 처리한다. WGSL을 한 번 읽어두는 정도면 충분하다.
5장 · glTF — 3D의 JPEG
폴리곤 모델을 웹으로 옮기는 표준은 glTF 2.0이다. "3D의 JPEG"라고 부른다. PBR 머티리얼·애니메이션·스킨·드라코 압축까지 한 파일에 들어간다.
Three.js의 로더:
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js'
const draco = new DRACOLoader()
draco.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
const loader = new GLTFLoader()
loader.setDRACOLoader(draco)
loader.load('/models/robot.glb', (gltf) => {
scene.add(gltf.scene)
// gltf.animations 에는 AnimationClip 배열이 들어있다
})
R3F + drei로는 한 줄이다:
import { useGLTF } from '@react-three/drei'
function Robot() {
const { scene } = useGLTF('/models/robot.glb')
return <primitive object={scene} />
}
useGLTF.preload('/models/robot.glb')
useGLTF는 Suspense로 묶이고, 같은 모델을 여러 곳에서 쓰면 한 번만 로드한다. preload로 미리 받아둘 수도 있다.
glTF 최적화 — 3가지만 기억
- Draco 압축 — 정점 데이터를 압축. 파일 크기 5~10배 작아진다.
- KTX2 / Basis 텍스처 — JPG·PNG 대신 GPU가 바로 먹는 압축 텍스처. 메모리·로드 시간 절약.
gltf-transformCLI — 위 둘을 한 번에 해주는 도구. CI에 박아두면 더 이상 신경 안 써도 된다.
npx @gltf-transform/cli optimize input.glb output.glb \
--texture-compress webp --simplify 0.5
6장 · 애니메이션 — 클립부터 스프링까지
3D 애니메이션은 크게 세 갈래다.
- glTF에 들어있는 본/스킨 애니메이션 — Blender·Maya에서 만든 걸 그대로 재생.
- 수학으로 도는 애니메이션 —
useFrame안에서 회전·트랜슬레이션. - 인터랙션 기반 — 호버·드래그·스크롤에 반응. 보통 스프링 물리로.
glTF 클립 재생
import { useAnimations, useGLTF } from '@react-three/drei'
import { useEffect } from 'react'
function Robot() {
const { scene, animations } = useGLTF('/models/robot.glb')
const { actions, names } = useAnimations(animations, scene)
useEffect(() => {
actions[names[0]]?.reset().fadeIn(0.3).play()
}, [actions, names])
return <primitive object={scene} />
}
스프링 애니메이션 — react-spring/three
import { useSpring, animated } from '@react-spring/three'
function Box({ hovered }) {
const { scale } = useSpring({ scale: hovered ? 1.3 : 1.0 })
return (
<animated.mesh scale={scale}>
<boxGeometry />
<meshStandardMaterial />
</animated.mesh>
)
}
값이 튀지 않고 물리적으로 자연스럽게 보간된다. 포트폴리오·랜딩 페이지의 미세한 디테일에 결정적이다.
7장 · 포스트프로세싱 — 한 끗 차이의 마감
같은 씬이라도 블룸·SSAO·필름 그레인 한 번 거치면 영상미가 다른 차원이 된다. 표준 라이브러리는 postprocessing (Vanruesc), R3F 래퍼는 @react-three/postprocessing.
import { EffectComposer, Bloom, DepthOfField, Vignette } from '@react-three/postprocessing'
<Canvas>
{/* ...씬... */}
<EffectComposer>
<Bloom intensity={1.2} luminanceThreshold={0.6} mipmapBlur />
<DepthOfField focusDistance={0} focalLength={0.02} bokehScale={2} />
<Vignette eskil={false} offset={0.1} darkness={1.0} />
</EffectComposer>
</Canvas>
조심할 점: 포스트프로세싱은 풀스크린 패스다 — 픽셀이 많을수록 비싸다. 모바일에서는 항상 pixelRatio를 캡(보통 1.5~2.0)하고, 효과 두세 개만 고른다.
8장 · 성능 — 드로우 콜이 모든 것의 절반
3D 웹 성능은 거의 항상 드로우 콜 수와 셰이더 비용 두 곳에서 갈린다. 2026년의 핵심 패턴 다섯.
1. 인스턴싱 — 같은 메시 1만 개를 한 번에
같은 지오메트리·머티리얼의 메시를 여러 개 그려야 할 때(나무·풀·박스 더미), 인스턴싱을 쓰면 드로우 콜이 1로 줄어든다.
import { Instances, Instance } from '@react-three/drei'
<Instances limit={10000}>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="white" />
{positions.map((p, i) => (
<Instance key={i} position={p} />
))}
</Instances>
WebGPU는 인스턴싱 비용이 더 낮다. WebGL에서 5천 인스턴스가 한계였다면, WebGPU에서는 5만이 종종 돈다.
2. 프러스텀 컬링·LOD
Three.js는 기본으로 카메라 시야 밖 객체를 그리지 않는다(프러스텀 컬링). 단, Mesh.frustumCulled는 기본 true니까 끄지 말 것. LOD(Level of Detail)는 카메라 거리에 따라 메시 해상도를 바꾼다.
import { Detailed } from '@react-three/drei'
<Detailed distances={[0, 10, 50]}>
<HighPolyMesh />
<MidPolyMesh />
<LowPolyMesh />
</Detailed>
3. 머티리얼·지오메트리 공유
같은 머티리얼·지오메트리는 메모리에서 단 한 벌만. R3F에서는 컴포넌트 바깥에서 만들어 공유한다.
4. 텍스처 — KTX2와 mipmap
JPG·PNG는 CPU에서 디코드 후 GPU로 업로드. KTX2(Basis Universal)는 GPU가 압축 그대로 먹는다. 로드는 빠르고, VRAM도 적게 쓴다.
5. pixelRatio 캡
레티나에서 devicePixelRatio가 3이면 화면 픽셀이 9배다. 항상 캡한다.
<Canvas dpr={[1, 2]}> {/* 최소 1, 최대 2 */}
9장 · WebXR — VR·AR을 웹으로
setAnimationLoop 한 줄과 WebXRManager 덕에 Three.js의 WebXR은 거의 공짜다. R3F에는 @react-three/xr이 있다.
import { XR, createXRStore, XROrigin } from '@react-three/xr'
const store = createXRStore()
<button onClick={() => store.enterVR()}>VR 진입</button>
<Canvas>
<XR store={store}>
<XROrigin />
{/* ...씬... */}
</XR>
</Canvas>
WebGPU + WebXR 조합은 2026년에 의외로 무겁지 않다 — Apple Vision Pro·Quest 3·Quest 3S 모두 WebGPU 기반 WebXR을 안정적으로 돌린다. 마케팅·교육·헬스케어 쪽에서 빠르게 채택 중.
10장 · Three.js vs Babylon.js vs PlayCanvas
엔진 비교는 짧게.
| 항목 | Three.js | Babylon.js | PlayCanvas |
|---|---|---|---|
| 라이선스 | MIT | Apache 2.0 | MIT (엔진) |
| 강점 | 거대한 생태계·예제·커뮤니티 | 게임 기능 풍부(피직스·오디오·머티리얼 에디터) | 비주얼 에디터·클라우드 IDE |
| 약점 | 게임용 기능은 직접 조립 | 생태계 작음 | 코드 우선은 약함 |
| WebGPU | r182 기본 권장 | Babylon 7+에서 안정 | 엔진 차원에서 지원 |
| 주간 다운로드(npm) | ~2.7M | ~10K | ~8K |
| 대표 영역 | 포트폴리오·제품·아트·시각화 | 브라우저 게임·시뮬레이션 | 광고·게임·구성 도구 |
한 줄 추천:
- 창작·아트·포트폴리오·제품 시각화 → Three.js (+ R3F).
- 게임형 인터랙션·물리 비중 큰 앱 → Babylon.js.
- 에디터로 시각적 협업이 중요 → PlayCanvas.
생태계 크기가 깡패다. 모르면 Three.js다.
11장 · Gaussian Splatting — 폴리곤 없는 사진실적 3D
여기서부터 새로운 페이지다.
Gaussian Splatting(이하 gsplat) 은 폴리곤 메시가 아니다. 씬을 수백만 개의 작은 3D 가우시안 점들(각자 위치·색·투명도·방향성을 가진 타원체)로 표현한다. 카메라가 그 점들을 화면에 "스플랫"(투영해서 펴)으로써 이미지를 만든다.
폴리곤 메시: Gaussian Splatting:
┌── 정점·면 데이터 ┌── 수백만 개 가우시안
├── UV·텍스처 │ (위치·SH 색·스케일·회전·α)
├── 노멀·머티리얼 ├── 텍스처 없음
└── 라이트로 셰이딩 └── 캡처 당시의 라이팅이 굽혀짐
왜 흥분되는가?
- 사진실적 — 30장~수백 장의 사진/영상에서 학습. 출력이 사진과 거의 같다.
- 실시간 — GPU 친화적. 웹에서 60fps 가능.
- 메시 모델링 0 — Blender도, UV도, 텍스처도, 노멀도 필요 없다. 카메라만 있으면 된다.
- NeRF의 후계 — NeRF가 학술적 마일스톤이었다면, gsplat은 실무적 도구다(빠른 학습·실시간 렌더링).
한계도 분명하다
- 라이팅이 "구워져" 있다 — 씬을 동적으로 비추기 어렵다.
- 충돌·물리 시뮬레이션이 어렵다 — 메시가 아니니까.
- 편집이 까다롭다 — SuperSplat 같은 전용 에디터가 필요.
- 파일이 크다 — 수십~수백 MB.
요는 — 존재하는 공간을 통째로 캡처해서 보여주는 것에는 무적이다. 부동산·문화재·박물관·콘서트·이벤트·전시.
2026년의 도구 풍경
- Polycam — 모바일 캡처의 시장 선도자. iOS LiDAR + 포토그래메트리 + gsplat. 평균 평점 4.7. iOS 리뷰 54만 개. 가장 쉬운 진입.
- Luma AI — 클라우드 처리로 시각 품질이 가장 좋다고 평가되는 무료 gsplat 플랫폼. 임베드 가능.
- SuperSplat — PlayCanvas 엔진 위에 만든 무료 오픈소스 브라우저 기반 gsplat 에디터. 라이브 어노테이션·핫스팟·후처리(블룸·비네트)·카메라 애니메이션·WebXR까지. HTML 뷰어로 내보내 GitHub Pages·Netlify·Vercel에 그대로 호스팅.
- NeRF Studio — 연구 지향. 로컬 학습·실험에 강함.
웹에 띄우기 — @mkkellogg/gaussian-splats-3d
Three.js 호환의 가벼운 gsplat 뷰어. R3F 환경에서:
import { GaussianSplats3D } from '@mkkellogg/gaussian-splats-3d'
import { useThree } from '@react-three/fiber'
import { useEffect } from 'react'
function Splat({ url }) {
const { scene, camera, gl } = useThree()
useEffect(() => {
const viewer = new GaussianSplats3D.Viewer({
threeScene: scene,
camera,
renderer: gl,
selfDrivenMode: false,
})
viewer.addSplatScene(url)
return () => viewer.dispose()
}, [url, scene, camera, gl])
return null
}
브라우저에서 수백만 가우시안을 60fps로 그린다. 5년 전에는 SF였다.
12장 · AI로 3D를 만든다 — Meshy · Tripo · Rodin
마지막 한 갈래. 텍스트나 사진에서 3D 메시를 뽑는 AI. 2026년에는 더 이상 실험이 아니라 워크플로의 일부다.
세 강자.
- Meshy 6 — 가장 균형 잡힌 제품. 텍스트→3D, 이미지→3D, PBR 텍스처, 토폴로지 제어, 폭넓은 익스포트. 생성 40~60초. "기본값 추천."
- Tripo AI — 가장 빠른 생성(20~30초). 기본값이 영리해서 초기 진입 마찰이 가장 낮다. 텍스트·이미지 둘 다.
- Rodin AI(Gen-2) — 100억 파라미터. 가장 높은 품질. 캐릭터·구조화된 자산에 강함. 생성 60~180초.
워크플로 예:
- 컨셉 단계 — Tripo에서 빠른 변형 30초.
- 마음에 드는 안 — Meshy에서 PBR 텍스처 깔끔하게 다시.
- 최종 캐릭터 — Rodin Gen-2로 고품질 메시.
- glTF로 내보내 Three.js/R3F 씬에 박는다.
Idea ─▶ Tripo (탐색)
└─▶ Meshy (정제·PBR)
└─▶ Rodin (피니시·캐릭터)
└─▶ glTF
└─▶ R3F 씬에 useGLTF
중요한 현실: AI 생성 메시는 토폴로지가 깔끔하지 않다. 포트폴리오·시각화·게임 백그라운드에는 충분하지만, 리깅·애니메이션이 중요한 캐릭터는 보통 Blender에서 리토폴로지가 필요하다.
13장 · "무엇으로 무엇을 만들까" — 유스케이스 매트릭스
| 유스케이스 | 추천 스택 | 비고 |
|---|---|---|
| 개발자 포트폴리오 | R3F + drei + Bloom | drei의 Float/Text 한 줌으로 충분 |
| 제품 컨피규레이터 | R3F + glTF + KTX2 | 색·텍스처 옵션은 머티리얼 교체 |
| 부동산 가상 투어 | gsplat (Luma·Polycam) + SuperSplat | 사진실적·실측 공간 |
| 박물관·전시 | gsplat + WebXR | 핫스팟·어노테이션 |
| 브라우저 게임 | Babylon.js 또는 Three.js + Rapier | 물리·충돌 필요 |
| 데이터 시각화 | R3F + 카메라 워크 | 인스턴싱 적극 활용 |
| AR 마케팅 | R3F + @react-three/xr + WebXR | iOS Quick Look 병행 |
| 인터랙티브 아트 | Three.js + TSL (셰이더 직접) | 노드 셰이더 자유도 |
| 캐릭터 중심 인터랙션 | R3F + AI 생성(Rodin) + Mixamo 리타게팅 | 토폴로지 정리 한 번 |
| LiDAR 캡처 자산 | Polycam → glTF or gsplat | 모바일만으로 종결 |
룰 오브 섬:
- "공간을 통째로 보여줘야 한다" → gsplat.
- "조작할 수 있어야 한다(색·옵션·물리)" → 폴리곤(glTF) + R3F.
- "조금 만들어 빨리 띄워야 한다" → R3F + AI 생성.
14장 · 포트폴리오 사이트 만들기 — 30분 레시피
가장 흔한 첫 프로젝트. 골격을 한 번에 본다.
import { Canvas } from '@react-three/fiber'
import { OrbitControls, Environment, Float, Text3D, useGLTF, ContactShadows } from '@react-three/drei'
import { EffectComposer, Bloom } from '@react-three/postprocessing'
import { Suspense } from 'react'
function Hero() {
const { scene } = useGLTF('/hero.glb')
return <primitive object={scene} scale={1.4} />
}
export default function Portfolio() {
return (
<Canvas camera={{ position: [0, 0, 6], fov: 50 }} dpr={[1, 2]}>
<color attach="background" args={['#0a0a0a']} />
<Suspense fallback={null}>
<Environment preset="studio" />
<Float speed={1.5} rotationIntensity={0.4} floatIntensity={0.8}>
<Hero />
</Float>
<ContactShadows position={[0, -1.6, 0]} opacity={0.6} blur={2.4} />
</Suspense>
<OrbitControls enableZoom={false} />
<EffectComposer>
<Bloom intensity={0.8} mipmapBlur />
</EffectComposer>
</Canvas>
)
}
체크리스트:
- 모델은 Meshy/Tripo로 뽑거나, sketchfab CC0에서 받아
gltf-transform optimize한 번. - 배경은 단색 + Environment HDR 한 장(스튜디오/시티).
- Float·ContactShadows·Bloom으로 "AI 생성스러움"을 가린다.
dpr={[1, 2]}로 레티나 폭주 막기.- 모바일에서는 Bloom을 끈다(미디어 쿼리 + 조건부 렌더).
에필로그 — 웹은 진짜로 3D가 되었다
2026년의 웹 3D는 더 이상 "한번 보고 마는 데모"의 자리가 아니다. 제품 페이지, 부동산, 박물관, 광고, 학습 도구가 일상적으로 3D를 깔고 있다. 그리고 그 일상의 도구는:
- Three.js + R3F — 폴리곤 기반 표준 스택.
- WebGPU + TSL — 셰이더 두 벌이 한 벌로.
- Gaussian Splatting — 카메라만 있으면 사진실적 공간.
- AI 3D 생성 — 텍스트 한 줄로 메시.
마지막으로 두 가지를 남긴다.
14개 항목 체크리스트
- WebGPURenderer를 기본으로 두고 WebGL 2 폴백을 확인했는가?
renderer.init()의 비동기 처리를 빠뜨리지 않았는가?- glTF가 Draco·KTX2로 압축됐는가?
- 인스턴싱이 필요한 대량 메시를 그냥 그리고 있지 않은가?
pixelRatio를 모바일에서 캡했는가?- 프러스텀 컬링을 끄지 않았는가?
- 같은 머티리얼/지오메트리를 중복 생성하고 있지 않은가?
- 포스트프로세싱 효과 수를 모바일에서 줄였는가?
- Suspense로 로딩 UX를 잡았는가?
- WebXR 진입은 사용자 제스처(클릭) 안에서 호출하는가?
- gsplat 자산은 압축 포맷(SPZ·KSPLAT)으로 내보냈는가?
- AI 생성 메시의 토폴로지를 (필요하면) 한 번 정리했는가?
setAnimationLoop한 곳에서만 도는가(중복 루프 없음)?- 첫 프레임 검정이 라이트 없음/init 누락은 아닌가?
안티패턴 10가지
- WebGPU 코드에
await renderer.init()을 빼고 첫 프레임이 검정. - 라이트 없이
MeshStandardMaterial을 쓰고 검정. - 같은 모델 인스턴스를 매 프레임 다시 로드.
frustumCulled = false를 그냥 켜둠.- JPG·PNG를 압축 없이 그대로 GPU에.
- 데스크톱·모바일 같은
dpr설정. - 포스트프로세싱 5개를 모바일에서도 돌림.
- AI 생성 메시를 토폴로지 정리 없이 캐릭터 리깅에 쓰기.
- gsplat을 폴리곤 워크플로(편집·물리)로 다루려 함.
useGLTF대신 매 컴포넌트에서GLTFLoader.load를 직접 호출.
다음 글 예고
다음 글 후보: WebGPU 컴퓨트 셰이더 실전 — GPGPU로 파티클 100만 개 돌리기, Gaussian Splatting 워크플로 — 캡처부터 웹 임베드까지, R3F + Rapier 물리 엔진 — 인터랙티브 3D 게임 한 시간 만에.
"웹은 진짜로 3D가 되었다. 폴리곤은 메시로, 사진은 가우시안으로, 텍스트는 AI로. 그 사이를 잇는 건 여전히 Three.js다."
— 웹의 3D 개발 2026, 끝.
참고 / References
- Three.js 공식 사이트
- Three.js WebGPURenderer 매뉴얼
- Three.js GitHub 릴리스 노트
- React Three Fiber 문서
- drei 헬퍼 라이브러리
- @react-three/postprocessing
- @react-three/xr
- WebGPU W3C 명세
- WGSL 명세
- TSL — Three Shading Language 가이드
- Babylon.js 공식
- PlayCanvas 공식
- SuperSplat — gsplat 에디터
- Polycam — 모바일 3D 캡처
- Luma AI
- NeRF Studio
- Meshy AI
- Tripo AI
- Rodin AI (Hyper3D)
- gltf-transform — 최적화 CLI
- Mkkellogg gaussian-splats-3d