원클릭으로
react-threejs-game
// Three.js game development with React using @react-three/fiber and @react-three/drei — strict TypeScript, 60 fps, accessible
// Three.js game development with React using @react-three/fiber and @react-three/drei — strict TypeScript, 60 fps, accessible
AI-assisted development governance — Copilot custom agents, MCP servers, change control, audit trail — per Hack23 ISMS AI Policy
Clear technical documentation — JSDoc, Mermaid, READMEs, ADRs, C4 diagrams, ISMS policy citations
Hack23 ISMS alignment — ISO 27001:2022, NIST CSF 2.0, CIS Controls v8.1, GDPR, NIS2, EU CRA — with policy citations
React re-render optimization, Three.js rendering performance, useMemo/useCallback, bundle size, 60 fps profiling, Lighthouse budgets
Defense-in-depth security principles — OWASP Top 10 prevention, input validation, secure error handling, encryption, least privilege
Vitest + Cypress + RTL — deterministic tests, ≥80% line / ≥70% branch coverage, ≥95% on security code, Three.js component testing
| name | react-threejs-game |
| description | Three.js game development with React using @react-three/fiber and @react-three/drei — strict TypeScript, 60 fps, accessible |
| license | MIT |
Applies when building 3D scenes, implementing game loops, handling 3D interactions, optimizing Three.js rendering, loading assets, or managing game state with React.
@react-three/fiber JSX, not imperative Three.jsuseRef<THREE.Mesh>(null), typed props, typed event handlersuseFrame for the game loop — ((state, delta) => …) — delta time, not wall-clockuseStateOrbitControls, useTexture, Html, Sparkles, TrailonClick, onPointerOver on meshes — no manual raycastinguseState inside useFrame — use refs for transient animation stateprefers-reduced-motion, readable HUD contrastuseFrameimport { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
interface TargetProps {
position: readonly [number, number, number];
size: number;
onClick: () => void;
}
export function Target({ position, size, onClick }: TargetProps): JSX.Element {
const meshRef = useRef<THREE.Mesh>(null);
useFrame((state, delta) => {
const mesh = meshRef.current;
if (!mesh) return;
mesh.rotation.y += delta * 0.5;
mesh.position.y = position[1] + Math.sin(state.clock.elapsedTime) * 0.3;
});
return (
<mesh ref={meshRef} position={position} onClick={onClick}>
<sphereGeometry args={[size, 16, 16]} />
<meshStandardMaterial color="hotpink" />
</mesh>
);
}
import { useEffect, useMemo } from 'react';
import * as THREE from 'three';
function Ring(): JSX.Element {
const geometry = useMemo(() => new THREE.TorusGeometry(1, 0.2, 16, 64), []);
const material = useMemo(() => new THREE.MeshStandardMaterial({ color: 'cyan' }), []);
useEffect(() => {
return () => {
geometry.dispose();
material.dispose();
};
}, [geometry, material]);
return <mesh geometry={geometry} material={material} />;
}
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
useFrame((_, delta) => {
const speed = prefersReduced ? 0 : 0.5;
if (meshRef.current) meshRef.current.rotation.y += delta * speed;
});
// BAD: setInterval for animation
setInterval(() => { mesh.rotation.y += 0.01 }, 16);
// BAD: useState in useFrame (60 renders/sec)
useFrame(() => setPosition(p => p + 1));
// BAD: untyped ref
const meshRef = useRef(null);
// BAD: forgetting disposal
// Leaks geometry and material on unmount
// BAD: loading textures from user-supplied URLs without validation
useTexture(userInputUrl);
useRef<THREE.X>(null))useFrame + delta; no timersuseState inside useFrameInstancedMeshprefers-reduced-motion honored