slick-address-kr 1.0.4 → 2.0.1

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 CHANGED
@@ -7,16 +7,21 @@ Korean address search library using official road address API - No popup require
7
7
  [![npm version](https://img.shields.io/npm/v/slick-address-kr.svg)](https://www.npmjs.com/package/slick-address-kr)
8
8
  [![license](https://img.shields.io/npm/l/slick-address-kr.svg)](https://github.com/Devguru-J/slick-address-kr/blob/main/LICENSE)
9
9
 
10
+ > v1에서 올라오는 경우 [docs/MIGRATION-v2.md](./docs/MIGRATION-v2.md)를 먼저 읽으세요.
11
+
10
12
  ## 특징
11
13
 
12
14
  - ✨ **팝업 없는 인라인 UI**: 다음 주소 API처럼 새 창이 열리지 않습니다
13
15
  - 🚀 **가벼운 용량**: 외부 의존성 없이 순수 TypeScript로 작성
14
16
  - 📱 **반응형 디자인**: 모바일부터 데스크톱까지 완벽 지원
15
- - 🎨 **커스터마이징 가능**: CSS로 쉽게 스타일 변경 가능
17
+ - 🎨 **커스터마이징 가능**: CSS 변수와 클래스로 쉽게 스타일 변경 가능
18
+ - 🌗 **다크모드 지원**: `--kaf-*` CSS 변수와 `prefers-color-scheme` 대응
16
19
  - ⚡ **자동완성 모드**: 타이핑하는 즉시 검색 결과 표시
17
20
  - ⌨️ **키보드 네비게이션**: 화살표 키로 결과 선택, 엔터로 적용 - 마우스 없이 빠른 입력!
21
+ - ♿ **스크린리더 지원**: ARIA combobox 패턴 적용
18
22
  - 🔒 **TypeScript 지원**: 완전한 타입 정의 제공
19
- - 🌐 **프레임워크 무관**: Vanilla JS, React, Vue 등 어디서나 사용 가능
23
+ - 🌐 **프레임워크 무관**: Vanilla JS는 물론, React/Vue 전용 어댑터 제공
24
+ - 🔌 **자체 프록시 연동**: `endpoint`/`fetchFn` 옵션으로 API 키를 서버에 숨길 수 있음
20
25
  - 📦 **모든 패키지 매니저 지원**: npm, yarn, pnpm, bun, deno 모두 호환
21
26
 
22
27
  ## 설치
@@ -43,21 +48,30 @@ bun add slick-address-kr
43
48
 
44
49
  ### deno
45
50
  ```typescript
46
- import { KoreanAddressFinder } from "npm:slick-address-kr@1.0.4";
51
+ import { KoreanAddressFinder } from "npm:slick-address-kr@2";
47
52
  ```
48
53
 
54
+ > **TypeScript 사용자 참고:** `moduleResolution: "node"`(node10) 설정은
55
+ > 지원하지 않습니다. `"bundler"`, `"node16"`, `"nodenext"` 중 하나를
56
+ > 쓰세요 — 이 패키지는 `exports` 필드 기반 타입 해석만 제공합니다.
57
+
49
58
  ## 빠른 시작
50
59
 
51
60
  ### 1. 기본 사용 (Vanilla JavaScript)
52
61
 
53
62
  **간단한 방법:**
54
63
 
55
- 1. 프로젝트 폴더의 `examples/index.html` 파일을 참조하세요
56
- 2. 로컬 서버 실행:
64
+ 1. 라이브러리를 빌드합니다 (`examples/`는 `../dist/index.js`를 직접 참조하며,
65
+ `dist/`는 git에 커밋되지 않습니다):
66
+ ```bash
67
+ npm run build
68
+ ```
69
+ 2. 프로젝트 폴더의 `examples/index.html` 파일을 참조하세요
70
+ 3. 로컬 서버 실행:
57
71
  ```bash
58
72
  python -m http.server 8000
59
73
  ```
60
- 3. 브라우저에서 열기:
74
+ 4. 브라우저에서 열기:
61
75
  ```
62
76
  http://localhost:8000/examples/
63
77
  ```
@@ -70,101 +84,107 @@ import { KoreanAddressFinder } from "npm:slick-address-kr@1.0.4";
70
84
  <head>
71
85
  <meta charset="UTF-8">
72
86
  <title>주소 검색</title>
87
+ <link rel="stylesheet" href="node_modules/slick-address-kr/dist/styles.css">
73
88
  </head>
74
89
  <body>
75
90
  <div id="address-search"></div>
76
91
 
77
- <script type="module">
78
- // config.js에서 API 키 로드
79
- import { config } from './config.js';
80
-
81
- // 주소 검색 API 호출 함수
82
- async function searchAddress(keyword) {
83
- const response = await fetch(
84
- `https://business.juso.go.kr/addrlink/addrLinkApi.do?` +
85
- `confmKey=${config.jusoApiKey}&keyword=${keyword}&resultType=json`
86
- );
87
- const data = await response.json();
88
- return data.results.juso;
89
- }
90
-
91
- // 검색 UI 구현...
92
+ <script src="node_modules/slick-address-kr/dist/browser.global.js"></script>
93
+ <script>
94
+ // 전역 이름은 window.KoreanAddressFinder다
95
+ const finder = new KoreanAddressFinder({
96
+ apiKey: 'YOUR_API_KEY',
97
+ autocompleteMode: true,
98
+ onSelect: (address, meta) => {
99
+ console.log(meta.reason, address);
100
+ },
101
+ });
102
+
103
+ finder.init(document.getElementById('address-search'));
92
104
  </script>
93
105
  </body>
94
106
  </html>
95
107
  ```
96
108
 
109
+ ESM 환경(모듈 번들러)에서는 다음처럼 씁니다.
110
+
111
+ ```js
112
+ import { KoreanAddressFinder } from 'slick-address-kr';
113
+ import 'slick-address-kr/styles.css';
114
+
115
+ const finder = new KoreanAddressFinder({
116
+ apiKey: 'YOUR_API_KEY',
117
+ onSelect: (address) => console.log(address),
118
+ });
119
+
120
+ finder.init(document.getElementById('address-search'));
121
+ ```
122
+
123
+ CommonJS로 `require`하는 경우, `require('slick-address-kr')`는 이름공간 객체를
124
+ 돌려주므로 `.default`나 이름이 붙은 export를 꺼내 써야 합니다.
125
+
126
+ ```js
127
+ const { KoreanAddressFinder } = require('slick-address-kr');
128
+ // 또는
129
+ const KoreanAddressFinder = require('slick-address-kr').default;
130
+ ```
131
+
97
132
  ### 2. React에서 사용
98
133
 
134
+ 전용 훅을 쓰는 것을 권장합니다.
135
+
99
136
  ```jsx
100
- import React, { useEffect, useRef } from 'react';
101
- import { KoreanAddressFinder } from 'korean-address-finder';
102
- import 'korean-address-finder/dist/styles.css';
103
-
104
- function AddressInput() {
105
- const containerRef = useRef(null);
106
- const finderRef = useRef(null);
107
-
108
- useEffect(() => {
109
- if (containerRef.current && !finderRef.current) {
110
- finderRef.current = new KoreanAddressFinder({
111
- onSelect: (address) => {
112
- console.log('선택된 주소:', address);
113
- }
114
- });
115
-
116
- finderRef.current.init(containerRef.current);
117
- }
118
-
119
- return () => {
120
- if (finderRef.current) {
121
- finderRef.current.destroy();
122
- }
123
- };
124
- }, []);
125
-
126
- return <div ref={containerRef}></div>;
137
+ import { useKoreanAddress } from 'slick-address-kr/react';
138
+ import 'slick-address-kr/styles.css';
139
+
140
+ function AddressForm() {
141
+ const { containerRef, address } = useKoreanAddress({
142
+ apiKey: import.meta.env.VITE_JUSO_API_KEY,
143
+ autocompleteMode: true,
144
+ });
145
+
146
+ return (
147
+ <>
148
+ <div ref={containerRef} />
149
+ {address && <p>{address.zipCode} {address.roadAddress}</p>}
150
+ </>
151
+ );
127
152
  }
128
153
  ```
129
154
 
155
+ 훅 없이 클래스를 직접 다루고 싶다면 `examples/react-example.jsx`를 참고하세요.
156
+
130
157
  ### 3. Vue에서 사용
131
158
 
132
159
  ```vue
133
- <template>
134
- <div ref="addressContainer"></div>
135
- </template>
160
+ <script setup>
161
+ import { useKoreanAddress } from 'slick-address-kr/vue';
162
+ import 'slick-address-kr/styles.css';
136
163
 
137
- <script>
138
- import { KoreanAddressFinder } from 'korean-address-finder';
139
- import 'korean-address-finder/dist/styles.css';
140
-
141
- export default {
142
- mounted() {
143
- this.finder = new KoreanAddressFinder({
144
- onSelect: (address) => {
145
- console.log('선택된 주소:', address);
146
- }
147
- });
148
-
149
- this.finder.init(this.$refs.addressContainer);
150
- },
151
-
152
- beforeUnmount() {
153
- if (this.finder) {
154
- this.finder.destroy();
155
- }
156
- }
157
- };
164
+ const { containerRef, address } = useKoreanAddress({
165
+ apiKey: import.meta.env.VITE_JUSO_API_KEY,
166
+ autocompleteMode: true,
167
+ });
158
168
  </script>
169
+
170
+ <template>
171
+ <div ref="containerRef"></div>
172
+ <p v-if="address">{{ address.zipCode }} {{ address.roadAddress }}</p>
173
+ </template>
159
174
  ```
160
175
 
176
+ > **참고:** `slick-address-kr/vue`의 컴포저블은 `setup()` 시점의 옵션을
177
+ > 한 번만 캡처합니다. React 훅과 달리 이후 `onSelect` 등을 바꿔도
178
+ > 반영되지 않습니다. 콜백을 동적으로 바꿔야 한다면 반환된 `finder` 인스턴스를
179
+ > 직접 조작하세요.
180
+
161
181
  ## API 문서
162
182
 
163
183
  ### Options
164
184
 
165
185
  ```typescript
166
186
  interface AddressFinderOptions {
167
- /** 행정안전부 API 키 (선택사항) */
187
+ /** 행정안전부 API 키. endpoint를 자체 프록시로 지정한 경우 생략 가능 */
168
188
  apiKey?: string;
169
189
 
170
190
  /** 한 페이지에 표시할 결과 수 (기본값: 10) */
@@ -173,11 +193,11 @@ interface AddressFinderOptions {
173
193
  /** 현재 페이지 번호 (기본값: 1) */
174
194
  currentPage?: number;
175
195
 
176
- /** 검색 결과를 표시할 컨테이너 ID */
196
+ /** 검색 결과를 표시할 컨테이너 ID. init()에 엘리먼트를 직접 넘기면 무시됨 */
177
197
  containerId?: string;
178
198
 
179
- /** 주소 선택 시 콜백 함수 */
180
- onSelect?: (address: AddressResult) => void;
199
+ /** 주소 선택 시, 그리고 상세주소가 바뀔 때마다 호출된다 */
200
+ onSelect?: (address: AddressResult, meta: AddressSelectMeta) => void;
181
201
 
182
202
  /** 상세주소 입력 필드 표시 여부 (기본값: true) */
183
203
  showDetailInput?: boolean;
@@ -187,6 +207,12 @@ interface AddressFinderOptions {
187
207
 
188
208
  /** 자동완성 모드 사용 여부 (기본값: false) */
189
209
  autocompleteMode?: boolean;
210
+
211
+ /** 호출할 주소. 자체 프록시 서버를 쓸 때 지정 */
212
+ endpoint?: string;
213
+
214
+ /** fetch 대체 구현. 인증 헤더 주입이나 테스트에 사용 */
215
+ fetchFn?: typeof fetch;
190
216
  }
191
217
  ```
192
218
 
@@ -220,6 +246,21 @@ interface AddressResult {
220
246
 
221
247
  /** 읍면동명 */
222
248
  bname?: string;
249
+
250
+ /** 상세주소·좌표 API 연동에 필요한 원본 코드 (행정구역코드, 도로명코드 등) */
251
+ codes?: AddressCodes;
252
+ }
253
+ ```
254
+
255
+ ### AddressSelectMeta
256
+
257
+ ```typescript
258
+ interface AddressSelectMeta {
259
+ /**
260
+ * 'select' — 사용자가 검색 결과에서 주소를 고른 시점
261
+ * 'detail-change' — 상세주소 입력값이 바뀐 시점
262
+ */
263
+ reason: 'select' | 'detail-change';
223
264
  }
224
265
  ```
225
266
 
@@ -238,21 +279,67 @@ finder.init(document.getElementById('my-container')); // 직접 엘리먼트 전
238
279
 
239
280
  #### `destroy(): void`
240
281
 
241
- 컴포넌트를 제거하고 이벤트 리스너를 정리합니다.
282
+ 컴포넌트를 제거하고 이벤트 리스너를 정리합니다. 여러 번 호출해도 안전합니다.
242
283
 
243
284
  ```javascript
244
285
  finder.destroy();
245
286
  ```
246
287
 
247
- #### `getSelectedAddress(): Object | null`
288
+ #### `getSelectedAddress(): AddressResult | null`
248
289
 
249
- 현재 선택된 주소 정보를 반환합니다.
290
+ 현재 선택된 주소 정보를 반환합니다. 선택 전에는 `null`입니다.
250
291
 
251
292
  ```javascript
252
293
  const selected = finder.getSelectedAddress();
253
- console.log(selected); // { roadAddress, zipCode, detailAddress }
294
+ console.log(selected); // AddressResult | null
295
+ ```
296
+
297
+ #### `search(): Promise<void>`
298
+
299
+ 검색 입력란의 현재 값으로 주소를 검색합니다. 검색 버튼 클릭이나 Enter와
300
+ 같은 동작을 코드에서 그대로 재현할 때 씁니다. 진행 중이던 이전 검색은
301
+ 자동으로 취소됩니다.
302
+
303
+ ```javascript
304
+ await finder.search();
254
305
  ```
255
306
 
307
+ ### 에러 처리
308
+
309
+ API 호출이 실패하면 `JusoApiError`가 던져집니다 (v1처럼 조용히 빈 결과로
310
+ 처리되지 않습니다). `AddressApiClient`를 직접 쓰는 경우:
311
+
312
+ ```javascript
313
+ import { AddressApiClient, JusoApiError } from 'slick-address-kr';
314
+
315
+ const client = new AddressApiClient({ apiKey: 'YOUR_API_KEY' });
316
+
317
+ try {
318
+ const { results, totalCount } = await client.search('판교역로 235', {
319
+ currentPage: 1,
320
+ countPerPage: 10,
321
+ });
322
+ } catch (error) {
323
+ if (error instanceof JusoApiError) {
324
+ // error.code: 행안부 API 원본 에러 코드
325
+ // error.userMessage: 화면에 바로 보여줄 수 있는 한국어 안내
326
+ console.error(error.code, error.userMessage);
327
+ }
328
+ }
329
+ ```
330
+
331
+ `error.userMessage`는 실제 API로 확인된 코드에만 따로 쓴 안내를 담습니다.
332
+
333
+ - 승인키 문제(`E0001`): 키·도메인 확인 안내와 발급 링크
334
+ - 검색어 문제(`E0005` 빈 검색어, `E0006` 시도명만 입력, `E0008` 한 글자,
335
+ `E0009`·`E0012` 숫자·특수문자만, `E0013` 금지 특수문자): 코드 번호 없는
336
+ 부드러운 안내. `error.isInputError`가 `true`이고, 컴포넌트는 빨간 오류 대신
337
+ 결과 머리줄 한 줄로 보여줍니다.
338
+
339
+ 그 외 코드는 API가 준 원본 메시지를 그대로 담습니다.
340
+
341
+ 자동완성 모드는 검색어가 두 글자가 될 때까지 검색하지 않습니다.
342
+
256
343
  ## 사용 예제
257
344
 
258
345
  ### 자동완성 모드
@@ -261,6 +348,7 @@ console.log(selected); // { roadAddress, zipCode, detailAddress }
261
348
 
262
349
  ```javascript
263
350
  const finder = new KoreanAddressFinder({
351
+ apiKey: 'YOUR_API_KEY',
264
352
  autocompleteMode: true,
265
353
  onSelect: (address) => {
266
354
  console.log(address);
@@ -270,22 +358,21 @@ const finder = new KoreanAddressFinder({
270
358
  finder.init();
271
359
  ```
272
360
 
273
- ### 키보드 네비게이션 🆕
361
+ ### 키보드 네비게이션
274
362
 
275
363
  마우스 없이 키보드만으로 주소를 빠르게 입력할 수 있습니다!
276
364
 
277
365
  **사용 방법:**
278
- - **⬇️ 화살표 아래**: 다음 검색 결과로 이동
279
- - **⬆️ 화살표 위**: 이전 검색 결과로 이동
280
- - **Enter**: 선택된 주소를 폼에 적용
366
+ - **⬇️ 화살표 아래 / ⬆️ 화살표 위**: 검색 결과 사이를 순환 이동
367
+ - **Enter**: 활성화된 결과를 선택 (없으면 검색 실행)
281
368
  - **ESC**: 검색 결과 닫기
282
369
 
283
370
  ```javascript
284
- // 자동완성 모드에서 키보드 네비게이션이 자동으로 활성화됩니다
371
+ // 검색 결과가 열려 있으면 키보드 네비게이션이 자동으로 활성화됩니다
285
372
  const finder = new KoreanAddressFinder({
286
373
  autocompleteMode: true, // 타이핑 시 자동 검색
287
- onSelect: (address) => {
288
- console.log('선택된 주소:', address);
374
+ onSelect: (address, meta) => {
375
+ console.log('선택된 주소:', address, meta.reason);
289
376
  // 폼에 자동으로 입력됩니다
290
377
  }
291
378
  });
@@ -299,6 +386,7 @@ finder.init();
299
386
 
300
387
  ```javascript
301
388
  const finder = new KoreanAddressFinder({
389
+ apiKey: 'YOUR_API_KEY',
302
390
  showDetailInput: false,
303
391
  onSelect: (address) => {
304
392
  console.log(address);
@@ -308,10 +396,37 @@ const finder = new KoreanAddressFinder({
308
396
  finder.init();
309
397
  ```
310
398
 
311
- ### 커스텀 스타일 적용
399
+ ### 자체 프록시 서버로 승인키 숨기기
400
+
401
+ API 키를 클라이언트 번들에 노출하고 싶지 않다면, 자체 서버가 승인키를
402
+ 붙여 행안부 API를 대신 호출하게 하고 `endpoint`만 지정하세요.
403
+
404
+ ```js
405
+ new KoreanAddressFinder({
406
+ endpoint: 'https://your-server.example/api/address',
407
+ // apiKey는 서버가 붙이므로 전달하지 않습니다
408
+ });
409
+ ```
410
+
411
+ 동작하는 예제 서버는 `examples/backend-proxy/`에 있습니다.
412
+
413
+ ### 테마 커스터마이징
414
+
415
+ 색상은 CSS 변수로 바꿀 수 있습니다.
416
+
417
+ ```css
418
+ .kaf-wrapper {
419
+ --kaf-accent: #ff6b35;
420
+ --kaf-border: #ddd;
421
+ }
422
+ ```
423
+
424
+ 다크모드는 `prefers-color-scheme: dark`를 자동으로 따릅니다. 클래스 단위
425
+ 커스터마이징이 필요하면 `customClass` 옵션을 쓰세요.
312
426
 
313
427
  ```javascript
314
428
  const finder = new KoreanAddressFinder({
429
+ apiKey: 'YOUR_API_KEY',
315
430
  customClass: 'my-custom-class',
316
431
  onSelect: (address) => {
317
432
  console.log(address);
@@ -337,10 +452,12 @@ finder.init();
337
452
 
338
453
  ### 1. API 키 발급
339
454
 
340
- 1. [주소기반산업지원서비스](https://business.juso.go.kr/) 접속
455
+ 1. [주소기반산업지원서비스 검색 API 신청](https://business.juso.go.kr/jst/jstAddressApiApplicationWrite) 접속
341
456
  2. 승인키 신청
342
457
  3. ⚠️ **중요: "검색 API" 선택** (팝업 API가 아님!)
343
- 4. 사용할 도메인 등록 (선택사항)
458
+ 4. **사용할 도메인을 등록하세요.** 발급 시 등록한 도메인에서만 호출이
459
+ 허용됩니다. **localhost 개발에는 도메인에 `localhost`를 넣은 별도의
460
+ 개발용 키가 필요합니다.**
344
461
 
345
462
  ### 2. API 키 안전하게 사용하기
346
463
 
@@ -369,12 +486,12 @@ const finder = new KoreanAddressFinder({
369
486
 
370
487
  ```bash
371
488
  # .env
372
- REACT_APP_JUSO_API_KEY=your_key_here
489
+ VITE_JUSO_API_KEY=your_key_here
373
490
  ```
374
491
 
375
492
  ```javascript
376
493
  const finder = new KoreanAddressFinder({
377
- apiKey: process.env.REACT_APP_JUSO_API_KEY,
494
+ apiKey: import.meta.env.VITE_JUSO_API_KEY,
378
495
  onSelect: (address) => console.log(address)
379
496
  });
380
497
  ```
@@ -386,12 +503,13 @@ API 키를 서버에만 저장하고 클라이언트는 자체 서버를 통해
386
503
  ```javascript
387
504
  // 클라이언트에서는 apiKey 불필요
388
505
  const finder = new KoreanAddressFinder({
389
- // 자체 서버 URL 설정 (api-client.ts 수정 필요)
506
+ endpoint: 'https://your-server.example/api/address',
390
507
  onSelect: (address) => console.log(address)
391
508
  });
392
509
  ```
393
510
 
394
- 자세한 내용은 `API-KEY-GUIDE.md` 및 `examples/backend-proxy/` 참조
511
+ 자세한 내용은 [docs/API-KEY-GUIDE.md](./docs/API-KEY-GUIDE.md) 및
512
+ `examples/backend-proxy/` 참조
395
513
 
396
514
  ## 다음 주소 API와의 비교
397
515
 
@@ -414,13 +532,25 @@ const finder = new KoreanAddressFinder({
414
532
  - iOS Safari (12+)
415
533
  - Android Chrome (최신)
416
534
 
535
+ ## 더 읽어보기
536
+
537
+ - [docs/MIGRATION-v2.md](./docs/MIGRATION-v2.md) — v1 → v2 마이그레이션 가이드
538
+ - [docs/QUICK-START.md](./docs/QUICK-START.md) — 빠른 시작
539
+ - [docs/HOW-TO-USE.md](./docs/HOW-TO-USE.md) — 실제 웹사이트 적용 가이드
540
+ - [docs/API-KEY-GUIDE.md](./docs/API-KEY-GUIDE.md) — API 키 발급·관리 가이드
541
+ - [docs/AUTOCOMPLETE-GUIDE.md](./docs/AUTOCOMPLETE-GUIDE.md) — 자동완성 상세 가이드
542
+ - [docs/TEST-GUIDE.md](./docs/TEST-GUIDE.md) — 테스트 가이드
543
+ - [docs/SELF-HOSTED-DATABASE.md](./docs/SELF-HOSTED-DATABASE.md) — 자체 DB 구축
544
+ - [CHANGELOG.md](./CHANGELOG.md) — 버전별 변경 이력
545
+
417
546
  ## 라이선스
418
547
 
419
548
  MIT License - 자유롭게 사용하세요!
420
549
 
421
550
  ## 기여하기
422
551
 
423
- 이슈와 PR은 언제나 환영합니다!
552
+ 이슈와 PR은 언제나 환영합니다! 자세한 개발 절차는
553
+ [CONTRIBUTING.md](./CONTRIBUTING.md)를 참고하세요.
424
554
 
425
555
  1. Fork the repository
426
556
  2. Create your feature branch (`git checkout -b feature/amazing-feature`)
@@ -434,7 +564,7 @@ MIT License - 자유롭게 사용하세요!
434
564
 
435
565
  이 라이브러리는 **API 키가 필수**입니다. 키 없이 사용하면 동작하지 않습니다.
436
566
 
437
- 1. [행정안전부 도로명주소 OpenAPI](https://business.juso.go.kr/addrlink/openApi/searchApi.do)에서 **"검색 API"** 키를 직접 발급받으세요. (팝업 API 아님!)
567
+ 1. [행정안전부 도로명주소 OpenAPI](https://business.juso.go.kr/jst/jstAddressApiApplicationWrite)에서 **"검색 API"** 키를 직접 발급받으세요. (팝업 API 아님!)
438
568
  2. 발급받은 키를 `apiKey` 옵션으로 전달하세요:
439
569
  ```js
440
570
  new KoreanAddressFinder({ apiKey: '발급받은_본인_키', /* ... */ });
@@ -447,7 +577,7 @@ MIT License - 자유롭게 사용하세요!
447
577
 
448
578
  1. API 키가 올바른지 확인
449
579
  2. ⚠️ **중요: API 발급 시 "검색 API"로 발급받았는지 확인** (팝업 API가 아님!)
450
- 3. **키 발급 시 등록한 도메인에서만 호출이 허용됩니다.** 사용할 도메인(예: `localhost`, 실제 서비스 도메인)을 juso.go.kr에 등록했는지 확인하세요.
580
+ 3. **키 발급 시 등록한 도메인에서만 호출이 허용됩니다.** 사용할 도메인(예: `localhost`, 실제 서비스 도메인)을 juso.go.kr에 등록했는지 확인하세요. **localhost 개발에는 별도의 개발용 키가 필요합니다.**
451
581
  4. HTTPS 사용 권장
452
582
 
453
583
  ### 검색 결과가 나오지 않아요
@@ -455,7 +585,7 @@ MIT License - 자유롭게 사용하세요!
455
585
  1. 인터넷 연결 확인
456
586
  2. API 키가 올바른지 확인
457
587
  3. ⚠️ **API 발급 시 "검색 API"로 선택했는지 확인**
458
- 4. 콘솔에서 에러 메시지 확인
588
+ 4. 콘솔에서 에러 메시지 확인 (v2부터는 `JusoApiError`가 던져지므로 원인이 명확히 보입니다)
459
589
 
460
590
  ## 지원
461
591