🌐 3D GIS 위 문화재·토지이용 특화 서비스
마이크로프론트엔드로 구현한 공간정보 특화 애플리케이션 아키텍처
| React 18 | Umi 3 | Cesium 1.115 | Qiankun 2 | Ant Design 4 | |:---:|:---:|:---:|:---::|
한 줄 요약
단일 SPA가 아닌 19개의 독립 엔트리 × UMD 번들로 쪼개진 마이크로프론트엔드.
Cesium 3D 뷰어 위에 문화재 검색·가시권 분석·허가·토지이용 등 행정 GIS 업무를 얹은 엔터프라이즈 프론트엔드.
📑 목차
- 프로젝트 개요
- 아키텍처 한눈에 보기
- 마이크로프론트엔드 설계 — Qiankun + Multi-Entry
- 빌드 파이프라인 — Umi × Webpack 커스터마이징
- 3D GIS 엔진 — Cesium 통합 전략
- 상태 관리 — DVA + Rematch 이중 구조
- 도메인 모듈 맵 — 19개 마이크로 앱
- 공간분석 BaseClass 레이어
- API·프록시 아키텍처
- 테마·스타일 시스템
- 개발·배포 워크플로우
- 기술적 도전과 설계 결정
- 마치며
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가 담당합니다.
알고리즘 흐름:
- ScreenSpaceEventHandler로 관측점·목표점 2점 클릭
- CustomDataSource('visibilityanalysis')에 폴리라인·라벨 entity 추가
- 3D Tileset / Terrain과 ray intersection → 가시·비가시 구간 색상 분리
- 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 실행:
- public/styles/component.less 초기화
- src/**/*.theme.less glob 탐색
- 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
'3D' 카테고리의 다른 글
| [Frontend] CSS의 Layout, Paint, Composite 단계와 리플로우 최적화 (0) | 2026.07.07 |
|---|---|
| [3D] Cesium Terrain Server 학습 (0) | 2026.07.04 |
| [3D] Cesium Terrain Builder (0) | 2026.07.04 |
| [3D] Gaussian Splatting 기술 학습 (0) | 2026.07.03 |
| [3D] ml-sharp 기술 학습 (0) | 2026.07.03 |