slick-address-kr 1.0.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/API-KEY-GUIDE.md +428 -0
- package/AUTOCOMPLETE-GUIDE.md +399 -0
- package/HOW-TO-USE.md +416 -0
- package/LICENSE +21 -0
- package/README.ko.md +88 -0
- package/README.md +434 -0
- package/config.example.js +16 -0
- package/dist/address-finder.d.ts +68 -0
- package/dist/address-finder.js +262 -0
- package/dist/api-client.d.ts +25 -0
- package/dist/api-client.js +79 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/styles.css +279 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.js +1 -0
- package/index.html +85 -0
- package/korean-address-finder.js +360 -0
- package/package.json +58 -0
package/API-KEY-GUIDE.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
# API 키 관리 가이드
|
|
2
|
+
|
|
3
|
+
웹사이트에서 주소 검색 기능을 사용할 때 API 키를 안전하게 관리하는 방법입니다.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ⚠️ 중요: API 키 보안
|
|
8
|
+
|
|
9
|
+
**문제점:**
|
|
10
|
+
- 프론트엔드 코드에 API 키를 직접 넣으면 누구나 볼 수 있습니다
|
|
11
|
+
- 악의적인 사용자가 키를 복사하여 악용할 수 있습니다
|
|
12
|
+
- API 호출 제한을 초과할 수 있습니다
|
|
13
|
+
|
|
14
|
+
**해결책:**
|
|
15
|
+
프로젝트 규모와 상황에 맞는 방법을 선택하세요.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 방법 1: 백엔드 프록시 서버 (⭐ 가장 안전, 추천)
|
|
20
|
+
|
|
21
|
+
API 키를 서버에만 저장하고, 클라이언트는 자체 서버를 통해 주소 API를 호출합니다.
|
|
22
|
+
|
|
23
|
+
### 장점
|
|
24
|
+
- ✅ API 키 완전 보호
|
|
25
|
+
- ✅ 요청 제한 관리 가능
|
|
26
|
+
- ✅ 로깅 및 모니터링 가능
|
|
27
|
+
- ✅ 악의적 사용 차단 가능
|
|
28
|
+
|
|
29
|
+
### 단점
|
|
30
|
+
- ❌ 백엔드 서버 필요
|
|
31
|
+
- ❌ 서버 관리 비용
|
|
32
|
+
|
|
33
|
+
### 구현 예제
|
|
34
|
+
|
|
35
|
+
#### Node.js + Express
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
// server.js
|
|
39
|
+
const express = require('express');
|
|
40
|
+
const axios = require('axios');
|
|
41
|
+
const cors = require('cors');
|
|
42
|
+
|
|
43
|
+
const app = express();
|
|
44
|
+
app.use(cors());
|
|
45
|
+
app.use(express.json());
|
|
46
|
+
|
|
47
|
+
// 환경 변수에서 API 키 로드 (.env 파일)
|
|
48
|
+
const JUSO_API_KEY = process.env.JUSO_API_KEY;
|
|
49
|
+
|
|
50
|
+
// 프록시 엔드포인트
|
|
51
|
+
app.get('/api/address/search', async (req, res) => {
|
|
52
|
+
const { keyword, currentPage = 1, countPerPage = 10 } = req.query;
|
|
53
|
+
|
|
54
|
+
// 입력 검증
|
|
55
|
+
if (!keyword) {
|
|
56
|
+
return res.status(400).json({ error: '검색어를 입력하세요' });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
// 행정안전부 API 호출 (서버에서만 API 키 사용)
|
|
61
|
+
const response = await axios.get('https://business.juso.go.kr/addrlink/addrLinkApi.do', {
|
|
62
|
+
params: {
|
|
63
|
+
confmKey: JUSO_API_KEY,
|
|
64
|
+
currentPage,
|
|
65
|
+
countPerPage,
|
|
66
|
+
keyword,
|
|
67
|
+
resultType: 'json',
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// 결과 반환
|
|
72
|
+
res.json(response.data);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('API 호출 오류:', error);
|
|
75
|
+
res.status(500).json({ error: '주소 검색 중 오류가 발생했습니다' });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
app.listen(3000, () => {
|
|
80
|
+
console.log('프록시 서버가 3000번 포트에서 실행 중입니다.');
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### .env 파일
|
|
85
|
+
|
|
86
|
+
```env
|
|
87
|
+
JUSO_API_KEY=your_actual_api_key_here
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### 클라이언트 코드 수정
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// api-client.ts 수정
|
|
94
|
+
export class AddressApiClient {
|
|
95
|
+
private apiUrl = 'http://your-domain.com/api/address/search'; // 본인 서버 URL
|
|
96
|
+
|
|
97
|
+
constructor(apiKey?: string) {
|
|
98
|
+
// apiKey 파라미터는 사용하지 않음 (서버에서 관리)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async search(keyword: string, currentPage: number = 1, countPerPage: number = 10) {
|
|
102
|
+
const params = new URLSearchParams({
|
|
103
|
+
keyword: keyword.trim(),
|
|
104
|
+
currentPage: currentPage.toString(),
|
|
105
|
+
countPerPage: countPerPage.toString(),
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const response = await fetch(`${this.apiUrl}?${params.toString()}`);
|
|
109
|
+
const data = await response.json();
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
results: data.results.juso.map(/* ... */),
|
|
113
|
+
totalCount: parseInt(data.results.common.totalCount, 10),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 방법 2: 환경 변수 사용 (빌드 시)
|
|
122
|
+
|
|
123
|
+
Webpack, Vite 등 번들러를 사용하여 빌드 시 API 키를 주입합니다.
|
|
124
|
+
|
|
125
|
+
### 장점
|
|
126
|
+
- ✅ 소스 코드에 하드코딩하지 않음
|
|
127
|
+
- ✅ 환경별로 다른 키 사용 가능
|
|
128
|
+
- ✅ 비교적 간단한 구현
|
|
129
|
+
|
|
130
|
+
### 단점
|
|
131
|
+
- ⚠️ 빌드된 파일에는 키가 포함됨
|
|
132
|
+
- ⚠️ 개발자 도구로 확인 가능
|
|
133
|
+
- ⚠️ 완전한 보안은 아님
|
|
134
|
+
|
|
135
|
+
### React (Create React App)
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# .env 파일
|
|
139
|
+
REACT_APP_JUSO_API_KEY=your_api_key_here
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// App.js
|
|
144
|
+
const finder = new KoreanAddressFinder({
|
|
145
|
+
apiKey: process.env.REACT_APP_JUSO_API_KEY,
|
|
146
|
+
onSelect: (address) => console.log(address)
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Vue
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# .env 파일
|
|
154
|
+
VUE_APP_JUSO_API_KEY=your_api_key_here
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
// main.js
|
|
159
|
+
const finder = new KoreanAddressFinder({
|
|
160
|
+
apiKey: process.env.VUE_APP_JUSO_API_KEY,
|
|
161
|
+
onSelect: (address) => console.log(address)
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Vite
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# .env 파일
|
|
169
|
+
VITE_JUSO_API_KEY=your_api_key_here
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
const finder = new KoreanAddressFinder({
|
|
174
|
+
apiKey: import.meta.env.VITE_JUSO_API_KEY,
|
|
175
|
+
onSelect: (address) => console.log(address)
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 방법 3: 설정 파일 사용 (간단한 프로젝트용)
|
|
182
|
+
|
|
183
|
+
프로젝트 루트에 설정 파일을 만들고 .gitignore에 추가합니다.
|
|
184
|
+
|
|
185
|
+
### 장점
|
|
186
|
+
- ✅ 구현이 매우 간단
|
|
187
|
+
- ✅ 번들러 없이도 사용 가능
|
|
188
|
+
|
|
189
|
+
### 단점
|
|
190
|
+
- ⚠️ 배포 시 설정 파일 관리 필요
|
|
191
|
+
- ⚠️ 여전히 클라이언트에 노출됨
|
|
192
|
+
|
|
193
|
+
### 구현 예제
|
|
194
|
+
|
|
195
|
+
#### config.js (Git에 올리지 않음)
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
// config.js
|
|
199
|
+
export const config = {
|
|
200
|
+
jusoApiKey: 'your_actual_api_key_here'
|
|
201
|
+
};
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### config.example.js (Git에 올림, 템플릿)
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
// config.example.js
|
|
208
|
+
export const config = {
|
|
209
|
+
jusoApiKey: 'YOUR_API_KEY_HERE' // 이 파일을 config.js로 복사하고 실제 키를 입력하세요
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### .gitignore
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
config.js
|
|
217
|
+
.env
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### 사용 방법
|
|
221
|
+
|
|
222
|
+
```javascript
|
|
223
|
+
// main.js
|
|
224
|
+
import { config } from './config.js';
|
|
225
|
+
import { KoreanAddressFinder } from 'korean-address-finder';
|
|
226
|
+
|
|
227
|
+
const finder = new KoreanAddressFinder({
|
|
228
|
+
apiKey: config.jusoApiKey,
|
|
229
|
+
onSelect: (address) => console.log(address)
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
finder.init();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 방법 4: 도메인 제한 (행정안전부 API 기능)
|
|
238
|
+
|
|
239
|
+
행정안전부 주소 API는 **승인 시 등록한 도메인에서만** 작동하도록 설정 가능합니다.
|
|
240
|
+
|
|
241
|
+
### 설정 방법
|
|
242
|
+
|
|
243
|
+
1. [주소기반산업지원서비스](https://business.juso.go.kr/) 로그인
|
|
244
|
+
2. 승인키 신청/관리
|
|
245
|
+
3. **사용 도메인 등록** (예: `https://yourdomain.com`)
|
|
246
|
+
4. 저장
|
|
247
|
+
|
|
248
|
+
### 장점
|
|
249
|
+
- ✅ 다른 도메인에서 키 악용 불가능
|
|
250
|
+
- ✅ 추가 코드 작업 불필요
|
|
251
|
+
|
|
252
|
+
### 단점
|
|
253
|
+
- ⚠️ localhost에서 테스트 시 별도 개발용 키 필요
|
|
254
|
+
- ⚠️ 도메인 변경 시 재등록 필요
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 권장 방법 (프로젝트별)
|
|
259
|
+
|
|
260
|
+
### 개인 프로젝트 / 작은 웹사이트
|
|
261
|
+
→ **방법 3 (설정 파일) + 방법 4 (도메인 제한)**
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
// config.js (gitignore에 추가)
|
|
265
|
+
export const config = {
|
|
266
|
+
jusoApiKey: 'devU01TX0FVVEgyMDI1MDExMDE1...'
|
|
267
|
+
};
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 중소 규모 프로젝트
|
|
271
|
+
→ **방법 2 (환경 변수) + 방법 4 (도메인 제한)**
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# .env
|
|
275
|
+
REACT_APP_JUSO_API_KEY=your_key
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 대규모 / 상용 서비스
|
|
279
|
+
→ **방법 1 (백엔드 프록시) ⭐ 필수**
|
|
280
|
+
|
|
281
|
+
서버에서 API 키 관리, 요청 제한, 로깅 구현
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## 실전 예제: 간단한 웹사이트
|
|
286
|
+
|
|
287
|
+
### 파일 구조
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
my-website/
|
|
291
|
+
├── index.html
|
|
292
|
+
├── config.js # API 키 포함 (gitignore)
|
|
293
|
+
├── config.example.js # 템플릿 (git에 포함)
|
|
294
|
+
├── main.js
|
|
295
|
+
└── .gitignore
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### config.example.js
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
export const config = {
|
|
302
|
+
// 이 파일을 config.js로 복사하고 아래에 실제 API 키를 입력하세요
|
|
303
|
+
// 발급 방법: https://business.juso.go.kr/
|
|
304
|
+
jusoApiKey: 'YOUR_API_KEY_HERE'
|
|
305
|
+
};
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### config.js (실제 사용, Git에 올리지 않음)
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
export const config = {
|
|
312
|
+
jusoApiKey: 'devU01TX0FVVEgyMDI1MDExMDE1MTY0MTExNTM2MjY='
|
|
313
|
+
};
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### .gitignore
|
|
317
|
+
|
|
318
|
+
```
|
|
319
|
+
config.js
|
|
320
|
+
node_modules/
|
|
321
|
+
.env
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### main.js
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
import { config } from './config.js';
|
|
328
|
+
import { KoreanAddressFinder } from 'korean-address-finder';
|
|
329
|
+
|
|
330
|
+
const finder = new KoreanAddressFinder({
|
|
331
|
+
apiKey: config.jusoApiKey,
|
|
332
|
+
onSelect: (address) => {
|
|
333
|
+
console.log('선택된 주소:', address);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
finder.init();
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 배포 시
|
|
341
|
+
|
|
342
|
+
1. 서버에 `config.js` 파일 직접 생성
|
|
343
|
+
2. 실제 API 키 입력
|
|
344
|
+
3. 파일 권한 설정 (읽기 전용)
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 보안 체크리스트
|
|
349
|
+
|
|
350
|
+
배포 전 확인:
|
|
351
|
+
|
|
352
|
+
- [ ] `.gitignore`에 `config.js`, `.env` 추가
|
|
353
|
+
- [ ] GitHub/GitLab에 실제 API 키 업로드 안 됨
|
|
354
|
+
- [ ] API 키에 도메인 제한 설정
|
|
355
|
+
- [ ] 가능하면 백엔드 프록시 사용
|
|
356
|
+
- [ ] 환경 변수는 빌드 서버에만 설정
|
|
357
|
+
- [ ] API 사용량 모니터링 설정
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 추가 보안 팁
|
|
362
|
+
|
|
363
|
+
### 1. API 사용량 제한
|
|
364
|
+
|
|
365
|
+
```javascript
|
|
366
|
+
// 간단한 Rate Limiting
|
|
367
|
+
let requestCount = 0;
|
|
368
|
+
let lastResetTime = Date.now();
|
|
369
|
+
|
|
370
|
+
async function searchWithLimit(keyword) {
|
|
371
|
+
const now = Date.now();
|
|
372
|
+
|
|
373
|
+
// 1분마다 리셋
|
|
374
|
+
if (now - lastResetTime > 60000) {
|
|
375
|
+
requestCount = 0;
|
|
376
|
+
lastResetTime = now;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 분당 10회 제한
|
|
380
|
+
if (requestCount >= 10) {
|
|
381
|
+
alert('너무 많은 요청입니다. 잠시 후 다시 시도하세요.');
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
requestCount++;
|
|
386
|
+
return await apiClient.search(keyword);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### 2. 디바운싱으로 불필요한 요청 감소
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
const finder = new KoreanAddressFinder({
|
|
394
|
+
apiKey: config.jusoApiKey,
|
|
395
|
+
autocompleteMode: true, // 자동으로 300ms 디바운싱 적용됨
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 문제 해결
|
|
402
|
+
|
|
403
|
+
### Q: API 키가 노출되면 어떻게 하나요?
|
|
404
|
+
|
|
405
|
+
1. 즉시 [주소기반산업지원서비스](https://business.juso.go.kr/)에서 키 재발급
|
|
406
|
+
2. 이전 키 비활성화
|
|
407
|
+
3. 새 키로 교체
|
|
408
|
+
|
|
409
|
+
### Q: localhost에서 테스트하려면?
|
|
410
|
+
|
|
411
|
+
1. 개발용 API 키 별도 발급
|
|
412
|
+
2. 도메인에 `localhost` 또는 `127.0.0.1` 등록
|
|
413
|
+
3. 또는 도메인 제한 없는 키 사용 (개발 시에만)
|
|
414
|
+
|
|
415
|
+
### Q: 여러 도메인에서 사용하려면?
|
|
416
|
+
|
|
417
|
+
- 도메인마다 별도 키 발급
|
|
418
|
+
- 또는 와일드카드 지원 여부 확인 (API 제공자에 문의)
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## 마무리
|
|
423
|
+
|
|
424
|
+
**개인/소규모 프로젝트**: 설정 파일 + 도메인 제한으로 충분합니다.
|
|
425
|
+
|
|
426
|
+
**상용 서비스**: 반드시 백엔드 프록시를 구현하세요!
|
|
427
|
+
|
|
428
|
+
더 자세한 내용은 프로젝트의 `SECURITY.md` 파일을 참조하세요.
|