2026. 2. 2. 22:21ใProjects/WooniePangee

๋ธ๋ ๋๋ก 3D ์ค๋ธ์ ํธ ๋ง๋ค๊ณ Three.js๋ก ๋ฐ์ํ์ ๊ณ ๋ คํ ์น ๋ ๋๋ง ํ๊ธฐ
๋ชฉ์ฐจ
1. ์น ๋ฉ์ธ ํ์ด์ง์ ์ปค์๋ฅผ ์ถ์ ํ๋ 3D ์ค๋ธ์ ํธ ๋ฃ๊ธฐ
2. ์ค๋น๋ฌผ: Three.js์ 3D ์ค๋ธ์ ํธ(๋ฅผ ์ง์ ๋ง๋ค์ด ๋ดค๋ค)
3. blender๋ก 3D ์ค๋ธ์ ํธ ๋ง๋ค๊ธฐ
4. Three.js๋ก ์นํ์ด์ง์ ๊ฐ์ฒด ๋ฃ๊ธฐ
1. ์น ๋ฉ์ธ ํ์ด์ง์ ์ปค์๋ฅผ ์ถ์ ํ๋ 3D ์ค๋ธ์ ํธ ๋ฃ๊ธฐ
์ผ๋จ ๊ธฐ์กด ํผ๊ทธ๋ง๋ก ๋์ถฉ ๋์์ธํ ๋ฉ์ธ ํ๋ฉด์ ์๋์ ๊ฐ์๋ค.

์ด๋ ๊ฒ ๊ทธ๋ฅ ์ญ ์๋๋ก ์คํฌ๋กคํ๋ ๋ฉ์ธ ํ๋ฉด์ด๋ค.
๊ทธ๋ฐ๋ฐ ๋ฆฌํฉํ ๋ง์ผ๋ก ๋จ๊ฒจ๋๋ คํ์ง๋ง ๋ฉ์ธ ์์ ์ ์์ํ๋ ค๋ ๋๋ฌด ๋ชป์๊ฒจ์ ํ๊ธฐ๊ฐ ์ซ์ ๊ฑฐ๋ค.
๊ทธ๋์ ์ผ๋จ ๊ณ ๋ ค๋ง ํ๋ Three.js๋ฅผ ๋ฐ๋ก ํฌ์ ํด๋ณด๊ธฐ๋ก ํ๋ค. ์ด์ฐจํผ ๊ฐ์์๊ฒ ๋ ๋ฉ์ธ์ด๋ผ๋ฉด.. ์คํธ๋ ์ค ๋ฐ์ง ๋ง๊ณ ๊ฐ์์๊ธฐ๋ก
2. ์ค๋น๋ฌผ: Three.js์ 3D ์ค๋ธ์ ํธ(๋ฅผ ์ง์ ๋ง๋ค์ด ๋ดค๋ค)
1. Three.js
https://threejs.org/manual/#ko/fundamentals
Three.js๋ ์นํ์ด์ง์ 3D ๊ฐ์ฒด๋ฅผ ์ฝ๊ฒ ๋ ๋๋งํ๋๋ก ๋์์ฃผ๋ JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. WebGL์ ์ฌ์ฉํ๋ค๊ณ ํ๋ค.
์ฝ๋ ๋ณด๋๋ฐ OpenGL ์์ ๋ค์๋ ๊ฒ ๋ ์ฌ๋ผ์ ์กฐ๊ธ ์ต์ํ ๋๋์ด์๋ค.
docs์ ํ๊ตญ์ด๋ ์๊ณ ์์์ ํจ๊ป ์ค๋ช ์ด ์ ๋ง ์ ๋์ด์๋ ๊ฒ ๊ฐ๋ค.
์๊ฐ์ด ์์ด์ ์์ธํ ์ฝ์ด๋ณด๋ฉด์ ํ์ง๋ ๋ชปํ์ง๋ง ๋์ค์ ์ฐฌ์ฐฌํ ์ฝ์ด๋ณด๋ฉด (ํนํ ํ) ์ข์ ๊ฒ ๊ฐ๋ค.
2. 3D ์ค๋ธ์ ํธ
์นํ์ด์ง์ 3D ๊ฐ์ฒด๋ฅผ ๋ ๋๋งํ๋ ค๋ฉด ์๋ฌด๋๋ 3D ๊ฐ์ฒด๊ฐ ์ฐ์ ํ์ํ๋ค.
์ฌ์ค 3D ๊ฐ์ฒด์ผ ai๋ก ๋๋ฑ ๋ง๋ค์ด์ ์ฐ๋ฉด ๋๊ฒ ์ง ์๊ฐํ์ผ๋..
https://hyper3d.ai/?lang=ko&gad_campaignid=23436059038&gbraid=0AAAABCNk7GYRlaWIKdh023Dat0yJDxD2t
์ํ๊น๊ฒ๋ 3D ๋ชจ๋ธ์ ๊ทธ๋ฆฌ๋ ai๋ค์ ๊ตฌ๋ ์ ํด์ผ ๋ค์ด ๋ฐ๊ฑฐ๋, ์๋๋ฉด ์ ๋ง ์ด์ํ๊ฒ ์๊ธด ์ค๋ธ์ ํธ๋ฅผ ๋ค์ด๋ฐ์ ์ ์๊ฒ ํด์ฃผ๋ ๊ฒ๋ง ์์๋ค. ๊ฒ๋ค๊ฐ ์ ๋ง๋ ๊ฒ๋ ๋ด ๋ง์์ ์ ๋ค์ง๋ ์์๋ค. ๋ด๊ฐ ๊ทธ๋ฆฐ ์บ๋ฆญํฐ์ฌ์ ์ข ๋ง์์ ์๋ค๊ฒ ์๊ธด ๊ฑธ ๋์ด๊ฐ ์๊ฐ ์๋ ๊ฒ์ด์๋ค.
๊ทธ๋์ ๋ธ๋ ๋๋ก ์ง์ ๊ฐ์ฒด๊น์ง ๋ง๋ค์๋ค.. ๊ทผ๋ฐ ์ meshy๋ ์น๊ตฌ ์ด๋ํ๋ฉด ํ๋ฌ๋์ ํ๋ก ํ๋ ๋ฌด๋ฃ ์ฌ์ฉ ๊ฐ๋ฅํ๋๊น ์ด๋ฐ ๊ณ ์ง์ด ์๋ค๋ฉด ai ์ฌ์ฉํ๋ ๊ฒ ํจ์ฌ ๋ซ๋ค.
๋ ธ๋ฒ ์ด์ค๋ก 3D ๊ฐ์ฒด ๋ง๋ค๊ธฐ ์ฝ์ง ์๋ค.
3. blender๋ก 3D ์ค๋ธ์ ํธ ๋ง๋ค๊ธฐ
์ด๊ฑด ๊ฐ๋ฐ๊ณผ ์๊ด ์๋ ์๊ธฐ๋ค. ์คํ๋ ค ์ด๊ฑฐ ํ๋๋ฐ ์๊ฐ ๋๋ฌด ๋ง์ด ์จ์ ์ค๋ ํด์ผํ ๋ค๋ฅธ ์ผ๋ค์ ๋ชปํ๋ค.
ํ์ง๋ง ๋๋ฆ ๋ง์กฑ์ค๋ฌ์ด ๊ฒฐ๊ณผ๋ฅผ ์ป์ ๊ฒ ๊ฐ๋ค.
๋ด๊ฐ ์๊ธฐ๋ก 3D ๋ ๋๋ง ๋๊ตฌ๋ก ๋ํ์ ์ธ ๊ฒ์ด ๋ง์ผ์ ๋ธ๋ ๋์ธ๋ฐ, ๋ง์ผ๊ฐ ์ ๋ฌธ๊ฐ ๋๋์ด ์ข ๋ ์๋์ ์ ์ ๋ธ๋ ๋๋ฅผ ํ ๋ฒ ์จ๋ณธ ์ ์ด ์์๋ค. ๊ทธ ๋ ๊ธ๋ฐฉ ํ๋ ๋ง๋ค์ด ๋ณธ ๊ฒ ์์ด์ ๊ธ๋ฐฉ ํ๊ฒ ๊ฑฐ๋ํ๊ณ ๋ง๋ค์ด๋ณด๋ค๊ฐ ํ๋ฅผ ๋ดค๋ค.


https://www.youtube.com/watch?v=RedV1S8ssDA&list=PLAosp_j4QfQfErnJbpZ1PcHw1Z74k_Xfd&index=3
์ ํ๋ธ์ ๋ธ๋ ๋ ๊ฒ์ํ๋ค๊ฐ ์ฐ๋๋ ์ข ๋น์ทํ๊ฒ ์๊ธด ๊ฒ ๊ฐ์์ ์ด๊ฑฐ ๋ณด๋ฉด์ ํ๋ค. ๋ง์ฐ์ค/ํค๋ณด๋ ๋๋ฅด๋ ๊ฒ๋ ๊ฐ์ด ๋ ์ ๋ ๋ณด๊ธฐ ๊ด์ฐฎ์๋ ๊ฒ ๊ฐ๋ค.


์์ ๋งค๋๋งค๋ํ๊ฒ ํ ์๋ ์๊ธดํ๋ฐ ๊ฐ์ง ๊ฒ ๋ญ๊ฐ ์ข ๋ 3D ๋๋ ๋๋ ๊ฒ ๊ฐ์์ ์ผ๋จ ๊ฐ์ง ๊ฑธ๋ก ๋ฃ์ด๋ดค๋ค. ๊ทธ๋ฆฌ๊ณ ์ spheer ์๋จธ๋ฆฌ๊ฐ ์ข ๊ทธ๋ฐ๊ฐ ์ถ์ด์.. ๋์ค์ ๋ค๋ฅธ ์ฌ๋๋คํํ ๋ ๋ฌผ์ด๋ด์ผ์ง..
4. Three.js๋ก ์นํ์ด์ง์ ๊ฐ์ฒด ๋ฃ๊ธฐ
6-7์๊ฐ๋ง์ ์ป์ .glb ํ์ผ์ ์ด์ ์นํ์ด์ง์ ๊ทธ๋ฆฌ๊ธฐ๋ง ํ๋ฉด ๋๋ค.
- ๋ธ๋ผ์ฐ์ ์ ์ค๋ธ์ ํธ ๋ ๋๋งํ๊ธฐ
- ๋ง์ฐ์ค/ํฐ์น์ ๋ฐ๋ผ ์ค๋ธ์ ํธ ํ์ ์ํค๊ธฐ
ํ ์ผ์ ํฌ๊ฒ ์ด๋ ๊ฒ ๋๊ฐ์ง์ด๋ค. ์ฌ๊ธฐ์ ์ ๊ฒฝ์ธ ์ ์ ์๋ ์ ๋๊ฐ ๋ ๊ฒ์ด๋ค.
- ๊ทธ๋ฅ ๊ทธ๋ฆฌ๋ฉด ์ฐฝ ํฌ๊ธฐ๊ฐ ๋ณํ ๋ ๋ ๋๋ง ๊ณต๊ฐ ๋ฐ ์ค๋ธ์ ํธ ํฌ๊ธฐ๊ฐ ๊ทธ๋๋ก์ด๊ธฐ ๋๋ฌธ์ innerWidth์ ๋ฐ๋ผ ์ค์๊ฐ ๋ฐ์์ด ํ์
- ๋ง์ฐ์ค์ ์์ ์ ์ ๊ฝ๋๋ก ์กฐ์
- ๋ ๋๋ง ๋ฃจํ ๊ด๋ฆฌ ๋ฐ ์ ๋ฆฌ
1. import
typescript๋ก ์งํํ๋ค. ์ฒ์์๋ THREE๋ง ์ฌ์ฉํ๋ ค๊ณ ํ๋๋ฐ ํ์ ๋ฌธ์ ์ธ์ง ๋ฒ์ ๋ฌธ์ ์ธ์ง ์๋ฌ๊ฐ ์๊พธ ๋์ GLTFLoader, GLTF๋ three-stdlib์ ์ถ๊ฐ๋ก ์ค์นํ๋ค.
'use client';
import { useEffect, useRef } from 'react';
import * as THREE from 'three';
import { GLTFLoader } from 'three-stdlib';
import type { GLTF } from 'three-stdlib';
2. ref ์ฐธ์กฐ
export default function ThreeHead() {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
// ref ๊ฐ์ ๋ณ์์ ์ ์ฅ
const container = containerRef.current;
useRef๋ก ๋ ๋๋งํ div ์์๋ฅผ ์ฐธ์กฐํ๊ณ container์ ์ ์ฅํ๋ค.
๋์ค์ ํด๋ฆฐ์ ํ ๋ ๋ฐ๋ก containerRef.current๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฏธ ๋ณ๊ฒฝ๋์์ ๊ฐ๋ฅ์ฑ์ผ๋ก ๋ฆฐํธ ๊ฒฝ๊ณ ๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ container ๋ณ์๋ฅผ ์ ์ธํด์ ๋ด์๋ค.
- useEffect ์คํ ์์ : containerRef.current๋ DOM ์์๋ฅผ ๊ฐ๋ฆฌํด
- ํด๋ฆฐ์ (์ธ๋ง์ดํธ) ๊ณผ์ ์ค React๊ฐ ref๋ฅผ ๋จผ์ null๋ก ๋ง๋ ๋ค์ useEffect cleanup์ด ์คํ๋ ์ ์์
3. Scene, Camera, Renderer, Light ์์ฑ
// --- ๋์ ์ฌ์ด์ฆ ---
const sizes = {
width: window.innerWidth,
height: window.innerHeight,
};
// --- ์ฌ / ์นด๋ฉ๋ผ / ๋ ๋๋ฌ ---
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, sizes.width / sizes.height, 0.1, 100);
camera.position.set(0.5, 0, 4);
const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
containerRef.current.appendChild(renderer.domElement);
// --- ์กฐ๋ช
---
scene.add(new THREE.AmbientLight(0xffffff, 2));
const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
dirLight.position.set(0, 5, 5);
scene.add(dirLight);
์ฐ์ ์๋์ฐ ํฌ๊ธฐ์ ๋ฐ๋ผ ๋์ ์ฌ์ด์ฆ๋ฅผ ์ฃผ๊ธฐ ์ํ sizes๋ฅผ ์ ์ธํ๊ณ ,
3D ์ค๋ธ์ ํธ๋ฅผ ๊ทธ๋ฆด ๊ณต๊ฐ Scene, ์ค๋ธ์ ํธ๋ฅผ ๋ณด๋ ์์ ์ธ Camera, 3D๋ฅผ ๊ทธ๋ฆฌ๋ Renderer, ์ค๋ธ์ ํธ๋ฅผ ๋น์ถ Light๋ฅผ ์์ฑํ์.
- THREE.Scene : ๊ณต๊ฐ์ ์๋ก ์์ฑํ๋ค
- HTREE.PerspectiveCamera : Camera๋ฅผ ์์ฑํ๋ค.
45 : ์นด๋ฉ๋ผ ๊ฐ๋(degree)
0.1 ~ 100 : ์ดฌ์์ด ๊ฐ๋ฅํ ๊ณต๊ฐ์ผ๋ก, ์ด ๋ฐ์ ์ค๋ธ์ ํธ๊ฐ ์์ผ๋ฉด ์นด๋ฉ๋ผ์ ์์ฐํ๋ค.
camera.position : ์ผ์ชฝ, ์์๋, ์ค๋ฅธ์ชฝ์ ๋ํ ์นด๋ฉ๋ผ ์์น - THREE.WebGLRenderer: Renderer๋ฅผ ์์ฑํ๋ค.
alpha: true → ๋ฐฐ๊ฒฝ ํฌ๋ช ํ๊ฒ
antialias : ๊ฒฝ๊ณ์ ๋ถ๋๋ฝ๊ฒ - THREE.AmbientLight: ๊ธฐ๋ณธ์กฐ๋ช
์ ์ธํ
ํ๋ค.
THREE.DirectionalLight: ์ด๊ฒ ์์ผ๋ฉด ๋ชจ๋ธ์ด 3D์ธ๋ฐ 3D๊ฐ ์๋ ๋๋์ด ๋๋ค.



4. GLTF ๋ชจ๋ธ ๋ก๋
// --- GLTF ๋ก๋ ---
const loader = new GLTFLoader();
let head: InstanceType<typeof THREE.Object3D>;
let baseScale = 1; // ๊ธฐ๋ณธ ์ค์ผ์ผ ์ ์ฅ
// ํ๋ฉด ํฌ๊ธฐ์ ๋ฐ๋ฅธ ์ค์ผ์ผ ๊ณ์ฐ ํจ์
const calculateScale = () => {
// ๊ธฐ์ค: 1920px ํ๋ฉด์์ scale 1
const scaleMultiplier = Math.min(sizes.width / 1920, 1.5); // ์ต๋ 1.5๋ฐฐ๊น์ง
return baseScale * scaleMultiplier;
};
loader.load('/images/head.glb', (gltf: GLTF) => {
const model = gltf.scene;
const group = new THREE.Group();
group.add(model);
const box = new THREE.Box3().setFromObject(model);
const center = new THREE.Vector3();
box.getCenter(center);
model.position.sub(center);
const size = new THREE.Vector3();
box.getSize(size);
const maxDim = Math.max(size.x, size.y, size.z);
baseScale = 2 / maxDim; // ๊ธฐ๋ณธ ์ค์ผ์ผ ์ ์ฅ
const currentScale = calculateScale();
group.scale.set(currentScale, currentScale, currentScale);
group.rotation.y = Math.PI;
scene.add(group);
head = group;
camera.lookAt(group.position);
});
ํ๊ฒฝ ์ธํ ์ด ๋๋๋ฉด ์ด์ ๋ชจ๋ธ์ ๋ก๋ํ ์ฐจ๋ก์ด๋ค.
GLTFLoader๋ก ์ค๋ธ์ ํธ๋ฅผ ๋ ๋๋งํ ์ ์๋ loader๋ฅผ ์ ์ธํ๊ณ , calculateScalesํจ์๋ฅผ ์ ์ํด์ ํ๋ฉด ํฌ๊ธฐ์ ๋ฐ๋ผ ์ค๋ธ์ ํธ ํฌ๊ธฐ๊ฐ ๋ฌ๋ผ์ง๋๋ก ํ์๋ค.
loader.load('/imaes/head.glb')์ ๊ฐ์ด 3D ์ด๋ฏธ์ง ํ์ผ์ ์ฃผ๊ณ ์์์ ์ธํ ์ ํ๋ฉด ๋๋ค.
์ฒ์์ ์ค๋ธ์ ํธ์ 0,0,0์ด ์ค์ฌ์ถ์ด ๋๋ ๋๋์ผ๋ก ํ์ ์ ํด์ ํ์ ์ ์ค์ฌ์ถ์ด ์ค๋ธ์ ํธ ๋ด๋ถ์ ์ ์ค์์ด ๋๋๋ก ํฌ์ง์ ๋์ ํ๋ค.
๊ทธ ์๋๋ ์ค์ผ์ผ ๊ณ์ฐ์ผ๋ก, ๋ง์ฝ ํฌ๊ธฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์๋๋ค๋ฉด ์ ๋งํผ์ด ํ์๊ฐ ์๋ค.
5. ๋ง์ฐ์ค/ํฐ์น ์ด๋ฒคํธ
// --- ๋ง์ฐ์ค/ํฐ์น ์์น ---
const mouse = { x: 0, y: 0 };
// ์์น ์
๋ฐ์ดํธ ๊ณตํต ํจ์
const updateMousePosition = (clientX: number, clientY: number) => {
mouse.x = (clientX / sizes.width) * 2 - 0.8;
mouse.y = -(clientY / sizes.height) * 2 + 1;
};
// ๋ง์ฐ์ค ์ด๋ฒคํธ
const onMouseMove = (e: MouseEvent) => {
updateMousePosition(e.clientX, e.clientY);
};
// ํฐ์น ์ด๋ฒคํธ
const onTouchMove = (e: TouchEvent) => {
if (e.touches.length > 0) {
const touch = e.touches[0];
updateMousePosition(touch.clientX, touch.clientY);
}
};
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('touchmove', onTouchMove, { passive: true });
์ฒ์์ ๋ง์ฐ์ค๋ง ๋ง๋ค์๋ค๊ฐ ์์ฐจ์ฐจํ๊ณ ํฐ์น ์ด๋ฒคํธ๋ฅผ ๊ธํ๊ฒ ๋ฃ์๋ค.
ํ๋ฉด ์ขํ๋ฅผ 3D ํ์ ์ ์ ํฉํ ์ ๊ทํ ๊ฐ์ผ๋ก ๋ณํํ๊ธฐ ์ํด์ ๊ณ์ฐ์ด ์กฐ๊ธ ๋ค์ด๊ฐ๋ค.
updateMousePosition์ ๋ค์ด๊ฐ ์์์ ๊ฒฝ์ฐ, ์๋์ ๊ฐ๋ค.
(clientX / sizes.width) * 2 - 0.8 โถ๏ธโถ๏ธ
ํ๋ฉด ๋ด ์๋์ ์์น ๋น์จ * ์ค์ฌ ๊ธฐ์ค์ผ๋ก ํ์ฅ - ๋ชจ๋ธ ์์ ์ฒ๋ฆฌ(UX) ๋ณด์ ์ฉ ์์
y๋ ๋ธ๋ผ์ฐ์ ๋ ์๋๋ก ๊ฐ์๋ก y๊ฐ ์ฆ๊ฐํ๋, Three.js ํ์ ์ ์๋ก ๊ฐ์๋ก ์ฆ๊ฐํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ ๋ถํธ๊ฐ ๋๋ค.
6. ๋ฆฌ์ฌ์ด์ฆ ๋์
// --- ๋ฆฌ์ฌ์ด์ฆ ํธ๋ค๋ฌ ---
const handleResize = () => {
// sizes ๊ฐ์ฒด ์
๋ฐ์ดํธ
sizes.width = window.innerWidth;
sizes.height = window.innerHeight;
// ์นด๋ฉ๋ผ ์
๋ฐ์ดํธ
camera.aspect = sizes.width / sizes.height;
camera.updateProjectionMatrix();
// ๋ ๋๋ฌ ์
๋ฐ์ดํธ
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// ์ค๋ธ์ ํธ ํฌ๊ธฐ ์
๋ฐ์ดํธ
if (head) {
const newScale = calculateScale();
head.scale.set(newScale, newScale, newScale);
}
};
window.addEventListener('resize', handleResize);
์๋์ฐ ํฌ๊ธฐ๊ฐ ๋ณํ๋ฉด sizes๋ฅผ ๋จผ์ ์ ๋ฐ์ดํธํ์ฌ ์นด๋ฉ๋ผ, ๋ ๋๋ฌ, ์ค๋ธ์ ํธ ํฌ๊ธฐ๊ฐ ๋ชจ๋ ์ ๋ฐ์ดํธ ๋๋ค.
7. ์ ๋๋ฉ์ด์ ๋ฃจํ
// --- ์ ๋๋ฉ์ด์
๋ฃจํ ---
const animate = () => {
requestAnimationFrame(animate);
if (head) {
head.rotation.y = Math.PI + mouse.x * 1;
head.rotation.x = -mouse.y * 1;
}
renderer.render(scene, camera);
};
animate();
requestAnimationFrame์ผ๋ก ๋งค ํ๋ ์๋ง๋ค ์ ๋๋ฉ์ด์ ์ ๊ฐฑ์ ํ๋ค.
๋ ๋๋งํ ๊ฐ์ฒด head์ ๋ง์ฐ์ค๋ฅผ ๋ฐ๋ผ์ x์ถ, y์ถ rotation์ ์ฃผ๊ณ , ํด๋นํ๋ scene๊ณผ camera ๋ ๋ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ฆฐ๋ค.
8. ํด๋ฆฐ์
// --- ํด๋ฆฐ์
---
return () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('touchmove', onTouchMove);
window.removeEventListener('resize', handleResize);
// ์ ์ฅ๋ ๋ณ์ ์ฌ์ฉ
container.removeChild(renderer.domElement);
renderer.dispose();
};
}, []);
์์์ ์ ์ธํ ์ด๋ฒคํธ๊ฐ ๋ง์๊ธฐ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง๋ฅผ ์ํด์ ์ปดํฌ๋ํธ ์ธ๋ง์ดํธ ์ ์ด๋ฒคํธ ์ ๊ฑฐ ๋ฐ ๋ ๋๋ฌ ํด์ ๋ฅผ ๋๋ค.
9. ๋ฆฌํด
return (
<div
ref={containerRef}
className="bg-blue-50 bg-[linear-gradient(to_right,#e5e7eb_1px,transparent_1px),linear-gradient(to_bottom,#e5e7eb_1px,transparent_1px)] bg-size-[20px_20px]"
/>
);
์ฌ๊ธฐ์ div๋ ๋ ๋๋ง ์บ๋ฒ์ค ์ญํ ์ ํ๋ค. ๋ฐฐ๊ฒฝ์ ๊ทธ๋ฅ ๋ฃ์ด๋ดค๋ค.
์ฐธ๊ณ
https://www.youtube.com/watch?v=RedV1S8ssDA&list=PLAosp_j4QfQfErnJbpZ1PcHw1Z74k_Xfd&index=3
https://savinglectures123.tistory.com/81
[threejs-journey 1-7] Resizing & FullScreen
๊ฐ์Three.js๋ฅผ ์ด์ฉํด ์น์์ 3D Scene์ ๊ตฌํํ ๋๋ ๋ธ๋ผ์ฐ์ ์ ํฌ๊ธฐ ๋ณํ๋ ๋์คํ๋ ์ด ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค์ํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค. ๋ณธ ๋ฌธ์์์๋ ์ฐฝ ๋ฆฌ์ฌ์ด์ง ์ ๋ฐ์ํ๋ ํ๋ฉด ์ฐ๊ทธ๋ฌ์ง
savinglectures123.tistory.com
'Projects > WooniePangee' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| ์ด๋ฏธ์ง ๋ค์ด ๋งํฌ ๋ฐ์ดํฐ ๋ฃ๊ธฐ (feat. cloudflare) (0) | 2026.02.22 |
|---|---|
| ESLint + Prettier์์ Delete cr ์๋ฌ๊ฐ ๋๋ ์ด์ (0) | 2026.01.28 |
| Github Acitons CI ์ธํ ํ๊ธฐ (0) | 2026.01.22 |
| ํ๋ก์ ํธ ์์ : ๋ง์ค์ฝํธ ์๊ฐ ์นํ์ด์ง MVP (1) | 2026.01.22 |
GitHub