[트래블슈팅]3D 모델 렌더링에서 발생한 타이밍 이슈

박두팔이·2025년 8월 5일
0

3D 모델 렌더링에서 발생한 타이밍 이슈 트러블슈팅

🚨 문제 정의

Vue.js + VTK.js를 사용한 3D 치과 모델 렌더링 시스템에서, 모델이 정렬되지 않은 상태로 먼저 화면에 표시되고, Reset 버튼을 눌러야 정렬된 상태로 보이는 문제 발생

핵심 증상

  • ❌ 초기 로딩: 정렬되지 않은 모델 표시
  • ✅ Reset 버튼 클릭 후: 정렬된 모델 표시
  • 🤔 같은 데이터, 다른 결과

📋 문제 상황 분석

현재 코드 흐름

graph TD
    A[onMounted] --> B[모델 렌더링]
    B --> C[정렬되지 않은 모델 화면 표시]
    C --> D[watch 감지]
    D --> E[registration_result 적용]
    E --> F[변환 행렬 적용]
    
    G[Reset 버튼] --> H[저장된 정렬 상태 적용]
    H --> I[정렬된 모델 표시]

문제의 근본 원인

1. 타이밍 이슈 (Timing Issue)

// 잘못된 실행 순서
onMounted(async () => {
    await renderScene(modelData);           // 1. 모델 먼저 렌더링
    // 이 시점에서 이미 화면에 정렬되지 않은 모델이 표시됨
});

watch(() => store.registration_result, (result) => {
    applyAlignment(result);                 // 2. 나중에 정렬 적용
});

2. 비동기 작업의 완료 시점 불명확

  • 3D 렌더링은 GPU 작업이라 정확한 완료 시점 파악 어려움
  • WebGL/VTK.js의 렌더링 완료와 실제 화면 업데이트 시점이 다름

3. 상태 동기화 문제

  • Vue의 반응형 시스템과 3D 렌더링 생명주기 불일치
  • 여러 곳에서 같은 데이터를 다른 시점에 처리

🔧 문제 해결 방법

1단계: 중복 실행 방지 플래그 추가

// 정렬 적용 중복 실행 방지
const isApplyingAlignment = ref(false);

function applyRegistrationResult(result: any) {
  if (isApplyingAlignment.value) {
    console.log('이미 정렬 적용 중 - 스킵');
    return;
  }
  
  isApplyingAlignment.value = true;
  try {
    // 정렬 로직 실행
    applyTransformations(result);
  } finally {
    isApplyingAlignment.value = false;
  }
}

2단계: ViewerFacade에 강제 렌더링 메서드 추가

// ViewerFacade.ts
public forceRender(): void {
  if (this.scene) {
    this.sceneManagerAdapter.renderScene(this.scene);
    console.log('ViewerFacade: Force render completed');
  } else {
    console.warn('ViewerFacade: Scene not initialized, cannot force render');
  }
}

3단계: onMounted에서 기존 정렬 결과 확인 및 적용

onMounted(async () => {
    try {
        // 1. 모델 렌더링
        const modelData = await createArrayBufferToPropRequestDto();
        viewerFacade.renderScene(modelData);
        
        // 2. 렌더링 완료 후 기존 정렬 결과가 있으면 즉시 적용
        setTimeout(() => {
            const existingResult = autoAlignmentStore.registration_result;
            if (existingResult && JSON.stringify(existingResult) !== '{}') {
                console.log('onMounted: 기존 registration_result 발견, 즉시 적용');
                applyRegistrationResult(existingResult);
            }
        }, 100); // 렌더링 완료를 위한 짧은 지연
        
        // 3. 이벤트 리스너 등록
        addEventListeners();
    } catch (error) {
        console.error('Component: Failed to render scene:', error);
    }
});

4단계: Watch와 onMounted에서 동일한 함수 사용

// 정렬 로직을 별도 함수로 분리하여 재사용
function applyRegistrationResult(result: any) {
  if (isApplyingAlignment.value) return;
  
  console.log('registrationResult 적용 시작 - ', result);
  
  if (result && JSON.stringify(result) !== '{}') {
    isApplyingAlignment.value = true;
    
    try {
      const registrationResult = new RegistrationResult(result);
      const transformations = extractTransformations(registrationResult);
      
      // 변환 행렬 적용
      transformations.forEach(({ modelName, matrix }) => {
        viewerFacade.applyTransformToProp(modelName, matrix);
      });
      
      // 강제 렌더링으로 즉시 화면 업데이트
      setTimeout(() => {
        viewerFacade.forceRender();
        setCameraView(CAMERA_VIEWS.FRONT);
      }, 200);
      
    } finally {
      isApplyingAlignment.value = false;
    }
  }
}

// onMounted와 watch에서 동일한 함수 사용
watch(() => autoAlignmentStore.registration_result, applyRegistrationResult);

✅ 해결 결과

Before (문제 상황)

onMounted → 정렬되지 않은 모델 표시 → 사용자가 Reset 클릭 → 정렬된 모델 표시

After (해결 후)

Case 1: Result 있음
onMounted → 모델 렌더링 → 즉시 정렬 적용 → 정렬된 모델 표시

Case 2: Result 없음  
onMounted → 정렬되지 않은 모델 표시 → Result 도착 → 즉시 정렬 적용 → 정렬된 모델 표시

핵심 개선사항

  1. 타이밍 독립성: Registration result의 도착 시점에 관계없이 정상 동작
  2. 중복 방지: 여러 곳에서 동시 호출되어도 한 번만 실행
  3. 즉시 반영: 강제 렌더링으로 변경사항 즉시 화면 반영
  4. 일관성: Reset 버튼과 동일한 로직으로 예측 가능한 동작

🎯 핵심 학습 사항

JavaScript/Node.js의 고질적 문제들

  1. 비동기 타이밍 이슈: 실행 순서 예측 어려움
  2. 이벤트 루프의 예측 불가능성: setTimeout, Promise, DOM 렌더링 순서
  3. 상태 동기화 문제: 여러 컴포넌트에서 같은 상태 변경
  4. 3D 렌더링 특성: GPU 작업 완료 시점 불명확

해결 패턴

  • 플래그 기반 중복 방지: isProcessing 플래그 활용
  • 함수 분리 및 재사용: 동일한 로직을 여러 곳에서 안전하게 사용
  • 강제 동기화: 필요 시점에 명시적 렌더링 수행
  • 타이밍 제어: setTimeout을 활용한 실행 순서 조절

🔍 추가 고려사항

성능 최적화

  • 불필요한 렌더링 방지를 위한 더 정교한 상태 관리
  • 대용량 3D 모델 처리를 위한 메모리 최적화

에러 처리

  • 네트워크 지연이나 파일 로딩 실패 시나리오 대응
  • 렌더링 실패 시 사용자 피드백

확장성

  • 다양한 파일 형식 지원 시 동일한 패턴 적용
  • 멀티뷰포트 환경에서의 동기화

결론: 3D 웹 애플리케이션에서는 렌더링 타이밍과 상태 동기화가 매우 중요하며, 명확한 실행 순서 보장과 중복 방지 메커니즘이 필수적이다.

profile
기억을 위한 기록 :>

0개의 댓글