kor-mapi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dylan Yi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.eng.md ADDED
@@ -0,0 +1,124 @@
1
+ # kor-mapi
2
+
3
+ [한국어](README.md) | [English](README.eng.md)
4
+
5
+ A provider-agnostic Korean map facade that abstracts **Naver Maps**, **Kakao Maps**, and **Google Maps** behind a single unified TypeScript API.
6
+
7
+ ## Why
8
+
9
+ South Korea restricted Google's map data export for 19 years. That restriction was conditionally lifted in February 2026, and Google Maps is expected to offer full Korean navigation coverage within months. In the meantime, apps serving Korean users have relied on Naver or Kakao — each with their own SDK, coordinate conventions, zoom scales, and event models.
10
+
11
+ `kor-mapi` lets you write map code once and switch providers with a single config change. Three are supported for now, with plans to add others like VWorld in the future as Google's Korean data matures.
12
+
13
+ ## Current Status: v0.1 — Tier 1 Complete
14
+
15
+ The following features are implemented and work identically across all three providers:
16
+
17
+ | # | Feature | API |
18
+ | --- | ------------------------- | ----------------------------------------------------------------------------- |
19
+ | 1 | **Initialization** | `KorMap.create({ provider, container, apiKey, center, zoom })` |
20
+ | 2 | **Camera / viewport** | `setCenter`, `getCenter`, `setZoom`, `getZoom`, `fitBounds`, `panTo`, `panBy` |
21
+ | 3 | **Map types** | `setMapType(MapTypeId.ROADMAP \| SATELLITE \| HYBRID \| TERRAIN)` |
22
+ | 4 | **Markers** | `new Marker({ position, icon, title, draggable, opacity, zIndex })` |
23
+ | 5 | **InfoWindows** | `new InfoWindow({ content })` → `open(map, anchor?)` / `close()` |
24
+ | 6 | **Vector overlays** | `Polyline`, `Polygon`, `Circle`, `Rectangle` with unified stroke/fill styles |
25
+ | 7 | **Events** | `map.on('click' \| 'zoom_changed' \| 'center_changed' \| 'idle' \| ...)` |
26
+ | 8 | **Custom tile layers** | `new TileLayer({ getTileUrl(coord, zoom) })` — works with VWORLD etc. |
27
+ | 9 | **Marker clustering** | `new MarkerClusterer(map, markers, options)` — grid-based, zero dependencies |
28
+
29
+ **Provider-specific notes:**
30
+
31
+ - Zoom is normalized to the Naver/Google standard of 7–19. Kakao's inverse 1–13 scale is internally converted. Note that the zoom levels of the three maps are not perfectly identical, so some variance is expected.
32
+ - Kakao's `idle` event (missing from the SDK) is synthesized via a 150ms debounce on `center_changed` + `zoom_changed`.
33
+ - Google markers use `AdvancedMarkerElement` (the current non-deprecated API). A `mapId` defaults to `'DEMO_MAP_ID'` for development; set `config.mapId` in production.
34
+ - Kakao has no standalone terrain base map — `MapTypeId.TERRAIN` falls back to `ROADMAP`.
35
+ - `map.native` gives direct access to the underlying provider object (`naver.maps.Map`, `kakao.maps.Map`, or `google.maps.Map`).
36
+
37
+ ## Out of Scope: Geocoding, Search, and Routing
38
+
39
+ Geocoding, address search, POI search, and routing are **intentionally not part of the `kor-mapi` unified API**.
40
+
41
+ Each provider has fundamentally different service APIs — different data models, result structures, transport mechanisms (client SDK vs. REST-only), and Korean-specific data quality. A common-denominator facade would force lossy type conversions and hide provider capabilities that users actually care about. Naver and Kakao routing is also REST-only and requires a server-side proxy, making it a different integration concern from map rendering.
42
+
43
+ Use `map.native` to access provider services directly:
44
+
45
+ ```ts
46
+ // Kakao — best for Korean addresses and POI
47
+ const map = await KorMap.create<'kakao'>({ provider: 'kakao', ... });
48
+ const geocoder = new window.kakao.maps.services.Geocoder();
49
+ const places = new window.kakao.maps.services.Places();
50
+
51
+ // Google — global coverage
52
+ const map = await KorMap.create<'google'>({ provider: 'google', ... });
53
+ const geocoder = new window.google.maps.Geocoder();
54
+ ```
55
+
56
+ ## Not Yet Implemented (Tier 2 / v0.2+)
57
+
58
+ Traffic layer, heatmap, drawing tools, map styles, elevation, street view, tilt/heading, multi-language switching, transit layer, administrative boundary overlays, and routing.
59
+
60
+ ## Usage
61
+
62
+ ```ts
63
+ import { KorMap, Marker, InfoWindow, MapTypeId } from "kor-mapi";
64
+
65
+ const map = await KorMap.create({
66
+ provider: "kakao", // 'naver' | 'kakao' | 'google'
67
+ container: "map",
68
+ apiKey: "YOUR_APP_KEY",
69
+ center: { lat: 37.5665, lng: 126.978 },
70
+ zoom: 12,
71
+ });
72
+
73
+ const marker = new Marker({ position: { lat: 37.5665, lng: 126.978 } });
74
+ marker.setMap(map);
75
+
76
+ const iw = new InfoWindow({ content: "<b>Seoul City Hall</b>" });
77
+ marker.on("click", () => iw.open(map, marker));
78
+
79
+ map.on("zoom_changed", () => console.log(map.getZoom()));
80
+ ```
81
+
82
+ Switch to Naver or Google by changing `provider` — everything else stays the same.
83
+
84
+ ## Demo
85
+
86
+ Since all three providers require API keys, register your app in each developer console, obtain keys, and set them in the `.env` file.
87
+
88
+ ```bash
89
+ cp demo/.env.example demo/.env
90
+ # fill in one or more API keys
91
+ npm run demo
92
+ # open http://localhost:3000
93
+ ```
94
+
95
+ The demo shows all three providers side-by-side with synchronized camera, shared toolbar controls (map type, marker, polyline, circle), and a unified event log.
96
+
97
+ ## Architecture
98
+
99
+ Stratified Adapter pattern: `KorMap` facade → `IMapProvider` interface → `NaverAdapter` / `KakaoAdapter` / `GoogleAdapter`. Each adapter handles SDK loading (dynamic `<script>` injection), coordinate translation, event name mapping, and provider-specific quirks. Zero production dependencies — all three SDKs are loaded at runtime.
100
+
101
+ ```
102
+ src/
103
+ core/
104
+ KorMap.ts ← facade + create() factory
105
+ types.ts ← all public interfaces and enums
106
+ overlays.ts ← Marker, InfoWindow, Polyline, Polygon, Circle, Rectangle, TileLayer
107
+ errors.ts ← KorMapError hierarchy
108
+ providers/
109
+ base/ ← IMapProvider interface
110
+ naver/ ← NaverAdapter + NaverLoader
111
+ kakao/ ← KakaoAdapter + KakaoLoader
112
+ google/ ← GoogleAdapter + GoogleLoader
113
+ utils/
114
+ coordinateUtils.ts ← zoom normalization across providers
115
+ ```
116
+
117
+ ## Commands
118
+
119
+ ```bash
120
+ npm run build # build with tsup
121
+ npm test # run tests (vitest)
122
+ npm run typecheck # tsc --noEmit
123
+ npm run demo # Vite dev server at localhost:3000
124
+ ```
package/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # kor-mapi
2
+
3
+ [한국어](README.md) | [English](README.eng.md)
4
+
5
+ **네이버 지도**, **카카오맵**, **구글 지도** API를 하나의 API로 추상화한 한국 지도 파사드입니다.
6
+
7
+ ## 배경
8
+
9
+ 한국은 19년간 구글에 대한 지도 데이터 반출을 제한했는데 이 제한이 2026년 2월에 조건부로 해제됐고 구글 지도는 수개월 내에 한국 지도 완성도를 상당히 높일 것으로 예상됩니다. 그동안 한국 사용자를 대상으로 하는 앱들은 각자 다른 SDK의 네이버, 카카오, 구글 등에 의존해왔는데 개인적으로 이러한 난립은 개발자에게 도움이 되지 않는다고 생각되어 이 프로젝트를 만들게 됐습니다.
10
+
11
+ `kor-mapi`를 사용하면 지도 코드를 한 번만 작성하고 설정 하나만 바꿔 공급자를 전환할 수 있습니다. 구글 지도 완성도를 고려하여 세 가지를 지원하고 있으나 향후 VWorld 등 다른
12
+ 공급자로도 확대할 생각이 있습니다.
13
+
14
+ ## 현재 상태: v0.1 — Tier 1 구현 완료
15
+
16
+ 아래 기능들이 세 공급자 모두에서 동일하게 동작합니다:
17
+
18
+ | # | 기능 | API |
19
+ | --- | ------------------------- | ----------------------------------------------------------------------------- |
20
+ | 1 | **초기화** | `KorMap.create({ provider, container, apiKey, center, zoom })` |
21
+ | 2 | **카메라 / 뷰포트** | `setCenter`, `getCenter`, `setZoom`, `getZoom`, `fitBounds`, `panTo`, `panBy` |
22
+ | 3 | **지도 유형** | `setMapType(MapTypeId.ROADMAP \| SATELLITE \| HYBRID \| TERRAIN)` |
23
+ | 4 | **마커** | `new Marker({ position, icon, title, draggable, opacity, zIndex })` |
24
+ | 5 | **정보창** | `new InfoWindow({ content })` → `open(map, anchor?)` / `close()` |
25
+ | 6 | **벡터 오버레이** | `Polyline`, `Polygon`, `Circle`, `Rectangle` — 통합 선/채우기 스타일 |
26
+ | 7 | **이벤트** | `map.on('click' \| 'zoom_changed' \| 'center_changed' \| 'idle' \| ...)` |
27
+ | 8 | **커스텀 타일 레이어** | `new TileLayer({ getTileUrl(coord, zoom) })` — 브이월드 등 지원 |
28
+ | 9 | **마커 클러스터링** | `new MarkerClusterer(map, markers, options)` — 격자 기반, 외부 의존성 없음 |
29
+
30
+ **공급자별 주요 사항:**
31
+
32
+ - 줌은 네이버/구글을 표준으로 7 ~ 19로 제한했으며 카카오는 역방향 1–13 범위로 내부적으로 변환됩니다. 세 지도의 줌 배율이 완전히 동일하지는 않으므로 약간의 오차는 감안하세요.
33
+ - 카카오 SDK에 없는 `idle` 이벤트는 `center_changed` + `zoom_changed`를 150ms 디바운스하여 합성합니다.
34
+ - 구글 마커는 현재 권고되는 `AdvancedMarkerElement`를 사용합니다. `mapId`는 기본값으로 `'DEMO_MAP_ID'`가 사용되며, 운영 환경에서는 `config.mapId`를 설정하시기 바랍니다.
35
+ - 카카오에는 독립적인 지형도 기본 지도가 없어 `MapTypeId.TERRAIN`은 `ROADMAP`으로 대체됩니다.
36
+ - `map.native`로 기반 공급자 객체(`naver.maps.Map`, `kakao.maps.Map`, `google.maps.Map`)에 직접 접근할 수 있습니다.
37
+
38
+ ## 범위 외: 지오코딩, 검색, 경로 탐색
39
+
40
+ 지오코딩, 주소 검색, 장소/POI 검색, 경로 탐색은 **`kor-mapi` 통합 API에서 의도적으로 제외**됩니다.
41
+
42
+ 각 공급자의 서비스 API는 데이터 모델, 결과 구조, 전송 방식(클라이언트 SDK vs REST 전용)이 근본적으로 다릅니다. 공통 분모 파사드를 만들면 각 공급자가 제공하는 고유한 기능(지번/도로명 주소 상세 정보, POI 카테고리 체계 등)을 숨기고 오히려 개발자에게 불필요한 학습 비용을 강요하게 됩니다. 또한 네이버와 카카오의 경로 탐색은 REST 전용이며 서버 측 프록시가 필요하므로 지도 렌더링과는 다른 통합 방식을 요구합니다.
43
+
44
+ `map.native`를 통해 각 공급자의 서비스에 직접 접근하세요:
45
+
46
+ ```ts
47
+ // 카카오 — 한국 주소 및 POI 검색에 최적
48
+ const map = await KorMap.create<'kakao'>({ provider: 'kakao', ... });
49
+ const geocoder = new window.kakao.maps.services.Geocoder();
50
+ const places = new window.kakao.maps.services.Places();
51
+
52
+ // 구글 — 글로벌 커버리지
53
+ const map = await KorMap.create<'google'>({ provider: 'google', ... });
54
+ const geocoder = new window.google.maps.Geocoder();
55
+ ```
56
+
57
+ ## 미구현 항목 (Tier 2 / v0.2+)
58
+
59
+ 교통 레이어, 히트맵, 그리기 도구, 지도 스타일, 고도, 거리뷰/로드뷰, 기울기/방향, 다국어 전환, 대중교통 레이어, 행정구역 경계 오버레이, 경로 탐색.
60
+
61
+ ## 사용법
62
+
63
+ ```ts
64
+ import { KorMap, Marker, InfoWindow, MapTypeId } from "kor-mapi";
65
+
66
+ const map = await KorMap.create({
67
+ provider: "kakao", // 'naver' | 'kakao' | 'google'
68
+ container: "map",
69
+ apiKey: "YOUR_APP_KEY",
70
+ center: { lat: 37.5665, lng: 126.978 },
71
+ zoom: 12,
72
+ });
73
+
74
+ const marker = new Marker({ position: { lat: 37.5665, lng: 126.978 } });
75
+ marker.setMap(map);
76
+
77
+ const iw = new InfoWindow({ content: "<b>서울시청</b>" });
78
+ marker.on("click", () => iw.open(map, marker));
79
+
80
+ map.on("zoom_changed", () => console.log(map.getZoom()));
81
+ ```
82
+
83
+ 네이버, 카카오, 구글에 따라 `provider` 값만 바꾸면 되며 나머지 코드는 그대로 유지됩니다.
84
+
85
+ ## 데모
86
+
87
+ 각 공급자 모두 API 키가 필요하므로 각 개발 콘솔에서 앱을 등록하고 키를 발급한 다음
88
+ `.env` 파일에 설정하세요.
89
+
90
+ ```bash
91
+ cp demo/.env.example demo/.env
92
+ # .env 파일에 API 키를 하나 이상 설정하세요
93
+ npm run demo
94
+ # http://localhost:3000 에서 확인
95
+ ```
96
+
97
+ 데모는 세 공급자를 나란히 표시하며 위치/배율 동기화, 공통 툴바(지도 유형, 마커, 폴리라인, 원), 통합 이벤트 로그를 제공합니다.
98
+
99
+ ## 아키텍처
100
+
101
+ 계층적 어댑터 패턴: `KorMap` 파사드 → `IMapProvider` 인터페이스 → `NaverAdapter` / `KakaoAdapter` / `GoogleAdapter`. 각 어댑터는 SDK 동적 로딩(`<script>` 주입), 좌표 변환, 이벤트 이름 매핑, 공급자별 특이사항을 처리합니다. 운영 의존성 없음 — 세 SDK 모두 런타임에 동적으로 로드됩니다.
102
+
103
+ ```
104
+ src/
105
+ core/
106
+ KorMap.ts ← 파사드 + create() 팩토리
107
+ types.ts ← 모든 공개 인터페이스 및 열거형
108
+ overlays.ts ← Marker, InfoWindow, Polyline, Polygon, Circle, Rectangle, TileLayer
109
+ errors.ts ← KorMapError 계층
110
+ providers/
111
+ base/ ← IMapProvider 인터페이스
112
+ naver/ ← NaverAdapter + NaverLoader
113
+ kakao/ ← KakaoAdapter + KakaoLoader
114
+ google/ ← GoogleAdapter + GoogleLoader
115
+ utils/
116
+ coordinateUtils.ts ← 공급자 간 줌 정규화
117
+ ```
118
+
119
+ ## 명령어
120
+
121
+ ```bash
122
+ npm run build # tsup으로 빌드
123
+ npm test # 테스트 실행 (vitest)
124
+ npm run typecheck # tsc --noEmit
125
+ npm run demo # localhost:3000에서 Vite 개발 서버 실행
126
+ ```