TroubleShooting

[TroubleShooting] 3D 지형 종/횡단면 분석 기능 이슈 해결

teddy bear 2026. 7. 6. 01:33
반응형

🔧 트러블슈팅 & 이슈 히스토리

3D 종·횡단면 분석 마이크로프론트엔드

 

프로젝트 ***-svc3d-front-mf-crossanalysis
범위 운영·개발 중 발생한 주요 이슈 · 원인 · 해결 · 재발 방지
작성일 2026-07-09

 

코드베이스, Git 커밋 이력, 주석·워크어라운드 패턴을 기반으로 정리했습니다.
IP · 내부 서비스명 등 민감 정보는 ████ 처리되었습니다.

 


📑 목차

#이슈심각도

01 CORS · Nginx API Gateway 라우팅 불일치 🔴 Critical
02 Qiankun CORS · Webpack Externals 의존성 🔴 Critical
03 공유 Cesium Viewer Entity 오염 🔴 Critical
04 3D Tileset 하이라이트 누적 버그 🟠 Major
05 SHP MultiLineString 인덱스 클로저 오류 🟠 Major
06 좌표계 불일치 (EPSG:5186 ↔ 4326) 🔴 Critical
07 모듈 스코프 변수 상태 꼬임 🟠 Major
08 보고서 생성 비동기 Race Condition 🟠 Major
09 request 인터셉터 조건식 버그 🟡 Minor
10 지하 시각화 Globe 투명도 · Depth Test 🟠 Major
11 레이어 WFS · Capabilities 파싱 실패 🟡 Minor
12 Docker · K8s 배포 경로 불일치 🟠 Major

이슈 요약 매트릭스

                        발생 빈도
              낮음 ────────────────── 높음
         ┌──────────────────────────────┐
  Critical │  #06 좌표계    #01 CORS      │
           │  #02 Externals #03 Entity    │
         ├──────────────────────────────┤
    Major  │  #12 Docker    #04 Tileset   │
           │  #07 Module    #05 SHP index │
           │  #08 Report    #10 Globe     │
         ├──────────────────────────────┤
    Minor  │  #09 Request   #11 Layer WFS │
         └──────────────────────────────┘

Issue #01: CORS · Nginx API Gateway 라우팅 불일치

심각도 🔴 Critical
증상 브라우저에서 API 호출 시 CORS policy 차단, 502/404, 프론트는 정상 로드되나 분석·레이어·보고서 API 전부 실패
영향 범위 전체 기능 (분석 · 레이어 · 보고서 · 파일 업로드)
관련 파일 default.conf, dsDockerfile

원인

  1. Cross-Origin 요청 — 마이크로앱(8099)과 API 서버(다른 Origin)가 분리되어 있어 브라우저 CORS 정책에 걸림
  2. Nginx root 경로 오류 — 정적 파일 root가 /usr/share/nginx/html/dtmap으로 설정되어 빌드 산출물 경로와 불일치
  3. API Upstream 직접 연결 — /api-gateway/ location이 내부 IP(10.**.***.***:8887)에 직접 프록시되어 게이트웨이 통합 전환 후 라우팅 실패

해결

① dsDockerfile — CORS 사전 대응

# CORS 에러 발생을 차단하기 위한 사전작성 설정 정보 적용
COPY default.conf /etc/nginx/conf.d/

② default.conf — 정적 root 수정 (commit: 886218a)

- root   /usr/share/nginx/html/dtmap;
+ root   /usr/share/nginx/html;

③ API Gateway 통합 라우팅 (commit: d9f0b7a)

  location /api-gateway/ {
-     proxy_pass http://10.**.***.***:8887/;
+     proxy_pass http://10.**.***.***:8000;
  }

아키텍처 (해결 후)

flowchart LR
    Browser["Browser\n(same origin)"] --> Nginx["Nginx :8080"]
    Nginx --> Static["/crossSectionalAnalysis\n(static)"]
    Nginx --> GW["/server · /dtapi\n/api-gateway · /geosvr"]
    GW --> Internal["Internal Services\n(10.**.***.***:8000)"]

재발 방지

  • 프론트 코드에 절대 URL(내부 IP) 하드코딩 금지 — 상대 경로(/server, /dtapi)만 사용
  • Nginx location 추가·변경 시 정적 root와 dist COPY 경로 동시 확인
  • 배포 후 Network 탭에서 Origin 일치 여부 smoke test

Issue #02: Qiankun 서브앱 CORS · Webpack Externals 의존성

심각도 🔴 Critical
증상 (개발) 메인 포털에서 서브앱 JS 로드 실패 / (운영) Cesium is not defined, React is not defined
영향 범위 마이크로앱 로드 · Production 빌드
관련 파일 webpack.config.js, index.tsx

원인

환경원인

개발 Webpack Dev Server(8099)와 메인 포털(다른 포트) 간 Cross-Origin 스크립트 로드
운영 Production 빌드에서 React · Cesium · Turf 등을 externals로 분리 → 메인 포털 CDN/global 변수에 의존

해결

개발 — CORS 헤더

// webpack.config.js
devServer: {
  port: 8099,
  headers: {
    'Access-Control-Allow-Origin': '*'
  },
}

운영 — Externals + UMD

externals: {
  "@cesiumgs/cesium-analytics": "Cesium",
  react: 'React',
  'react-dom': 'ReactDOM',
  '@turf/turf': 'turf',
  proj4: 'proj4',
  wellknown: 'wellknown',
},
output: {
  libraryTarget: 'umd'  // window.LongitudinalAnalysis
}

체크리스트

□ 메인 포털 HTML에 Cesium · React · Turf CDN/global 선로드 확인
□ build:prod 산출물을 포털 없이 단독 열면 externals 에러 발생 (정상)
□ Qiankun mount 시 container DOM id 일치 확인
   → #microapp-crossSectionalAnalysis-LongitudinalAnalysis

Issue #03: 공유 Cesium Viewer Entity 오염

심각도 🔴 Critical
증상 마이크로앱 종료 후에도 분석선·포인트·레이어가 지도에 잔존, 다른 마이크로앱과 Entity ID 충돌
영향 범위 전체 포털 UX, 다른 분석 도구
관련 파일 app.tsx, LongitudinalAnalysis.tsx, ChangeInfo.tsx

원인

초기 구현에서 Entity를 **view.entities(Viewer 기본 컬렉션)**에 직접 추가했습니다. Qiankun 아키텍처에서 Viewer는 여러 마이크로앱이 공유하므로, 한 앱의 Entity가 다른 앱에 영향을 줍니다.

// ❌ 초기 패턴 (주석으로 잔존)
// view.entities.removeById(`${prefix}-line`);
// view.entities.removeAll();

해결

① CustomDataSource 격리

// LongitudinalAnalysis.tsx
dataSource = new CustomDataSource('crossDrawCreat');
view.dataSources.add(dataSource);

// Entity 추가/제거는 dataSource 기준
dataSource.entities.add({ ... });
dataSource.entities.removeById(`${prefix}-line`);

② 언마운트 cleanup

// useEffect return (LongitudinalAnalysis.tsx)
if (dataSource) {
  dataSource.entities.removeAll();
  view.dataSources.remove(dataSource);
}
view.dataSources.getByName('crossMemoCreate').forEach(item => {
  view.dataSources.remove(item);
});

③ 앱 종료 시 Viewer 정리 (app.tsx)

useEffect(() => {
  return () => {
    refreshCoverageCheck();
    onCloseMicroApp();
  }
}, [])

// 패널 닫기 (handleChange case 1)
window.cviewer.entities.removeAll();
layer.forEach(item => window.cviewer.imageryLayers.remove(item.inst));

DataSource 네이밍 규칙

DataSource Name용도

crossDrawCreat 분석선 · 포인트 · 하이라이트
crossMemoCreate 지점 메모 Entity
polygon / lodPolygon WFS · LOD1 폴리곤

Issue #04: 3D Tileset 하이라이트 누적 버그

심각도 🟠 Major
증상 분석 반복 시 LOD1 건물 타일 색상이 점점 진해지거나, 이전 분석 객체 색상이 남음
영향 범위 지상 객체 시각화 · 3D Tileset Coverage
관련 파일 app.tsx — refreshCoverageCheck()

원인

분석 중 Coverage 3D Tileset Feature에 색상을 직접 변경하지만, 타일이 unload/reload될 때 이전 색상 상태가 GPU 버퍼에 누적됩니다. tileVisible 이벤트 리스너도 분석마다 중복 등록될 수 있습니다.

해결

// app.tsx — refreshCoverageCheck()
primitive.tileVisible.addEventListener(async (tile) => {
  try {
    if (!tile.contentReady) {
      await tile.content.readyPromise;
    }
    const content = tile.content;
    for (let j = 0; j < content.featuresLength; j++) {
      const feature = content.getFeature(j);
      // ★ 이전 상태와 무관하게 강제 WHITE 리셋
      feature.color = Cesium.Color.WHITE;
    }
  } catch (error) {
    console.error(`Error processing tile at index ${i}:`, error);
  }
});

언마운트 시 refreshCoverageCheck() 호출로 타일 색상을 초기화합니다.

주의

⚠ tileVisible 리스너는 타일셋마다 등록됨 → 앱 재진입 시 중복 등록 가능
⚠ 분석 시작(buttonClick) 시 tileset = [] 로 배열 초기화 병행

Issue #05: SHP MultiLineString 인덱스 클로저 오류

심각度 🟠 Major
증상 MultiLineString SHP 업로드 시 모든 라인이 동일 label/value를 갖거나, 잘못된 라인이 선택됨
영향 범위 SHP 기반 분석선 업로드
관련 커밋 0fa21b4 — SHP(line) 분석 시 index 수정
관련 파일 LongitudinalAnalysis.tsx — handleUpload()

원인

forEach 중첩 시 **외부 루프의 index**를 내부 MultiLineString 항목 label에 재사용했습니다. Feature가 여러 개일 때 마지막 index 값만 반영되는 클로저/스코프 버그입니다.

// ❌ Before
allFeatures.forEach((feature, index) => {
  featuresArr.forEach((featureItem) => {
    label: `line${index + 1}`,  // 외부 index 재사용
    value: index,
  });
});

// ✅ After
featuresArr.forEach((featureItem, lineIndex) => {
  label: `line${lineIndex + 1}`,
  value: lineIndex,
});

재발 방지

  • 중첩 forEach/map에서 각 레벨 고유 index 변수명 사용 (lineIndex, featureIndex)
  • MultiLineString SHP 테스트 케이스: 2개 이상 LineString 포함 파일로 회귀 테스트

Issue #06: 좌표계 불일치 (EPSG:5186 ↔ 4326)

심각도 🔴 Critical
증상 지하시설물 Scatter가 종단면 축에서 수 km 어긋남, 교차점 0건, 차트에 지하 데이터 미표시
영향 범위 지하 객체 분석 · SHP 업로드
관련 파일 LongitudinalAnalysis.tsx, LayerData.tsx, sample.ts

원인

시스템 내 데이터 소스별 기본 좌표계가 다릅니다.

데이터좌표계API/소스

Cesium 지형 · 분석선 EPSG:4326 (WGS84) sampleTerrainMostDetailed
LOD1 건물 EPSG:4326 getBuildgeom2
지하시설물 EPSG:5186 (Korea 2000) queryCrossList
SHP 업로드 사용자 선택 (4326~5186) uploadShpZipForEPSG

지하 geometry WKT는 5186인데, 종단면 축(lineString)은 4326으로 계산 → Turf lineIntersect 결과 무효.

해결

// 지하 MULTILINESTRING → WGS84 변환 후 교차
json.forEach((item) => {
  let obj4326 = convertCoordinates(
    'EPSG:5186', 'EPSG:4326',
    [Number(item[0]), Number(item[1]), Number(item[2])]
  );
  newList.push([obj4326.x, obj4326.y]);
});
const intersection = turf.lineIntersect(turf.lineString(newList), lineString);

SHP 업로드는 sourceEpsg → targetEpsg: 4326 서버 변환 후 사용합니다.

디버깅 팁

콘솔 필터: [종/횡단 분석]
확인 항목:
  ① queryCrossList 요청 WKT 좌표 범위 (5186: x~200000, y~500000)
  ② lineString 좌표 범위 (4326: lng~126-132, lat~33-39)
  ③ 변환 후 intersection.features.length > 0 ?

Issue #07: 모듈 스코프 변수 상태 꼬임

심각도 🟠 Major
증상 HMR(Hot Reload) 후 이전 분석 데이터 잔존, 분석 버튼 무반응, allOption 플래그 오동작
영향 범위 개발 환경 · React Strict Mode
관련 파일 LongitudinalAnalysis.tsx, LongDataDownload.tsx, ChangeInfo.tsx

원인

Cesium Entity 수명주기와 연동된 상태를 컴포넌트 외부 let 변수로 관리합니다.

// LongitudinalAnalysis.tsx — 모듈 스코프
let drawPoints: Cartesian3[] = [];
let splinePositions: Cartesian3[] = [];
let dataSource: CustomDataSource;
let allOption = 0;
let lineChange: any[] = [];

// LongDataDownload.tsx
let newGroundData: any = [];
let newUnderGroundData: any = [];

React 컴포넌트 remount/HMR 시 useState는 초기화되지만 모듈 변수는 유지됩니다.

완화 조치 (현재 코드)

시점조치

useEffect cleanup dataSource.entities.removeAll(), Handler removeInputAction
buttonClick(1) 초기화 addedPoints = [], lineChange = [], dataSource.entities.removeAll()
citySpaceData allOption = 0 분석 완료 후 리셋

권장 개선 (향후)

useRef 또는 Redux로 마이그레이션
→ HMR/Strict Mode double-mount에도 안전
→ 디버깅 시 React DevTools로 상태 추적 가능

Issue #08: 보고서 생성 비동기 Race Condition

심각도 🟠 Major
증상 DOCX 다운로드 시 지상 객체 목록이 비어 있거나 일부만 포함, upperList 누락
영향 범위 보고서 생성 (LongDataDownload)
관련 파일 LongDataDownload.tsx

원인

useEffect에서 groundData.map() 내부 newquery3DtailInfonext1 Promise가 비동기인데, setGroundDataSource(newGroundData)를 Promise 완료 전에 호출합니다.

// ❌ 문제 패턴
groundData.map((data) => {
  newquery3DtailInfonext1({ nfId }).then((res) => {
    newGroundData.push(obj);  // 나중에 완료
  });
});
setGroundDataSource(newGroundData);  // 빈 배열 또는 일부만

handleDownload 시점에 groundDataSource가 아직 채워지지 않았을 수 있습니다.

해결 방향

// ✅ Promise.all 패턴 (권장)
const results = await Promise.all(
  groundData.map(data =>
    newquery3DtailInfonext1({ nfId: data.newPolygon.properties.item.nfId })
  )
);
setGroundDataSource(results.map(/* transform */));

// handleDownload에서 groundDataSource 대신
// await로 fresh fetch 후 reportDownLoad 호출

임시 우회

보고서 다운로드 전 속성 패널에서 데이터 로드 완료 확인 후 버튼 클릭 (UX 개선 필요).


Issue #09: request 인터셉터 조건식 버그

심각도 🟡 Minor (잠재적)
증상 Portal Embed가 아닌 환경에서도 URL에 . prefix가 붙거나, 반대로 prefix가 누락
관련 파일 utils/request.ts

원인

// ❌ JavaScript truthy 함정 — "op-portal" 문자열은 항상 true
if (process.env.appConfigGbn === "portal-embed" || "op-portal") {
  if (url.startsWith('/')) {
    url = '.' + url;
  }
}

|| "op-portal"은 두 번째 operand가 non-empty string이므로 조건식 전체가 항상 true입니다.

수정 (권장)

// ✅
if (process.env.appConfigGbn === "portal-embed" ||
    process.env.appConfigGbn === "op-portal") {
  if (url.startsWith('/')) {
    url = '.' + url;
  }
}

Issue #10: 지하 시각화 Globe 투명도 · Depth Test

심각度 🟠 Major
증상 지하시설물 Scatter는 보이나 지형 아래 매설 깊이가 가려짐, 또는 지형이 불투명하게 덮음
영향 범위 종단면 차트 + 3D 지도 동시 분석 UX
관련 파일 LongitudinalAnalysis.tsx — mount/unmount useEffect

원인

Cesium Globe 기본 설정은 지형이 항상 불투명하고 depthTestAgainstTerrain = false입니다. 지하 매설 깊이를 3D+차트로 동시에 보여주려면 Globe translucency 조정이 필요합니다.

해결 (마운트 시)

// 분석 시작 — 지하 관찰 모드
globe.depthTestAgainstTerrain = true;
globe.translucency.enabled = true;
globe.translucency.frontFaceAlphaByDistance = new Cesium.NearFarScalar(
  10.0, 0.0,   // 가까울수록 투명
  5000, 1.0    // 멀수록 불투명
);
scene.screenSpaceCameraController.enableCollisionDetection = false;

cleanup (언마운트 시)

// 원래 Globe 설정 복원 필수 — 다른 마이크로앱 영향 방지
globe.baseColor = oldBaseColor;
globe.undergroundColor = oldUndergroundColor;
globe.depthTestAgainstTerrain = oldDepthTestAgainstTerrain;
globe.translucency.enabled = oldTransEnable;
scene.screenSpaceCameraController.enableCollisionDetection = oldEnableCollisionDetection;

Issue #11: 레이어 WFS · Capabilities 파싱 실패

심각도 🟡 Minor
증상 레이어 트리 체크 시 리턴값 없음 Ant Design error toast, WFS 폴리곤 미표시
영향 범위 LayerData 패널 · 2D 레이어 오버레이
관련 파일 LayerData.tsx, sample.ts

원인

  1. GeoServer WFS GetFeature 응답 empty 또는 CQL Filter 좌표 오류
  2. Portal Embed 환경에서 레이어 URL이 /로 시작 → 상대 경로 prefix 누락 시 404
  3. WMS Capabilities XML 파싱 시 cntntsId 매칭 실패

해결

Portal Embed URL prefix

// LayerData.tsx — addLayer()
if (process.env.appConfigGbn === "portal-embed") {
  if (node.url.startsWith('/')) {
    node.url = '.' + node.url;
  }
}

에러 핸들링

try {
  // WFS GetFeature + INTERSECTS(the_geom, ...)
} catch (error) {
  message.error('리턴값 없음');
}

디버깅

콘솔 필터: [종/횡단 분석] 레이어
확인: capabilitiesJson, matchingLayer, minX/minY/maxX/maxY

Issue #12: Docker · K8s 배포 경로 불일치

심각도 🟠 Major
증상 컨테이너 기동 후 404, 마이크로앱 HTML/JS 로드 실패, 구버전 캐시 서빙
영향 범위 CI/CD · 운영 배포
관련 커밋 d133510, 4e227fe, 886218a 등 Dockerfile/default.conf 연속 수정
관련 파일 dsDockerfile, default.conf, deploy/all-in-one/devops-web-master.yaml

원인

문제상세

dist COPY 경로 projects/crossSectionalAnalysis/dist → /usr/share/nginx/html/crossSectionalAnalysis
Nginx root 불일치 root가 dtmap 서브디렉터리를 가리켜 index.html 404
Private Registry FROM ████.██.██.███:5000/nginx_unprivileged:1.0 — 레지스트리 접근 필요
K8s Image placeholder __IMAGE__ CI 치환 누락 시 ImagePullBackOff

해결된 Dockerfile

FROM ████.██.██.███:5000/nginx_unprivileged:1.0
USER root
RUN rm /usr/share/nginx/html/index.html
COPY projects/crossSectionalAnalysis/dist /usr/share/nginx/html/crossSectionalAnalysis
COPY default.conf /etc/nginx/conf.d/
EXPOSE 80

배포 검증 체크리스트

□ curl http://<host>/crossSectionalAnalysis/LongitudinalAnalysis.html → 200
□ curl http://<host>/crossSectionalAnalysis/static/*.js → 200
□ /server/api/health (또는 동등 엔드포인트) → 200
□ K8s Pod log에 nginx error 없음
□ 브라우저 Hard Refresh (chunkhash 변경 반영)

부록 A: 건물 Annotation startIndex/endIndex 교정

종단면 차트에 LOD1 건물 Bar를 그릴 때, 교차 구간의 start/end index가 역전되는 경우가 있습니다.

// LongitudinalAnalysis.tsx
if (startIndex > endIndex) {
  [startIndex, endIndex] = [endIndex, startIndex];
}
const unifiedValue = data[startIndex].value;
for (let i = startIndex; i <= endIndex; i++) {
  data[i].value = unifiedValue;  // Bar 높이 통일
}

증상 (수정 전): 건물 Bar가 차트 범위 밖에 그려지거나, Recharts annotation 위치 깨짐.


부록 B: 분석선 10개 제한

// LongitudinalAnalysis.tsx
message.warning('분석선은 최대 10개까지 그릴 수 있습니다.');

Cesium Entity · API 부하 · 차트 가독성을 위해 동시 분석선 수를 10개로 제한합니다. 초과 시 사용자에게 warning toast 표시.


부록 C: Git 이슈 타임라인

timeline
    title 주요 Fix 커밋 흐름
    section 2025-02
        init : 프로젝트 초기화
        Dockerfile 연속 수정 : 배포 경로 튜닝
        default.conf root 수정 : 정적 404 해결
        API Gateway 라우팅 : /api-gateway upstream 변경
        SHP index fix : MultiLineString label 버그
    section 2025+
        UI/UX Source Merge : 포털 UI 통합
        config 수정 : AppConfig 환경 분기

부록 D: 운영 디버깅 Quick Reference

증상1차 확인2차 확인

API 전부 실패 Nginx /server proxy Network 탭 CORS
Cesium is not defined 포털 CDN 로드 webpack externals
Entity 잔존 CustomDataSource cleanup onCloseMicroApp()
지하 데이터 없음 EPSG:5186→4326 변환 queryCrossList 응답
SHP 라인 선택 오류 MultiLineString lineIndex features.length
보고서 upperList 빈값 Promise.all 미적용 API 응답 timing
레이어 404 portal . URL prefix Capabilities XML
404 static nginx root path dist COPY 경로

콘솔 필터

[종/횡단 분석]

 

 

 

Tags: #Troubleshooting #Cesium #MicroFrontend #CORS #GIS #DevOps