웨이퍼맵 3D 구현 청사진 - <Wafermap3D/> 컴포넌트 아키텍처
📋 개요
이 문서는 단일 <Wafermap3D/> 컴포넌트로 모든 내부 파싱과 처리를 담당하는 3D 웨이퍼맵 시스템의 구현 청사진입니다.
상호작용(interaction)과 데이터 관리(data-management), 최적화는 우선 배제하고 핵심 렌더링 기능에 집중합니다.
🎯 구현 vs 라이브러리 구분
Three.js에서 제공하는 것 (가져다 쓰는 것)
🔧 기본 3D 엔진
// Three.js 핵심 클래스들 - 그대로 사용
import {
Scene, // 3D 장면 관리
PerspectiveCamera, // 카메라 시점
WebGLRenderer, // WebGL 렌더링
CylinderGeometry, // 웨이퍼 원통 형태
BoxGeometry, // Shot 사각형 형태
PlaneGeometry, // 기본 평면
ExtrudeGeometry, // Focus Spot 돌출 형태
InstancedMesh, // 다중 객체 성능 최적화
MeshStandardMaterial, // 기본 재질
Color, // 색상 처리
Vector3, // 3D 벡터 연산
Matrix4, // 변환 행렬
Raycaster, // 마우스 피킹
AmbientLight, // 환경 조명
DirectionalLight // 방향성 조명
} from 'three';
// Three.js 확장 기능들
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer';
🎨 React Three Fiber 생태계
// React Three Fiber - 그대로 사용
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { OrbitControls, Text, Html } from '@react-three/drei';
직접 구현해야 하는 것
📊 데이터 처리 레이어
// 1. 데이터 파싱 및 검증
interface WaferDataProcessor {
validateWaferData(data: RawWaferData): ValidationResult;
parseShots(shots: RawShot[]): ProcessedShot[];
parseFocusSpots(spots: RawFocusSpot[]): ProcessedFocusSpot[];
}
// 2. 좌표계 변환
interface CoordinateTransformer {
waferToWorld(waferCoord: WaferCoordinate): Vector3;
worldToWafer(worldCoord: Vector3): WaferCoordinate;
calculateShotPositions(shots: ProcessedShot[]): Vector3[];
}
// 3. 색상 매핑 시스템
interface ColorMapper {
generateColorSpectrum(steps: number): Color[];
mapValueToColor(value: number, range: ValueRange): Color;
createColorPalette(values: number[]): ColorPalette;
}
🎯 렌더링 로직
// 4. Shot 렌더링 시스템
interface ShotRenderingSystem {
createShotGeometry(size: number): BoxGeometry;
setupInstancedMesh(shotCount: number): InstancedMesh;
updateShotPositions(shots: ProcessedShot[]): void;
updateShotColors(shots: ProcessedShot[]): void;
}
// 5. Focus Spot 렌더링 시스템
interface FocusSpotRenderingSystem {
createPolygonGeometry(vertices: Vector2[]): ExtrudeGeometry;
createFocusSpotMaterial(type: FocusSpotType): Material;
renderFocusSpots(spots: ProcessedFocusSpot[]): Mesh[];
}
// 6. 웨이퍼 기하학 구조
interface WaferGeometrySystem {
createWaferCylinder(diameter: number, thickness: number): CylinderGeometry;
createNotch(position: NotchPosition): Geometry;
createGuideLines(): LineSegments;
}
🎮 컴포넌트 아키텍처
// 7. 상태 관리 (Zustand)
interface WaferStore {
data: WaferData | null;
mode: 'shot' | 'focusSpot';
selectedShots: Set<number>;
cameraView: CameraView;
// 액션들...
}
// 8. UI 컴포넌트들
interface UIComponents {
Toolbar3D: React.FC;
Legend: React.FC;
ModeSelector: React.FC;
ViewControls: React.FC;
}
🏗️ 컴포넌트 아키텍처
컴포넌트 구조도
<Wafermap3D>
├── 📊 DataProcessor # 데이터 파싱 및 검증
├── 🎨 Scene3DContainer # Three.js Scene 래퍼
│ ├── WaferGeometry # 웨이퍼 원통 + 노치
│ ├── ShotRenderer # Shot 모드 렌더링
│ ├── FocusSpotRenderer # Focus Spot 모드 렌더링
│ ├── LightingSystem # 조명 설정
│ └── CameraController # 카메라 제어
├── 🎮 UIOverlay # HTML/CSS UI 레이어
│ ├── Toolbar3D # 상단 툴바
│ ├── Legend # 우측 범례
│ └── StatusIndicator # 상태 표시
└── 🔧 SystemManager # 내부 시스템 관리
├── CoordinateSystem # 좌표 변환
├── ColorMappingSystem # 색상 매핑
└── StateManager # 상태 관리
메인 컴포넌트 인터페이스
interface Wafermap3DProps {
// 필수 데이터
data: WaferData;
// 기본 설정
initialMode?: 'shot' | 'focusSpot';
initialView?: 'top' | 'side' | 'iso';
// 스타일링
width?: number;
height?: number;
backgroundColor?: string;
// 이벤트 핸들러 (나중에 추가)
// onShotSelect?: (shot: Shot) => void;
// onFocusSpotSelect?: (spot: FocusSpot) => void;
}
const Wafermap3D: React.FC<Wafermap3DProps> = ({
data,
initialMode = 'shot',
initialView = 'top',
width = 800,
height = 600,
backgroundColor = '#f0f0f0'
}) => {
// 내부 구현...
};
📁 파일 구조
src/components/wafermap-3d/
├── Wafermap3D.tsx # 메인 컴포넌트
├── core/ # 핵심 시스템
│ ├── DataProcessor.ts # 데이터 처리
│ ├── CoordinateTransformer.ts # 좌표 변환
│ ├── ColorMapper.ts # 색상 매핑
│ └── GeometryFactory.ts # 지오메트리 생성
├── rendering/ # 렌더링 시스템
│ ├── Scene3DContainer.tsx # Scene 컨테이너
│ ├── WaferGeometry.tsx # 웨이퍼 지오메트리
│ ├── ShotRenderer.tsx # Shot 렌더링
│ ├── FocusSpotRenderer.tsx # Focus Spot 렌더링
│ ├── LightingSystem.tsx # 조명 시스템
│ └── CameraController.tsx # 카메라 제어
├── ui/ # UI 컴포넌트
│ ├── Toolbar3D.tsx # 툴바
│ ├── Legend.tsx # 범례
│ ├── ModeSelector.tsx # 모드 선택
│ └── ViewControls.tsx # 시점 제어
├── stores/ # 상태 관리
│ ├── waferStore.ts # 메인 상태
│ └── uiStore.ts # UI 상태
├── types/ # 타입 정의
│ ├── WaferData.ts # 데이터 타입
│ ├── RenderingTypes.ts # 렌더링 타입
│ └── UITypes.ts # UI 타입
└── utils/ # 유틸리티
├── constants.ts # 상수 정의
├── calculations.ts # 계산 함수
└── helpers.ts # 헬퍼 함수
🔄 데이터 플로우
1단계: 데이터 입력 및 검증
// Wafermap3D.tsx
const Wafermap3D: React.FC<Wafermap3DProps> = ({ data }) => {
const [processedData, setProcessedData] = useState<ProcessedWaferData | null>(null);
useEffect(() => {
const processor = new DataProcessor();
// 1. 데이터 검증
const validation = processor.validateWaferData(data);
if (!validation.isValid) {
console.error('Invalid wafer data:', validation.errors);
return;
}
// 2. 데이터 파싱
const processed = processor.processWaferData(data);
setProcessedData(processed);
}, [data]);
// ...
};
2단계: 좌표계 변환
// CoordinateTransformer.ts
export class CoordinateTransformer {
private scale = 0.01; // 1mm = 0.01 Three.js unit
waferToWorld(waferCoord: { x: number; y: number; z: number }): Vector3 {
return new Vector3(
waferCoord.x * this.scale, // X축 그대로
waferCoord.z * this.scale, // Z → Y (높이)
-waferCoord.y * this.scale // Y → -Z (깊이, 뒤집기)
);
}
calculateShotPositions(shots: ProcessedShot[]): Vector3[] {
return shots.map(shot => this.waferToWorld(shot.position));
}
}
3단계: 색상 매핑
// ColorMapper.ts
export class ColorMapper {
generateColorSpectrum(steps: number = 25): Color[] {
const colors: Color[] = [];
for (let i = 0; i < steps; i++) {
const hue = (240 - (i / (steps - 1)) * 240) / 360; // 파랑→빨강
const color = new Color().setHSL(hue, 1, 0.5);
colors.push(color);
}
return colors;
}
mapValueToColor(value: number, range: { min: number; max: number }): Color {
const normalizedValue = (value - range.min) / (range.max - range.min);
const colorIndex = Math.floor(normalizedValue * (this.colorSpectrum.length - 1));
return this.colorSpectrum[Math.max(0, Math.min(colorIndex, this.colorSpectrum.length - 1))];
}
}
4단계: 렌더링
// ShotRenderer.tsx
const ShotRenderer: React.FC<{ shots: ProcessedShot[] }> = ({ shots }) => {
const instancedMeshRef = useRef<InstancedMesh>(null);
useEffect(() => {
if (!instancedMeshRef.current) return;
const mesh = instancedMeshRef.current;
const transformer = new CoordinateTransformer();
const colorMapper = new ColorMapper();
// 위치 설정
shots.forEach((shot, index) => {
const position = transformer.waferToWorld(shot.position);
const matrix = new Matrix4().setPosition(position);
mesh.setMatrixAt(index, matrix);
});
// 색상 설정
const colorAttribute = mesh.geometry.getAttribute('instanceColor');
shots.forEach((shot, index) => {
const color = colorMapper.mapValueToColor(shot.value, shot.valueRange);
colorAttribute.setXYZ(index, color.r, color.g, color.b);
});
mesh.instanceMatrix.needsUpdate = true;
colorAttribute.needsUpdate = true;
}, [shots]);
return (
<instancedMesh
ref={instancedMeshRef}
args={[new BoxGeometry(1, 1, 0.1), new MeshStandardMaterial(), shots.length]}
>
<instancedBufferAttribute
attach="geometry-attributes-instanceColor"
args={[new Float32Array(shots.length * 3), 3]}
/>
</instancedMesh>
);
};
🎨 렌더링 시스템 상세
WaferGeometry 컴포넌트
// WaferGeometry.tsx
const WaferGeometry: React.FC<{ wafer: WaferSpec }> = ({ wafer }) => {
const waferMesh = useMemo(() => {
const geometry = new CylinderGeometry(
wafer.diameter / 2 * 0.01, // 상단 반지름
wafer.diameter / 2 * 0.01, // 하단 반지름
wafer.thickness * 0.01, // 높이
32 // 세그먼트
);
const material = new MeshStandardMaterial({
color: 0x888888,
transparent: true,
opacity: 0.3
});
return { geometry, material };
}, [wafer]);
return (
<mesh geometry={waferMesh.geometry} material={waferMesh.material}>
{/* 노치 추가 (나중에 구현) */}
</mesh>
);
};
Scene3DContainer 컴포넌트
// Scene3DContainer.tsx
const Scene3DContainer: React.FC<{ children: React.ReactNode }> = ({ children }) => {
return (
<Canvas
camera={{ position: [0, 5, 0], fov: 75 }}
style={{ width: '100%', height: '100%' }}
>
{/* 조명 설정 */}
<ambientLight intensity={0.4} />
<directionalLight position={[10, 10, 5]} intensity={0.8} />
{/* 카메라 컨트롤 */}
<OrbitControls enablePan={true} enableZoom={true} enableRotate={true} />
{/* 자식 컴포넌트들 */}
{children}
</Canvas>
);
};
🎮 상태 관리
Zustand Store 구조
// waferStore.ts
interface WaferState {
// 데이터
rawData: WaferData | null;
processedData: ProcessedWaferData | null;
// 모드
currentMode: 'shot' | 'focusSpot';
// 카메라
cameraView: 'top' | 'side' | 'iso';
// 액션들
setData: (data: WaferData) => void;
setMode: (mode: 'shot' | 'focusSpot') => void;
setCameraView: (view: 'top' | 'side' | 'iso') => void;
}
export const useWaferStore = create<WaferState>((set) => ({
rawData: null,
processedData: null,
currentMode: 'shot',
cameraView: 'top',
setData: (data) => set({ rawData: data }),
setMode: (mode) => set({ currentMode: mode }),
setCameraView: (view) => set({ cameraView: view }),
}));
🎯 메인 컴포넌트 구현
// Wafermap3D.tsx
const Wafermap3D: React.FC<Wafermap3DProps> = ({
data,
initialMode = 'shot',
initialView = 'top',
width = 800,
height = 600,
backgroundColor = '#f0f0f0'
}) => {
const [processedData, setProcessedData] = useState<ProcessedWaferData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
// 상태 관리
const { currentMode, cameraView, setMode, setCameraView } = useWaferStore();
// 데이터 처리
useEffect(() => {
const processData = async () => {
try {
setIsLoading(true);
setError(null);
const processor = new DataProcessor();
const transformer = new CoordinateTransformer();
const colorMapper = new ColorMapper();
// 1. 데이터 검증
const validation = processor.validateWaferData(data);
if (!validation.isValid) {
throw new Error(`Invalid data: ${validation.errors.join(', ')}`);
}
// 2. 데이터 파싱
const shots = processor.parseShots(data.shots);
const focusSpots = processor.parseFocusSpots(data.focusSpots);
// 3. 좌표 변환
const shotPositions = transformer.calculateShotPositions(shots);
const focusSpotGeometries = focusSpots.map(spot =>
transformer.createFocusSpotGeometry(spot)
);
// 4. 색상 매핑
const shotColors = shots.map(shot =>
colorMapper.mapValueToColor(shot.value, { min: 0, max: 100 })
);
setProcessedData({
wafer: data.wafer,
shots: shots.map((shot, index) => ({
...shot,
worldPosition: shotPositions[index],
color: shotColors[index]
})),
focusSpots: focusSpots.map((spot, index) => ({
...spot,
geometry: focusSpotGeometries[index]
}))
});
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setIsLoading(false);
}
};
processData();
}, [data]);
// 로딩 상태
if (isLoading) {
return (
<div style={{ width, height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div>Loading wafer data...</div>
</div>
);
}
// 에러 상태
if (error) {
return (
<div style={{ width, height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div style={{ color: 'red' }}>Error: {error}</div>
</div>
);
}
// 메인 렌더링
return (
<div style={{ width, height, position: 'relative', backgroundColor }}>
{/* 3D Scene */}
<Scene3DContainer>
{processedData && (
<>
{/* 웨이퍼 기하학 구조 */}
<WaferGeometry wafer={processedData.wafer} />
{/* 모드별 렌더링 */}
{currentMode === 'shot' && (
<ShotRenderer shots={processedData.shots} />
)}
{currentMode === 'focusSpot' && (
<FocusSpotRenderer spots={processedData.focusSpots} />
)}
</>
)}
</Scene3DContainer>
{/* UI 오버레이 */}
<UIOverlay>
<Toolbar3D
mode={currentMode}
onModeChange={setMode}
view={cameraView}
onViewChange={setCameraView}
/>
<Legend
colorSpectrum={processedData?.colorSpectrum}
valueRange={processedData?.valueRange}
/>
</UIOverlay>
</div>
);
};
export default Wafermap3D;
📊 타입 정의
// types/WaferData.ts
export interface WaferData {
wafer: {
id: string;
diameter: number; // mm
thickness: number; // mm
notchPosition: 'top' | 'bottom' | 'left' | 'right';
};
shots: RawShot[];
focusSpots: RawFocusSpot[];
}
export interface RawShot {
id: number;
x: number; // mm from center
y: number; // mm from center
z?: number; // mm height
value: number; // measurement value
isValid: boolean;
}
export interface ProcessedShot extends RawShot {
worldPosition: Vector3;
color: Color;
size: number;
}
export interface RawFocusSpot {
id: number;
type: 'focus' | 'chuck';
vertices: { x: number; y: number }[];
height: number;
metadata?: Record<string, any>;
}
export interface ProcessedFocusSpot extends RawFocusSpot {
geometry: ExtrudeGeometry;
material: Material;
}
🎉 결론
이 청사진은 <Wafermap3D/> 컴포넌트가 모든 내부 처리를 담당하면서도 명확하게 구조화된 아키텍처를 제공합니다:
✅ 구현해야 할 것
- 데이터 파싱 및 검증 로직
- 좌표계 변환 시스템
- 색상 매핑 알고리즘
- Shot/Focus Spot 렌더링 로직
- 상태 관리 시스템
- UI 컴포넌트들
✅ Three.js에서 가져다 쓸 것
- 기본 3D 엔진 (Scene, Camera, Renderer)
- 지오메트리 클래스들 (Cylinder, Box, Extrude)
- 재질 시스템 (Material)
- 수학 유틸리티 (Vector3, Matrix4, Color)
- 컨트롤 시스템 (OrbitControls)
이 구조를 통해 단일 컴포넌트로 완전한 3D 웨이퍼맵 시스템을 구현할 수 있습니다.