slick-address-kr 1.0.2 → 1.0.4
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/README.md +30 -3
- package/dist/address-finder.d.ts +4 -0
- package/dist/address-finder.js +18 -7
- package/dist/api-client.js +7 -2
- package/korean-address-finder.js +27 -9
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -17,19 +17,35 @@ Korean address search library using official road address API - No popup require
|
|
|
17
17
|
- ⌨️ **키보드 네비게이션**: 화살표 키로 결과 선택, 엔터로 적용 - 마우스 없이 빠른 입력!
|
|
18
18
|
- 🔒 **TypeScript 지원**: 완전한 타입 정의 제공
|
|
19
19
|
- 🌐 **프레임워크 무관**: Vanilla JS, React, Vue 등 어디서나 사용 가능
|
|
20
|
+
- 📦 **모든 패키지 매니저 지원**: npm, yarn, pnpm, bun, deno 모두 호환
|
|
20
21
|
|
|
21
22
|
## 설치
|
|
22
23
|
|
|
24
|
+
### npm
|
|
23
25
|
```bash
|
|
24
26
|
npm install slick-address-kr
|
|
25
27
|
```
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
### yarn
|
|
29
30
|
```bash
|
|
30
31
|
yarn add slick-address-kr
|
|
31
32
|
```
|
|
32
33
|
|
|
34
|
+
### pnpm
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add slick-address-kr
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### bun
|
|
40
|
+
```bash
|
|
41
|
+
bun add slick-address-kr
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### deno
|
|
45
|
+
```typescript
|
|
46
|
+
import { KoreanAddressFinder } from "npm:slick-address-kr@1.0.4";
|
|
47
|
+
```
|
|
48
|
+
|
|
33
49
|
## 빠른 시작
|
|
34
50
|
|
|
35
51
|
### 1. 기본 사용 (Vanilla JavaScript)
|
|
@@ -414,13 +430,24 @@ MIT License - 자유롭게 사용하세요!
|
|
|
414
430
|
|
|
415
431
|
## 문제 해결
|
|
416
432
|
|
|
433
|
+
### "승인되지 않은 KEY 입니다" 오류가 나요
|
|
434
|
+
|
|
435
|
+
이 라이브러리는 **API 키가 필수**입니다. 키 없이 사용하면 동작하지 않습니다.
|
|
436
|
+
|
|
437
|
+
1. [행정안전부 도로명주소 OpenAPI](https://business.juso.go.kr/addrlink/openApi/searchApi.do)에서 **"검색 API"** 키를 직접 발급받으세요. (팝업 API 아님!)
|
|
438
|
+
2. 발급받은 키를 `apiKey` 옵션으로 전달하세요:
|
|
439
|
+
```js
|
|
440
|
+
new KoreanAddressFinder({ apiKey: '발급받은_본인_키', /* ... */ });
|
|
441
|
+
```
|
|
442
|
+
3. 행정안전부 **개발용(dev) 키는 발급 후 일정 기간이 지나면 만료**됩니다. 만료 시 새로 발급받으세요.
|
|
443
|
+
|
|
417
444
|
### CORS 에러가 발생해요
|
|
418
445
|
|
|
419
446
|
행정안전부 API는 CORS를 지원합니다. 만약 에러가 발생한다면:
|
|
420
447
|
|
|
421
448
|
1. API 키가 올바른지 확인
|
|
422
449
|
2. ⚠️ **중요: API 발급 시 "검색 API"로 발급받았는지 확인** (팝업 API가 아님!)
|
|
423
|
-
3.
|
|
450
|
+
3. **키 발급 시 등록한 도메인에서만 호출이 허용됩니다.** 사용할 도메인(예: `localhost`, 실제 서비스 도메인)을 juso.go.kr에 등록했는지 확인하세요.
|
|
424
451
|
4. HTTPS 사용 권장
|
|
425
452
|
|
|
426
453
|
### 검색 결과가 나오지 않아요
|
package/dist/address-finder.d.ts
CHANGED
package/dist/address-finder.js
CHANGED
|
@@ -91,7 +91,7 @@ export class KoreanAddressFinder {
|
|
|
91
91
|
const searchBtn = this.container.querySelector('.kaf-search-btn');
|
|
92
92
|
searchBtn?.addEventListener('click', () => this.handleSearch());
|
|
93
93
|
// 검색 입력 엔터키
|
|
94
|
-
this.searchInput?.addEventListener('
|
|
94
|
+
this.searchInput?.addEventListener('keydown', (e) => {
|
|
95
95
|
if (e.key === 'Enter') {
|
|
96
96
|
this.handleSearch();
|
|
97
97
|
}
|
|
@@ -156,14 +156,14 @@ export class KoreanAddressFinder {
|
|
|
156
156
|
<div class="kaf-result-item" data-index="${index}">
|
|
157
157
|
<div class="kaf-result-main">
|
|
158
158
|
<span class="kaf-result-type">도로명</span>
|
|
159
|
-
<span class="kaf-result-address">${result.roadAddress}</span>
|
|
159
|
+
<span class="kaf-result-address">${this.escapeHtml(result.roadAddress)}</span>
|
|
160
160
|
</div>
|
|
161
161
|
<div class="kaf-result-sub">
|
|
162
162
|
<span class="kaf-result-type">지번</span>
|
|
163
|
-
<span class="kaf-result-address">${result.jibunAddress}</span>
|
|
163
|
+
<span class="kaf-result-address">${this.escapeHtml(result.jibunAddress)}</span>
|
|
164
164
|
</div>
|
|
165
|
-
<div class="kaf-result-zipcode">우편번호: ${result.zipCode}</div>
|
|
166
|
-
${result.buildingName ? `<div class="kaf-result-building">${result.buildingName}</div>` : ''}
|
|
165
|
+
<div class="kaf-result-zipcode">우편번호: ${this.escapeHtml(result.zipCode)}</div>
|
|
166
|
+
${result.buildingName ? `<div class="kaf-result-building">${this.escapeHtml(result.buildingName)}</div>` : ''}
|
|
167
167
|
</div>
|
|
168
168
|
`).join('');
|
|
169
169
|
// 결과 항목 클릭 이벤트
|
|
@@ -182,10 +182,10 @@ export class KoreanAddressFinder {
|
|
|
182
182
|
selectedContainer.innerHTML = `
|
|
183
183
|
<div class="kaf-selected-content">
|
|
184
184
|
<div class="kaf-selected-main">
|
|
185
|
-
<strong>도로명:</strong> ${address.roadAddress}
|
|
185
|
+
<strong>도로명:</strong> ${this.escapeHtml(address.roadAddress)}
|
|
186
186
|
</div>
|
|
187
187
|
<div class="kaf-selected-sub">
|
|
188
|
-
<strong>우편번호:</strong> ${address.zipCode}
|
|
188
|
+
<strong>우편번호:</strong> ${this.escapeHtml(address.zipCode)}
|
|
189
189
|
</div>
|
|
190
190
|
</div>
|
|
191
191
|
`;
|
|
@@ -235,6 +235,17 @@ export class KoreanAddressFinder {
|
|
|
235
235
|
this.openResults();
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* HTML 특수문자 이스케이프 (XSS 방지)
|
|
240
|
+
*/
|
|
241
|
+
escapeHtml(value) {
|
|
242
|
+
return String(value ?? '')
|
|
243
|
+
.replace(/&/g, '&')
|
|
244
|
+
.replace(/</g, '<')
|
|
245
|
+
.replace(/>/g, '>')
|
|
246
|
+
.replace(/"/g, '"')
|
|
247
|
+
.replace(/'/g, ''');
|
|
248
|
+
}
|
|
238
249
|
/**
|
|
239
250
|
* 컴포넌트 제거
|
|
240
251
|
*/
|
package/dist/api-client.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
export class AddressApiClient {
|
|
6
6
|
constructor(apiKey) {
|
|
7
7
|
this.apiUrl = 'https://business.juso.go.kr/addrlink/addrLinkApi.do';
|
|
8
|
-
// API
|
|
9
|
-
|
|
8
|
+
// API 키는 필수입니다. 행정안전부 도로명주소 사이트에서 직접 발급받아야 합니다.
|
|
9
|
+
// https://business.juso.go.kr/addrlink/openApi/searchApi.do
|
|
10
|
+
this.confmKey = apiKey || '';
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
13
|
* 주소 검색
|
|
@@ -19,6 +20,10 @@ export class AddressApiClient {
|
|
|
19
20
|
if (!keyword || keyword.trim().length === 0) {
|
|
20
21
|
return { results: [], totalCount: 0 };
|
|
21
22
|
}
|
|
23
|
+
if (!this.confmKey) {
|
|
24
|
+
throw new Error('API 키가 설정되지 않았습니다. KoreanAddressFinder 생성 시 apiKey 옵션을 전달하세요. ' +
|
|
25
|
+
'키 발급: https://business.juso.go.kr/addrlink/openApi/searchApi.do');
|
|
26
|
+
}
|
|
22
27
|
try {
|
|
23
28
|
const params = new URLSearchParams({
|
|
24
29
|
confmKey: this.confmKey,
|
package/korean-address-finder.js
CHANGED
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
// API 설정
|
|
15
15
|
const API_URL = 'https://business.juso.go.kr/addrlink/addrLinkApi.do';
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* HTML 특수문자 이스케이프 (XSS 방지)
|
|
19
|
+
*/
|
|
20
|
+
function escapeHtml(value) {
|
|
21
|
+
return String(value == null ? '' : value)
|
|
22
|
+
.replace(/&/g, '&')
|
|
23
|
+
.replace(/</g, '<')
|
|
24
|
+
.replace(/>/g, '>')
|
|
25
|
+
.replace(/"/g, '"')
|
|
26
|
+
.replace(/'/g, ''');
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
/**
|
|
18
30
|
* 주소 검색 클래스
|
|
19
31
|
*/
|
|
@@ -28,7 +40,10 @@
|
|
|
28
40
|
this.debounceTimer = null; // 디바운스 타이머
|
|
29
41
|
|
|
30
42
|
if (!this.apiKey) {
|
|
31
|
-
console.warn(
|
|
43
|
+
console.warn(
|
|
44
|
+
'⚠️ API 키가 설정되지 않았습니다. 행정안전부 도로명주소 사이트에서 "검색 API" 키를 발급받아 apiKey 옵션으로 전달하세요.\n' +
|
|
45
|
+
'키 발급: https://business.juso.go.kr/addrlink/openApi/searchApi.do'
|
|
46
|
+
);
|
|
32
47
|
}
|
|
33
48
|
}
|
|
34
49
|
|
|
@@ -203,7 +218,7 @@
|
|
|
203
218
|
button.addEventListener('click', () => this.search(input.value));
|
|
204
219
|
|
|
205
220
|
// 엔터키 검색
|
|
206
|
-
input.addEventListener('
|
|
221
|
+
input.addEventListener('keydown', (e) => {
|
|
207
222
|
if (e.key === 'Enter') {
|
|
208
223
|
this.search(input.value);
|
|
209
224
|
}
|
|
@@ -300,17 +315,20 @@
|
|
|
300
315
|
|
|
301
316
|
let html = '';
|
|
302
317
|
addresses.forEach(addr => {
|
|
318
|
+
const zipNo = escapeHtml(addr.zipNo);
|
|
319
|
+
const roadAddr = escapeHtml(addr.roadAddr);
|
|
320
|
+
const jibunAddr = escapeHtml(addr.jibunAddr);
|
|
303
321
|
html += `
|
|
304
|
-
<div class="kaf-result-item" data-zipno="${
|
|
322
|
+
<div class="kaf-result-item" data-zipno="${zipNo}" data-road="${roadAddr}" data-jibun="${jibunAddr}">
|
|
305
323
|
<div>
|
|
306
324
|
<span class="kaf-result-type">도로명</span>
|
|
307
|
-
<span class="kaf-result-address">${
|
|
325
|
+
<span class="kaf-result-address">${roadAddr}</span>
|
|
308
326
|
</div>
|
|
309
327
|
<div style="margin-top: 4px;">
|
|
310
328
|
<span class="kaf-result-type" style="background-color: #999;">지번</span>
|
|
311
|
-
<span class="kaf-result-address">${
|
|
329
|
+
<span class="kaf-result-address">${jibunAddr}</span>
|
|
312
330
|
</div>
|
|
313
|
-
<div class="kaf-result-zipcode">우편번호: ${
|
|
331
|
+
<div class="kaf-result-zipcode">우편번호: ${zipNo}</div>
|
|
314
332
|
</div>
|
|
315
333
|
`;
|
|
316
334
|
});
|
|
@@ -339,9 +357,9 @@
|
|
|
339
357
|
|
|
340
358
|
selectedDiv.innerHTML = `
|
|
341
359
|
<h3>✅ 선택된 주소</h3>
|
|
342
|
-
<p><strong>우편번호:</strong> ${address.zipCode}</p>
|
|
343
|
-
<p><strong>도로명 주소:</strong> ${address.roadAddress}</p>
|
|
344
|
-
<p><strong>지번 주소:</strong> ${address.jibunAddress}</p>
|
|
360
|
+
<p><strong>우편번호:</strong> ${escapeHtml(address.zipCode)}</p>
|
|
361
|
+
<p><strong>도로명 주소:</strong> ${escapeHtml(address.roadAddress)}</p>
|
|
362
|
+
<p><strong>지번 주소:</strong> ${escapeHtml(address.jibunAddress)}</p>
|
|
345
363
|
`;
|
|
346
364
|
|
|
347
365
|
selectedDiv.style.display = 'block';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slick-address-kr",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "행정안전부 도로명주소 API 기반 팝업 없는 한국 주소 검색 컴포넌트 (자동완성 지원) - Korean address search with autocomplete using official road address API",
|
|
5
5
|
"main": "korean-address-finder.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
"행정안전부",
|
|
28
28
|
"daum-address",
|
|
29
29
|
"address-finder",
|
|
30
|
-
"road-address"
|
|
30
|
+
"road-address",
|
|
31
|
+
"pnpm",
|
|
32
|
+
"bun",
|
|
33
|
+
"deno",
|
|
34
|
+
"keyboard-navigation"
|
|
31
35
|
],
|
|
32
36
|
"author": "Devguru-J",
|
|
33
37
|
"license": "MIT",
|
|
@@ -49,7 +53,7 @@
|
|
|
49
53
|
"config.example.js",
|
|
50
54
|
"index.html",
|
|
51
55
|
"README.md",
|
|
52
|
-
"README
|
|
56
|
+
"README.ko.md",
|
|
53
57
|
"HOW-TO-USE.md",
|
|
54
58
|
"API-KEY-GUIDE.md",
|
|
55
59
|
"AUTOCOMPLETE-GUIDE.md",
|