3D 프론트엔드 — 스크롤 기반 3D 웹사이트
스크롤로 Three.js로 구축된 3D 씬을 탐험하는 몰입형 단일 파일 HTML 페이지를 만듭니다. 콘텐츠(제목, 텍스트, 이미지, CTA)는 3D 세계 위에 오버레이되거나 그 안에 내장됩니다. 모든 DreamWalk 페이지는 빌드 도구, npm, React 없이 자체 포함된 .html 아티팩트입니다.
기술 스택
모든 출력물은 단일 파일 HTML 아티팩트입니다. 빌드 도구, npm, React 없음 — 오직 자체 포함된 .html 파일입니다.
| 라이브러리 | CDN | 용도 |
|---|---|---|
| Three.js r128 | https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js | 3D 렌더링 엔진 |
| GSAP 3.12.5 | https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js | 애니메이션 엔진 |
| ScrollTrigger 3.12.5 | https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js | 스크롤-애니메이션 바인딩 |
이 스택을 사용하는 이유? Three.js는 가장 큰 생태계를 가진 지배적인 WebGL 프레임워크입니다. GSAP ScrollTrigger는 스크롤 위치를 애니메이션 타임라인에 매핑하는 업계 표준입니다. 이 조합은 빌드 단계 없이 단일 HTML 파일에서 작동하며, 아티팩트에 완벽합니다.
중요: Three.js r128 API 제약사항. 이는 최신 Three.js가 아닙니다. r128에서 다음은 존재하지 않으므로 절대 사용해서는 안 됩니다:
CapsuleGeometry,RoundedBoxGeometry,THREE.MathUtils.seededRandom,WebGPURenderer,THREE.ColorManagement. 항상BufferGeometry프리미티브(BoxBufferGeometry,SphereBufferGeometry등) 또는 해당 별칭(BoxGeometry,SphereGeometry)을 사용하세요.
아키텍처 패턴
┌─────────────────────────────────────────┐
│ HTML 페이지 │
│ ┌───────────────────────────────────┐ │
│ │ <canvas> - Three.js 씬 (고정) │ │
│ │ • 경로/스플라인 위의 카메라 │ │
│ │ • 3D 오브젝트(지오메트리/파티클) │ │
│ │ • 조명 및 안개 │ │
│ └───────────────────────────────────┘ │
│ ┌───────────────────────────────────┐ │
│ │ <div class="scroll-container"> │ │
│ │ height: 500vh+ (스크롤 가능) │ │
│ │ position:fixed 또는 스크롤 │ │
│ │ 트리거에 의해 오버레이되는 │ │
│ │ 텍스트/콘텐츠 섹션 │ │
│ └───────────────────────────────────┘ │
│ │
│ GSAP ScrollTrigger가 스크롤%를 → │
│ camera.position / camera.lookAt / │
│ object transforms / material params │
│ 에 매핑 │
└─────────────────────────────────────────┘
3D 스크롤 페이지 구축 방법
코드를 작성하기 전에 references/PATTERNS.md를 읽고 상세한 복사-붙여넣기 코드 패턴을 확인하세요. 여기에는 Three.js r128과 호환되는 카메라 애니메이션, 조명, 셰이더, 파티클, 절차적 지오메트리 및 성능 최적화를 위한 40개 이상의 프로덕션 준비 패턴이 포함되어 있습니다.
1단계: 씬 디자인
사용자 의도에 따라 경험 유형을 결정합니다:
| 유형 | 카메라 동작 | 최적 대상 |
|---|---|---|
| 터널 / 복도 | Z축을 따라 앞으로 비행 | 제품 공개, 스토리텔링 |
| 오빗 / 쇼케이스 | 중앙 객체 주위로 회전 | 제품 페이지, 포트폴리오 |
| 조경 비행 | 지형/도시 위를 스윕 | 랜딩 페이지, 인트로 |
| 방 둘러보기 | 연결된 공간을 이동 | 가상 투어, 프레젠테이션 |
| 수직 하강 | 카메라가 Y축 아래로 이동 | 타임라인, 레이어 공개 |
| 스플라인 경로 | 카메라가 곡선 3D 경로를 따름 | 시네마틱, 에디토리얼 |
2단계: HTML 스캐폴드
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Experience</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { overflow-x: hidden; }
canvas { position: fixed; top: 0; left: 0; z-index: 0; }
.scroll-container { position: relative; z-index: 1; pointer-events: none; }
.section { height: 100vh; display: flex; align-items: center; justify-content: center; }
.content { pointer-events: auto; /* 인터랙티브 요소를 위해 재활성화 */ }
</style>
</head>
<body>
<!-- Three.js 캔버스는 프로그래밍 방식으로 생성됩니다 -->
<div class="scroll-container">
<!-- 섹션은 스크롤 높이를 결정합니다. 콘텐츠는 3D 씬 위에 오버레이됩니다 -->
<div class="section" id="section-1">...</div>
<div class="section" id="section-2">...</div>
<!-- 섹션을 추가할수록 스크롤 거리가 늘어나고 카메라 여정이 길어집니다 -->
</div>
<script src="THREE_CDN"></script>
<script src="GSAP_CDN"></script>
<script src="SCROLLTRIGGER_CDN"></script>
<script>
// 씬 설정, 애니메이션, ScrollTrigger 바인딩
</script>
</body>
</html>
3단계: 핵심 Three.js 설정
항상 다음 기본 사항을 포함하세요:
- 씬 + 카메라 + 렌더러 — 몰입감을 위해 FOV 60-75의
PerspectiveCamera사용 - 조명 — 최소: 하나의
AmbientLight+ 하나의DirectionalLight. 드라마틱한 효과를 위해PointLight또는SpotLight추가 - 안개 —
scene.fog = new THREE.FogExp2(color, density)는 깊이를 더하고 갑작스러운 표시를 숨깁니다 - 리사이즈 핸들러 — 항상
window.resize처리 - 렌더 루프 —
requestAnimationFrame과renderer.render(scene, camera)
4단계: GSAP으로 스크롤 → 카메라 애니메이션
핵심 바인딩 패턴:
gsap.registerPlugin(ScrollTrigger);
// 카메라 애니메이션 대상을 일반 객체로 저장
const camAnim = { x: 0, y: 2, z: 10, lookX: 0, lookY: 0, lookZ: 0 };
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".scroll-container",
start: "top top",
end: "bottom bottom",
scrub: 1, // 1초 스무딩
}
});
// 카메라 키프레임 연결
tl.to(camAnim, { x: 0, y: 2, z: 5, lookX: 0, lookY: 0, lookZ: 0, duration: 1 })
.to(camAnim, { x: 3, y: 1, z: 0, lookX: 2, lookY: 0, lookZ: -5, duration: 1 })
.to(camAnim, { x: 0, y: 5, z: -10, lookX: 0, lookY: 0, lookZ: -15, duration: 1 });
// 렌더 루프에서 애니메이션된 값 적용
function animate() {
requestAnimationFrame(animate);
camera.position.set(camAnim.x, camAnim.y, camAnim.z);
camera.lookAt(camAnim.lookX, camAnim.lookY, camAnim.lookZ);
renderer.render(scene, camera);
}
5단계: 콘텐츠 오버레이
스크롤 위치에 따라 나타나거나 사라지는 HTML 콘텐츠 오버레이:
// 카메라가 웨이포인트에 도달하면 섹션 페이드 인
gsap.fromTo("#section-2 .content",
{ opacity: 0, y: 50 },
{ opacity: 1, y: 0,
scrollTrigger: {
trigger: "#section-2",
start: "top center",
end: "top 20%",
scrub: true
}
}
);
6단계: 시각적 개선
기본에서 인상적으로 업그레이드하려면 다음을 적용하세요:
- 파티클 — 떠다니는 먼지/별을 위한
THREE.BufferGeometry가 있는THREE.Points - 포스트 프로세싱 글로우 — 방출 재질 + 블룸과 유사한 밝은 색상 사용
- 색상 전환 — 스크롤에 따라
scene.background및scene.fog.color애니메이션 - 객체 공개 — 카메라가 접근함에 따라 객체가 0에서 확대되거나 페이드 인
- 시차 레이어 — 다른 거리의 객체가 다른 속도로 움직임
- 미묘한 회전 — 렌더 루프에서 장식용 객체를 천천히 회전(스크롤과 독립적)
- 마우스 시차 — 깊이감을 위해 마우스 위치 기반 약간의 카메라 오프셋
디자인 원칙
- 성능 우선 — 지오메트리를 단순하게 유지하세요.
BufferGeometry를 사용하고, 드로우 콜을 제한하며,fog를 사용하여 먼 객체를 숨깁니다. 60fps를 목표로 하세요 - 부드러운 스크롤링 — ScrollTrigger에서 부드러운 움직임을 위해
scrub: 1또는scrub: 2를 사용하세요.scrub: true(즉시, 뚝뚝 끊김)는 절대 사용하지 마세요 - 읽기 가능한 콘텐츠 — 텍스트 오버레이는 대비가 필요합니다. 밝은 텍스트 뒤에 반투명 어두운 패널을 사용하거나, 텍스트 영역 뒤에 3D 배경이 어둡거나 흐릿한지 확인하세요
- 모바일 대응 — 터치 스크롤은 ScrollTrigger에서 작동합니다.
window.innerWidth확인을 통해 모바일에서 파티클 수와 지오메트리 복잡성을 줄이는 것을 고려하세요 - 우아한 로딩 — 간단한 CSS 로딩 화면을 표시하고, Three.js 씬이 준비되면 숨깁니다
구축 가능한 일반적인 3D 객체 (외부 모델 불필요)
이들은 모두 Three.js 내장 지오메트리로 생성할 수 있습니다 — GLTF 파일이 필요 없습니다:
- 떠다니는 기하학적 모양 — MeshStandardMaterial이 있는 BoxGeometry, IcosahedronGeometry, TorusKnotGeometry
- 지형 — 정점 변위(사인파, 노이즈)가 있는 PlaneGeometry
- 파티클 필드 — 별, 먼지, 눈, 데이터 시각화를 위한 BufferGeometry + Points
- 그리드 바닥 — GridHelper 또는 사용자 정의 라인 지오메트리
- 튜브/터널 — CatmullRomCurve3 경로를 따르는 TubeGeometry
- 텍스트 — PlaneGeometry에 렌더링된 캔버스 텍스처 또는 스프라이트 기반
- 와이어프레임 객체 — 기술적인 미학을 위한
wireframe: true재질이 있는 모든 지오메트리 - 광선 — 방출형 투명 재질이 있는 CylinderGeometry
7단계: 마우스 시차 (선택 사항이지만 권장)
깊이 인식을 위해 마우스 이동 시 약간의 카메라 오프셋 추가:
const mouse = { x: 0, y: 0 };
document.addEventListener('mousemove', (e) => {
mouse.x = (e.clientX / window.innerWidth - 0.5) * 2; // -1 to 1
mouse.y = (e.clientY / window.innerHeight - 0.5) * 2;
});
// 렌더 루프에서 카메라에 약간의 오프셋 추가
const parallaxStrength = 0.3;
camera.position.x = camAnim.x + mouse.x * parallaxStrength;
camera.position.y = camAnim.y - mouse.y * parallaxStrength * 0.5;
방 둘러보기 패턴
건축/부동산 둘러보기의 경우, 단순한 지오메트리로 방을 만듭니다:
// 벽, 바닥, 천장을 위한 BoxGeometry로 방 생성
function createRoom(width, height, depth, position, color) {
const mat = new THREE.MeshStandardMaterial({ color, side: THREE.BackSide });
const room = new THREE.Mesh(new THREE.BoxGeometry(width, height, depth), mat);
room.position.copy(position);
scene.add(room);
return room;
}
// 단순한 지오메트리로 가구 자리 표시자 생성
// 소파 = 넓고 평평한 상자, 테이블 = 원통 위 얇은 상자, 램프 = 원통 + 구
// 따뜻한 실내 분위기를 위해 방 내부에 방출형 PointLights 사용
방 둘러보기의 핵심 원칙:
- 재질에
THREE.BackSide를 사용하여 벽이 내부에서 렌더링되도록 함 - 각 방 내부에 따뜻한
PointLight광원 배치(색상0xffeedd, 강도 0.8) - 카메라 경로는 벽을 뚫지 않고 문 개구부를 통해 들어가야 함
- 방에 들어갈 때 자연스럽게 각 방을 훑도록 camera lookAt 애니메이션
- 햇빛을 시뮬레이션하기 위해 방출형 밝은 재질의 직사각형 창문 추가
피해야 할 반패턴
- ❌
OrbitControls를 사용하지 마세요 — 스크롤이 유일한 내비게이션이어야 합니다 - ❌ 외부 GLTF/GLB 모델을 로드하지 마세요 — HTML 파일 내에 자체 포함되도록 유지하세요
- ❌
THREE.CapsuleGeometry를 사용하지 마세요 — r128에서 사용할 수 없습니다 - ❌ 캔버스에
pointer-events: none을 전역적으로 설정하지 마세요 — 렌더 루프가 캔버스를 필요로 합니다 - ❌
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))를 잊지 마세요 — 레티나에서 GPU 과부하를 방지합니다 - ❌ 카메라를
camera.rotation으로 직접 애니메이션하지 마세요 — 안정적인 결과를 위해 애니메이션된 대상 좌표로camera.lookAt()을 사용하세요 - ❌ 스크롤 컨테이너를 너무 짧게 만들지 마세요 — 의미 있는 여정을 위해 최소 400vh, 600vh+ 권장
미적 방향
frontend-design 스킬과 동일한 창의적 대담함을 적용하세요. 각 3D 스크롤 페이지는 독특하게 느껴져야 합니다:
- 색상 분위기 선택: 네온 사이버펑크, 따뜻한 일몰, 깊은 바다, 수정 같은 얼음, 방출 악센트가 있는 어두운 공허 등
- 지오메트리 언어 선택: 유기적 곡선 vs 날카로운 모서리, 희소 vs 빽빽함, 큰 모노리스 vs 작은 파티클
- 타이포그래피 오버레이: 독특한 Google Fonts 가져오기. 크고 시네마틱한 유형을 사용하세요. 스크롤에 따라 불투명도와 변형을 애니메이션하세요
- 3D 씬과 HTML 콘텐츠는 하나의 응집력 있는 경험처럼 느껴져야 하며, 텍스트가 얹혀진 3D 배경이 아니어야 합니다
빠른 참조: CDN URL
Three.js r128: https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js
GSAP 3.12.5: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js
ScrollTrigger: https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js