3D

[3D] 3D GIS 기능 구현 (feat. MSA)

teddy bear 2026. 7. 6. 22:03
반응형

🌐 3D GIS 위 문화재·토지이용 특화 서비스

마이크로프론트엔드로 구현한 공간정보 특화 애플리케이션 아키텍처

 

| React 18 | Umi 3 | Cesium 1.115 | Qiankun 2 | Ant Design 4 | |:---:|:---:|:---:|:---::|

 

한 줄 요약
단일 SPA가 아닌 19개의 독립 엔트리 × UMD 번들로 쪼개진 마이크로프론트엔드.
Cesium 3D 뷰어 위에 문화재 검색·가시권 분석·허가·토지이용 등 행정 GIS 업무를 얹은 엔터프라이즈 프론트엔드.


📑 목차

  1. 프로젝트 개요
  2. 아키텍처 한눈에 보기
  3. 마이크로프론트엔드 설계 — Qiankun + Multi-Entry
  4. 빌드 파이프라인 — Umi × Webpack 커스터마이징
  5. 3D GIS 엔진 — Cesium 통합 전략
  6. 상태 관리 — DVA + Rematch 이중 구조
  7. 도메인 모듈 맵 — 19개 마이크로 앱
  8. 공간분석 BaseClass 레이어
  9. API·프록시 아키텍처
  10. 테마·스타일 시스템
  11. 개발·배포 워크플로우
  12. 기술적 도전과 설계 결정
  13. 마치며

1. 프로젝트 개요

1.1 무엇을 만드는가

이 프로젝트는 3D 디지털 트윈 지도 플랫폼특화 서비스 마이크로프론트엔드(MF) 모듈입니다.
행정·공공 부문에서 필요한 아래 업무를 지도 위 패널 UI로 제공합니다.

영역핵심 기능

문화재(Cltpty) 문화재 통합검색, 레이어 관리, 가시권·경관 분석, SHP 내보내기
허가·심의(Prmsn/Dlbr) 건축·개발 허가 관리, 심의 목록, 체크리스트, 검토 목록
토지이용(Land Utilization) 필지·시설물 검색, 행위제한 정보, 개발행위, 도시계획 레이어
법령·통계 법령 정보 연동, 통계 대시보드(ECharts), API 연계 관리
부가 기능 온라인 도움말, 공고(Ancmnt) 관리, 문화재 속성 관리

🔒 민감 정보 안내
본 문서에서는 내부 서버 IP, 조직명, L▓ 계열 사내 식별자를 ▓▓▓ 마스킹 처리했습니다.

1.2 기술 스택 스냅샷

Runtime:
  - React 18.2
  - TypeScript 4.9
  - Umi 3.4 (개발 서버·빌드 오케스트레이션)

Micro-Frontend:
  - qiankun 2.10
  - @umijs/plugin-qiankun 2.44
  - UMD libraryTarget per entry

3D / GIS:
  - cesium 1.115.0
  - @cesiumgs/cesium-analytics 1.115.0
  - @turf/turf 6.5
  - proj4, wellknown, geotiff, netcdfjs
  - @sakitam-gis/kriging (공간 보간)

State:
  - dva 2.4 + dva-immer
  - @rematch/core 2.2
  - redux-persist 6.0

UI:
  - antd 4.24 (로컬 fork: src/libs/antd)
  - @ant-design/charts, echarts 5.4
  - react-draggable, react-resizable, react-dnd

Build:
  - webpack 4
  - babel 7, ts-loader
  - mini-css-extract-plugin, compression-webpack-plugin (gzip)
  - less / sass / postcss 파이프라인

Quality:
  - eslint + prettier + husky + lint-staged
  - stylelint

2. 아키텍처 한눈에 보기

2.1 시스템 컨텍스트

flowchart TB
    subgraph Host["🏠 Shell App (Qiankun Main)"]
        MapViewer["Cesium 3D Map Viewer<br/>window.cviewer"]
        PanelHost["CommonPanel / SignlePanel<br/>Floating UI Host"]
        GlobalStore["Global DVA Store<br/>store_msa"]
    end

    subgraph MF["📦 CHDT Micro-Apps (19 entries)"]
        Search["SearchCltptyCom"]
        Visible["VisibleAnlsCom"]
        Manage["ManagePrmsn / ManageDlbr"]
        Land["SearchLotInfo / DevAct"]
        Stats["StatisticsService"]
        Others["... 14 more modules"]
    end

    subgraph Backend["🔌 Backend Layer (Proxied)"]
        CHDTAPI["/apichdt → CHDT Backend"]
        GeoServer["GeoServer WMS/WFS"]
        PublicAPI["공공데이터 / 법령 API"]
        GISProxy["/gisapi GIS Proxy"]
    end

    Host -->|"mount(props)"| MF
    MF -->|"dispatch comPanel/setChildren"| PanelHost
    MF --> MapViewer
    MF --> GlobalStore
    MF --> Backend
    MapViewer --> Backend

2.2 레이어드 아키텍처

┌─────────────────────────────────────────────────────────────┐
│  Presentation Layer                                         │
│  src/pages/Chdt-*/          ← Qiankun lifecycle (bootstrap/ │
│  src/components/ChdtService/    mount/unmount)              │
├─────────────────────────────────────────────────────────────┤
│  State Layer                                                │
│  src/models/  (DVA models per domain)                       │
│  src/models/store.ts  (Rematch — Cesium analysis modules)   │
├─────────────────────────────────────────────────────────────┤
│  Service Layer                                              │
│  src/services/ChdtService/  (REST via umi-request)          │
├─────────────────────────────────────────────────────────────┤
│  Spatial Engine Layer                                       │
│  src/BaseClass/Cesium/  (Visibility, Draw, Wind, Flood...) │
│  src/extends/  (CesiumView, Widget extensions)              │
├─────────────────────────────────────────────────────────────┤
│  Infrastructure                                             │
│  .umirc.ts / webpack.config.js  (Multi-entry UMD build)     │
│  theme-script.js  (Dynamic LESS aggregation)                │
└─────────────────────────────────────────────────────────────┘

3. 마이크로프론트엔드 설계 — Qiankun + Multi-Entry

3.1 왜 Multi-Entry인가?

일반적인 SPA는 single entry → single bundle이지만, 이 프로젝트는 기능 단위로 HTML·JS·CSS를 분리합니다.

설계 목표:

  • Shell 앱이 필요한 모듈만 lazy-load
  • 각 모듈 독립 배포·버전 관리
  • Cesium·React 등 공통 라이브러리 externals로 중복 번들 제거
  • UMD 포맷으로 Qiankun sandbox와 호환

3.2 엔트리 등록 (.umirc.ts)

.umirc.ts에서 19개 엔트리를 명시적으로 선언합니다.

// ▓▓▓ 특화 서비스 엔트리 (발췌)
entries['Chdt-SearchCltptyCom'] = './src/pages/Chdt-SearchCltptyCom/index.tsx'
entries['Chdt-VisibleAnlsCom']   = './src/pages/Chdt-VisibleAnlsCom/index.tsx'
entries['Chdt-CltptyAnls']       = './src/pages/Chdt-CltptyAnls/index.tsx'
entries['Chdt-ManagePrmsn']      = './src/pages/Chdt-ManagePrmsn/index.tsx'
entries['Chdt-StatisticsService']= './src/pages/Chdt-StatisticsService/index.tsx'
// ... 총 19개

각 엔트리마다 전용 HTML 템플릿이 생성됩니다.

new HtmlWebpackPlugin({
  template: path.join(entryDir, 'index.html'),
  filename: `${entryName}.html`,   // e.g. Chdt-VisibleAnlsCom.html
  chunks: [entryName],
  inject: true,
})

3.3 UMD 출력 설정

각 HtmlWebpackPlugin 인스턴스마다 고유 library/jsonpFunction을 부여합니다.

config.output
  .library(`${entryName}_[name]`)
  .libraryTarget('umd')
  .jsonpFunction(`webpackJsonp_${entryName}`)
  .globalObject('window')

이 설정이 없으면 여러 MF가 동시에 로드될 때 webpack runtime 충돌이 발생합니다.
jsonpFunction 분리는 실무에서 자주 놓치는 포인트인데, 이 프로젝트는 엔트리별로 명시적으로 격리합니다.

3.4 Qiankun Lifecycle

모든 페이지 엔트리는 동일한 Micro-App Contract를 따릅니다.

// src/pages/Chdt-VisibleAnlsCom/index.tsx (패턴)
export async function bootstrap() {
  console.log('[react18] react app bootstraped')
}

export async function mount(props: any) {
  // props: { dispatch, store_msa, onCloseMicroApp, ... }
  render(props)
}

export async function unmount(props: any) {
  ReactDOM.unmountComponentAtNode(document.querySelector('#root'))
}

// Shell 없이 단독 실행 시 경고
if (!window.__POWERED_BY_QIANKUN__) {
  console.log('[react18] __POWERED_BY_QIANKUN__ error')
}

3.5 Shell과의 통신 — CommonPanel 패턴

마이크로 앱은 직접 DOM root를 채우기보다, Shell의 floating panel에 UI를 주입합니다.

// ready.tsx 패턴 (발췌)
useEffect(() => {
  dispatch({
    type: 'comPanel/setChildren',
    payload: {
      isshow: true,
      children: (
        <Provider store={store}>
          <IntlProvider locale='ko-KR' messages={message}>
            <SearchCltptyCom
              store={store}
              dispatch={dispatch}
              onCloseMicroApp={onCloseMicroApp}
            />
          </IntlProvider>
        </Provider>
      ),
      styles: { width: '330px', height: '800px', top: '30px' },
    },
  })
}, [])

장점:

  • 3D 지도(window.cviewer)는 Shell이 소유 → MF는 업무 UI만 담당
  • 패널 크기·위치를 Shell 레이아웃 정책으로 통일
  • onCloseMicroApp으로 수명 주기를 Shell에 위임
sequenceDiagram
    participant Shell as Qiankun Shell
    participant MF as CHDT Micro-App
    participant Panel as CommonPanel
    participant Cesium as window.cviewer

    Shell->>MF: mount({ dispatch, store_msa })
    MF->>Panel: comPanel/setChildren
    Panel-->>Shell: Floating UI rendered
    MF->>Cesium: camera.flyTo / entity add
    Note over MF,Cesium: Spatial ops via global viewer ref
    Shell->>MF: unmount()
    MF->>Panel: cleanup handlers / remove primitives

4. 빌드 파이프라인 — Umi × Webpack 커스터마이징

4.1 Dual Portal 환경

UMI_ENV로 두 가지 배포 타겟을 분기합니다.

환경설정 파일publicPath (prod)

Portal A .umirc.lx-portal.ts /▓▓▓-svc3d-front-3dmapapp/
Portal B .umirc.op-portal.ts /▓▓▓-svc3d-front-mf-chdt/
// op-portal 예시
define: {
  'process.env.UMI_ENV': JSON.stringify('op-portal'),
  'process.env.MAP_PATH': JSON.stringify('/▓▓▓-svc3d-front-3dmapapp/'),
},
publicPath: process.env.NODE_ENV === 'production'
  ? '/▓▓▓-svc3d-front-mf-chdt/'
  : '/',

동일 코드베이스 → 다른 Shell Portal에 탑재 가능.

4.2 Externals — 번들 다이어트

Cesium·React 등 수 MB급 라이브러리는 CDN/headScripts로 주입하고 webpack 번들에서 제외합니다.

config.externals({
  '@cesiumgs/cesium-analytics': 'Cesium',
  react: 'React',
  'react-dom': 'ReactDOM',
  moment: 'moment',
  antd: 'antd',
  '@turf/turf': 'turf',
  echarts: 'echarts',
  proj4: 'proj4',
  wellknown: 'wellknown',
})

headScripts에 미리 로드:

headScripts: [
  { src: 'Cesium.js' },
  { src: 'lib/react/react.production.min.js' },
  { src: 'lib/react-dom/react-dom.production.min.js' },
  { src: 'lib/antd/antd.min.js' },
  { src: 'lib/turf/turf.min.js' },
  { src: 'lib/echarts/echarts.min.js' },
  // ...
]

💡 인사이트
MF 환경에서 externals는 "옛날 방식"처럼 보이지만, Cesium + 19개 엔트리 조합에서는
공통 라이브러리 중복 로드를 막는 가장 확실한 전략입니다.

4.3 PreBuildCopyPlugin — yoga-layout 패치

빌드 전 yoga-layout 소스를 node_modules에 복사하는 커스텀 플러그인이 있습니다.

class PreBuildCopyPlugin {
  apply(compiler) {
    compiler.hooks.beforeRun.tapAsync('PreBuildCopyPlugin', (_, callback) => {
      fs.copy(from, to).then(() => callback()).catch(callback)
    })
  }
}

@react-pdf/renderer 등이 의존하는 yoga-layout의 패키지 구조 불일치를 빌드 타임에 해결하는 패턴입니다.

4.4 Gzip & Content Hash

프로덕션 빌드:

  • compression-webpack-plugin — 10KB 이상 js/css gzip
  • MiniCssExtractPlugin — 엔트리별 CSS + contenthash + 랜덤 salt
  • hash: true (Umi) — 캐시 무효화
function generateRandomString(length) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
  // 8자리 랜덤 → CSS filename salt
}

4.5 GeoJSON Loader

공간 데이터를 JS 모듈처럼 import하기 위한 규칙:

config.module
  .rule('geojson')
  .test(/\.geojson$/)
  .use('json-loader')
  .loader('json-loader')

src/public/data/ 아래 수백 개의 GeoJSON(행정구역, 문화재 exit point 등)을 번들/서빙합니다.


5. 3D GIS 엔진 — Cesium 통합 전략

5.1 글로벌 Viewer 참조

Shell이 초기화한 Cesium Viewer는 **window.cviewer**로 공유됩니다.

// main.tsx — Qiankun mount 후 카메라 이동
useEffect(() => {
  if (window.__POWERED_BY_QIANKUN__) {
    const c = Cartesian3.fromDegrees(126.9, 36.807191, 70000)
    window.cviewer.camera.flyTo({ destination: c })
  }
}, [])

MF는 Viewer를 생성하지 않고 scene/primitives/entities만 조작합니다.

5.2 Cesium Extensions

src/extends/에서 Viewer 초기화·위젯·PostProcessStage를 확장합니다.

src/extends/
├── CesiumView.ts              # Viewer bootstrap
├── CesiumWidgetsExtends.ts    # Custom widgets
└── PostProcessStageLibraryExtends.ts

5.3 가시권 분석 (Visibility Analysis)

문화재 경관 심의의 핵심 기능. NewVisibilityAnalysisClass가 담당합니다.

알고리즘 흐름:

  1. ScreenSpaceEventHandler로 관측점·목표점 2점 클릭
  2. CustomDataSource('visibilityanalysis')에 폴리라인·라벨 entity 추가
  3. 3D Tileset / Terrain과 ray intersection → 가시·비가시 구간 색상 분리
  4. distory()로 handler·entity 정리 (메모리 누수 방지)
export default class NewVisibilityAnalysisClass {
  viewer = null
  drawLayer = null

  constructor(viewer, tileset) {
    this.viewer = viewer
    this.drawLayer = new Cesium.CustomDataSource('visibilityanalysis')
    this.viewer.dataSources.add(this.drawLayer)
  }

  drawPointline() {
    this.VA_handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
    // LEFT_CLICK: 포인트 수집
    // MOUSE_MOVE: 동적 폴리라인
    // RIGHT_CLICK: 분석 시작 → startAnalysis()
  }
}

VisibleAnlsCom.tsx는 1,800+ LOC로 UI·카메라 freeze·경로 재생·건물 모델 오버레이까지 통합합니다.

5.4 공간 연산 스택

라이브러리용도

@turf/turf buffer, intersect, area, centroid
proj4 EPSG:5186 ↔ WGS84 좌표변환
wellknown WKT ↔ GeoJSON
geotiff GeoTIFF 래스터 로드
@sakitam-gis/kriging Kriging 보간 (KrigingModel.ts)
cesium-heatmap-es6 히트맵 시각화

서버 측 buffer 연산 API도 병행:

export async function getBuffer({ polygonString, dist }) {
  return request.post(`${ChdtCfg.moduleUri}chdt/cltpty/getBufferArea.do`, {
    data: { polygonString, dist },
  })
}

6. 상태 관리 — DVA + Rematch 이중 구조

6.1 DVA — 도메인 업무 상태

각 MF 페이지는 createDvaStore.js로 페이지 전용 DVA store를 생성합니다.

src/models/ChdtService/
├── Prmsn/SearchPrmsnModel.ts
├── Dlbr/SearchDlbrModel.ts
├── LandUtilizationService/
│   ├── devAct.ts
│   ├── statistics.ts
│   └── ...

DVA effects에서 src/services/ChdtService/ API를 호출하고,
Ant Design Table·Form과 connect() HOC로 연결합니다.

6.2 Rematch — Cesium 분석 상태

src/models/store.ts는 지도 분석 전용 Rematch store입니다.

const store = init({
  shadowAnalysis: ShadowAnalysisModel,
  cesiumview: CesiumViewModel,
  coverage: CoverageModel,
  comPanel: comPanelModel,
  floodAnalysis: FloodAnalysisModel,
  densityAnalysis: DensityAnalysisModel,
  // ... 20+ models
})

분리 이유:

Store책임

DVA (per MF) CRUD, pagination, form state
Rematch (global) Cesium viewer, analysis tools, panel layout

Shell에서 store_msa로 Rematch store를 MF에 props injection → 지도 상태 공유.


7. 도메인 모듈 맵 — 19개 마이크로 앱

#Entry Name도메인핵심 컴포넌트

1 Chdt-SearchCltptyCom 문화재 통합검색 SearchCltptyCom
2 Chdt-ManageCltpty 문화재 속성 관리 ManageCltpty, UpdateCltpty
3 Chdt-VisibleAnlsCom 가시권·경관 분석 VisibleAnlsCom, VisualAnalysis
4 Chdt-CltptyAnls 문화재 분석 (풀맵) Cesium + CltptyAnlsService
5 Chdt-CulturalProperties 문화재 목록·속성 CulturalProperties
6 Chdt-CtyPlanLyr 도시계획 레이어 CtyPlanLyr, LyrOnOff
7 Chdt-ManageAncmnt 공고 관리 ManageAncmnt
8 Chdt-ManagePrmsn 허가 관리 ManagePrmsn, PrmsnBuildings
9 Chdt-ManageDlbr 심의 관리 InsertDlbr, CheckDlbr
10 Chdt-CheckList 체크리스트 CheckList, CheckListLawPop
11 Chdt-ReviewList 검토 목록 ReviewItem
12 Chdt-PrmsnStandard 허가 기준 기준 데이터 조회
13 Chdt-SearchLotInfo 필지 정보 검색 필지 API + 지도 하이라이트
14 Chdt-SearchFacilityInfo 시설물 검색 facility.ts service
15 Chdt-SearchActLimitInfo 행위제한 검색 actLimitInfo.ts
16 Chdt-DevAct 개발행위 RelatedLaw, DevAct model
17 Chdt-LawInfo 법령 정보 LawInfo, LawDetailContent
18 Chdt-StatisticsService 통계 StatisticsCharts (ECharts)
19 Chdt-LinkApi / Chdt-OnlineHelp API 연계 / 도움말 부가 기능

8. 공간분석 BaseClass 레이어

src/BaseClass/Cesium/ 아래 재사용 가능한 GIS primitive가 80+ 모듈로 구성됩니다.

8.1 카테고리

BaseClass/Cesium/
├── HistoryHeritage/     # 문화재 가시권·SHP export·DrawArea
├── Draw/                # Point/Line/Polygon/Circle/Rect DrawAction
├── CustomMeasure/       # 거리·면적·고도 측정
├── Wind/ / WindNew/     # 바람장 파티클 (custom GLSL shader)
├── WindSystem/          # 3D wind field module
├── DisasterAnalysis/    # FloodAnalysis, SlopeAspect
├── UrbanEnvironmentAnalysis/  # AirQuality, WindDirection
├── Interpolation/       # KrigingModel
├── Geotiffutil/         # GeoTIFF parser
├── BuildingSimulation/  # 건축 시뮬레이션
├── FlightSimulation/    # 비행 경로
├── Swip/                # 스와이프 비교
├── FadeLine/            # 애니메이션 라인
└── CustomMaterialProperty/  # 커스텀 Cesium Material

8.2 Draw Action 패턴

Command 패턴으로 지도 위 그리기 상호작용을 추상화:

// DrawAction.ts 계열
DrawPointAction / DrawLineAction / DrawPolygonAction
DrawCircleAction / DrawRectAction

// 공통: DrawEventType, DrawOptions, DrawResult

각 Action은 ScreenSpaceEventHandler 수명을 캡슐화하고,
완료 시 WKT/GeoJSON DrawResult를 반환 → 서버 API 전송.

8.3 Wind Particle System

WindNew/는 GPU 기반 파티클 렌더링:

WindNew/
├── particleSystem.js
├── particlesComputing.js   # Compute pass
├── particlesRendering.js   # Render pass
├── glsl/
│   ├── updatePosition.frag
│   ├── screenDraw.frag
│   └── segmentDraw.frag
└── customPrimitive.js    # Cesium CustomPrimitive wrapper

Cesium PostProcessStage + custom shader로 대기 오염·바람장 시각화.


9. API·프록시 아키텍처

9.1 ChdtCfg — 환경별 엔드포인트

export const ChdtCfg = {
  moduleUri: '/apichdt/',
  nginxUri: '/chdt/',
  geoserverUri: 'http://▓▓▓.▓▓▓.▓▓▓.▓▓▓:▓▓▓▓▓/',
  serverApiUri: '/server',
  ▓▓ApiUri: '/chdt▓▓ApiUri/',
  geoserverStorage: '▓▓▓▓▓▓▓▓',
}

🔒 운영/개발 서버 주소는 소스에 주석으로 분기되어 있으나,
본 문서에서는 모두 마스킹합니다.

9.2 Dev Proxy 맵 (Umi)

개발 서버에서 CORS를 우회하는 path-prefix 프록시:

Prefix역할

/apichdt CHDT 백엔드 REST
/gisapi GIS 통합 프록시
/server, /serverdata 행정·워크스페이스 API
/publicData 공공데이터포털
/lawData 국가법령정보센터
/publicPrice, /publicApartRent 국토부 Open API
/jdtgeo, /usujgeoserver GeoServer 인스턴스
/fmeserver FME 공간 ETL
proxy: {
  '/apichdt': {
    target: 'http://▓▓▓.▓▓▓.▓▓▓.▓▓▓:▓▓▓▓▓',
    changeOrigin: true,
    pathRewrite: { '^/apichdt/': '/apichdt/▓▓▓-opsmgmt-back-chdtback' },
  },
  '/lawData': {
    target: 'https://www.law.go.kr',
    changeOrigin: true,
    pathRewrite: { '^/lawData': '/' },
  },
}

9.3 HTTP Forward 패턴

일부 레거시 API는 게이트웨이 HTTP Forward를 사용:

return request.post('/server/api/httpForward/▓▓_uav_api', {
  data: {
    url: 'http://▓▓▓.▓▓▓.▓▓▓.▓▓▓:▓▓▓▓/services/▓▓▓/address/coord?...',
  },
})

프론트 → 사내 게이트웨이 → 외부/레거시 URL. serviceKey 등 민감 파라미터는 서버 측 관리가 이상적입니다.


10. 테마·스타일 시스템

10.1 Dynamic Theme Aggregation

npm run theme → theme-script.js 실행:

  1. public/styles/component.less 초기화
  2. src/**/*.theme.less glob 탐색
  3. global.theme.less 우선, 나머지 파일 append
glob('src/**/*.theme.less', { ignore: ['node_modules/**', 'src/.umi/**'] }, (err, files) => {
  globalFile.concat(otherList).forEach((filePath) => {
    fs.appendFileSync(customPath, `\n// ${filePath}\n${content}`)
  })
})

장점: 페이지·컴포넌트별 theme 파일을 선언적으로 분산하고, 빌드 시 하나로 합침.

10.2 Ant Design Fork

config.resolve.alias.set('antd', path.resolve(__dirname, 'src/libs/antd'))
config.resolve.alias.set('./antd/dist/antd.less', path.resolve(__dirname, 'src/libs/antd/dist/antd.less'))

사내 디자인 시스템에 맞게 antd 소스를 vendoring → 버전 업그레이드는 수동 merge 필요.

10.3 Styling Stack

  • Less — 컴포넌트 스코프 스타일 (.less co-located)
  • Sass/SCSS — .theme.less 제외 css rule
  • CSS Modules — webpack.config.js에서 [name]-[local]-[hash] (legacy webpack path)
  • PostCSS — autoprefixer, preset-env, nested

11. 개발·배포 워크플로우

11.1 npm Scripts

# 개발 — Portal B (운영 포털 Shell 연동)
npm run umi-start:op-portal

# 개발 — Portal A
npm run umi-start:▓▓-portal

# 프로덕션 빌드
npm run umi-build:op-portal
npm run umi-build:▓▓-portal

# 테마 재생성 (dev/build 전 자동 실행)
npm run theme

# Legacy webpack (multi-entry standalone)
npm run start    # webpack-dev-server
npm run build    # webpack production

11.2 Node 옵션

"umi-start:op-portal": "... cross-env UMI_ENV=op-portal umi dev"

OpenSSL legacy provider, --max_old_space_size=819200 등 대형 Cesium 프로젝트용 Node 플래그 적용.

11.3 Git Hooks

"husky": { "hooks": { "pre-commit": "lint-staged" } },
"lint-staged": { "*.{js,ts,jsx,tsx,css,md}": "prettier --write" }

12. 기술적 도전과 설계 결정

✅ 잘한 선택

결정효과

Multi-entry UMD + jsonpFunction 분리 MF 동시 로드 충돌 방지
Cesium/React externals 19개 번들 × 5MB → 공유 1회 로드
CommonPanel injection 지도·업무 UI 관심사 분리
BaseClass GIS layer 분석 로직 React lifecycle과 decouple
GeoJSON json-loader 정적 공간 데이터 즉시 import

⚠️ 트레이드오프

이슈현재 상태개선 방향

window.cviewer 글로벌 Shell 의존 강함 Context API / custom hook abstraction
React 18 + ReactDOM.render Legacy API createRoot + concurrent features
Umi 3 + Webpack 4 구세대 스택 Module Federation / Rspack 검토
antd vendoring 업그레이드 비용 Design token + CSS variables
Service key in frontend 보안 리스크 BFF에서 key 관리
1800 LOC component 유지보수 난이도 sub-hook / feature slice 분리

🧪 성능 고려사항

  • Cesium primitive / entity cleanup — MF unmount 시 반드시 remove
  • ScreenSpaceEventHandler.destroy() — 가시권·Draw 모듈 공통
  • gzip + contenthash — 정적 자산 CDN 캐싱
  • BroadcastChannel — MF 간 이벤트 (main.tsx 패턴)

13. 마치며

이 프로젝트는 단순한 "React + 지도"가 아닙니다.

엔터프라이즈 MF 아키텍처 × Cesium 3D GIS × 문화재·토지이용 행정 도메인
이 세 축이 교차하는 고밀도 프론트엔드 시스템입니다.

19개의 독립 엔트리, 80+ 공간분석 BaseClass, 이중 상태관리, externals 기반 번들 전략 —
각각이 대규모 공공 GIS 플랫폼에서 마주치는 실제 문제를 풀기 위한 선택입니다.


 

Built with React · Umi · Cesium · Qiankun · Ant Design

 

📄 Document generated for technical blog purposes · Sensitive identifiers masked
Organization: ▓▓▓▓▓ · Internal project codename: CHDT MF Module