dongnelibrary 0.3.10 → 0.3.13
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/.claude/projects/-Users-bob-src-dongnelibrary/memory/MEMORY.md +4 -0
- package/.claude/projects/-Users-bob-src-dongnelibrary/memory/reference_data4library_api.md +106 -0
- package/.claude/settings.local.json +18 -0
- package/.playwright-mcp/console-2026-03-11T12-40-15-835Z.log +1 -0
- package/API.md +83 -23
- package/README.md +11 -5
- package/dist/cli.js +153 -96
- package/dist/cli.js.map +1 -1
- package/dist/dongnelibrary.d.ts +13 -1
- package/dist/dongnelibrary.d.ts.map +1 -1
- package/dist/dongnelibrary.js +18 -5
- package/dist/dongnelibrary.js.map +1 -1
- package/dist/localLibraryModule/ice.d.ts +6 -0
- package/dist/localLibraryModule/ice.d.ts.map +1 -0
- package/dist/localLibraryModule/ice.js +92 -0
- package/dist/localLibraryModule/ice.js.map +1 -0
- package/dist/localLibraryModule/wonju.d.ts +6 -0
- package/dist/localLibraryModule/wonju.d.ts.map +1 -0
- package/dist/localLibraryModule/wonju.js +87 -0
- package/dist/localLibraryModule/wonju.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/claude-lm-library_cli_interface.md +60 -0
- package/docs/comparison-claude-vs-opencode.md +40 -0
- package/docs/opencode-lm-interface_description.md +72 -0
- package/docs//353/217/204/354/204/234/352/264/200-/354/240/225/353/263/264/353/202/230/353/243/250-API.md +119 -0
- package/opencode.json +7 -0
- package/package.json +7 -2
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.search = exports.homeUrl = exports.moduleName = void 0;
|
|
4
|
+
exports.getLibraryNames = getLibraryNames;
|
|
5
|
+
const util_1 = require("../util");
|
|
6
|
+
const http_1 = require("../http");
|
|
7
|
+
const jsdom_1 = require("jsdom");
|
|
8
|
+
exports.moduleName = "인천광역시교육청통합공공도서관";
|
|
9
|
+
exports.homeUrl = "https://lib.ice.go.kr/";
|
|
10
|
+
const libraryList = [
|
|
11
|
+
{ code: "lib_ME", name: "인천광역시교육청계양도서관" },
|
|
12
|
+
{ code: "lib_MC", name: "인천광역시교육청부평도서관" },
|
|
13
|
+
{ code: "lib_MF", name: "인천광역시교육청서구도서관" },
|
|
14
|
+
{ code: "lib_MA", name: "인천광역시교육청신트리도서관" },
|
|
15
|
+
{ code: "lib_MH", name: "인천광역시교육청연수도서관" },
|
|
16
|
+
{ code: "lib_MD", name: "인천광역시교육청주안도서관" },
|
|
17
|
+
{ code: "lib_MB", name: "인천광역시교육청중앙도서관" },
|
|
18
|
+
{ code: "lib_MJ", name: "인천광역시교육청평생학습관도서관" },
|
|
19
|
+
{ code: "lib_MG", name: "인천광역시교육청화도진도서관" },
|
|
20
|
+
];
|
|
21
|
+
const getLibraryCode = (0, util_1.createLibraryCodeLookup)(libraryList);
|
|
22
|
+
function buildSearchUrl(title, lcode) {
|
|
23
|
+
const params = new URLSearchParams();
|
|
24
|
+
params.append("menu_idx", "11");
|
|
25
|
+
params.append("viewPage", "1");
|
|
26
|
+
params.append("libraryCodes", "ALL");
|
|
27
|
+
params.append("_libraryCodes", "on");
|
|
28
|
+
params.append("libraryCodes", lcode);
|
|
29
|
+
params.append("_libraryCodes", "on");
|
|
30
|
+
params.append("search_type", "L_TITLE");
|
|
31
|
+
params.append("search_text", title);
|
|
32
|
+
params.append("booktype", "BOOK");
|
|
33
|
+
params.append("rowCount", "1000");
|
|
34
|
+
return `https://lib.ice.go.kr/ice/intro/search/index.do?${params}`;
|
|
35
|
+
}
|
|
36
|
+
async function searchImpl(opt) {
|
|
37
|
+
const { title, libraryName, signal } = opt;
|
|
38
|
+
(0, util_1.validateSearchOptions)(opt);
|
|
39
|
+
const lcode = getLibraryCode(libraryName);
|
|
40
|
+
const url = buildSearchUrl(title, lcode);
|
|
41
|
+
const { statusCode, body } = await (0, http_1.get)(url, { signal });
|
|
42
|
+
if (statusCode !== 200) {
|
|
43
|
+
throw new Error(`HTTP ${statusCode}`);
|
|
44
|
+
}
|
|
45
|
+
const dom = new jsdom_1.JSDOM(body);
|
|
46
|
+
const document = dom.window.document;
|
|
47
|
+
const bodyText = document.body?.textContent ?? "";
|
|
48
|
+
const countMatch = bodyText.match(/총\s*(\d+)건/);
|
|
49
|
+
const count = countMatch ? Number(countMatch[1]) : 0;
|
|
50
|
+
const booklist = [];
|
|
51
|
+
const bookItems = document.querySelectorAll(".bif");
|
|
52
|
+
bookItems.forEach((item) => {
|
|
53
|
+
const titleLink = item.querySelector("a.goDetail");
|
|
54
|
+
const bookTitle = titleLink?.textContent?.trim() ?? "";
|
|
55
|
+
const regNo = titleLink?.getAttribute("regno") ?? "";
|
|
56
|
+
const manageCode = titleLink?.getAttribute("managecode") ?? "";
|
|
57
|
+
const bookUrl = regNo
|
|
58
|
+
? `https://lib.ice.go.kr/ice/intro/search/detail.do?regNo=${regNo}&manageCode=${manageCode}&booktype=BOOK`
|
|
59
|
+
: "";
|
|
60
|
+
const paragraphs = item.querySelectorAll("p");
|
|
61
|
+
let libName = "";
|
|
62
|
+
for (const p of paragraphs) {
|
|
63
|
+
const text = p.textContent ?? "";
|
|
64
|
+
if (text.includes("소장처")) {
|
|
65
|
+
const match = text.match(/소장처\s*:\s*([^/]+)/);
|
|
66
|
+
libName = match ? match[1].trim() : "";
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const availCell = item.querySelector("td");
|
|
71
|
+
const availability = availCell?.textContent?.trim() ?? "";
|
|
72
|
+
if (bookTitle) {
|
|
73
|
+
booklist.push({
|
|
74
|
+
libraryName: libName || libraryName,
|
|
75
|
+
title: bookTitle,
|
|
76
|
+
bookUrl,
|
|
77
|
+
exist: availability === "대출가능",
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
startPage: opt.startPage,
|
|
83
|
+
totalBookCount: count,
|
|
84
|
+
booklist,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
exports.search = (0, util_1.wrapWithCallback)(searchImpl);
|
|
88
|
+
function getLibraryNames() {
|
|
89
|
+
return (0, util_1.getLibraryNames)(libraryList);
|
|
90
|
+
}
|
|
91
|
+
({ moduleName: exports.moduleName, homeUrl: exports.homeUrl, search: exports.search, getLibraryNames });
|
|
92
|
+
//# sourceMappingURL=ice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ice.js","sourceRoot":"","sources":["../../src/localLibraryModule/ice.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SearchOptions, SearchResult } from "../types";
|
|
2
|
+
export declare const moduleName = "\uC6D0\uC8FC\uC2DC\uB9BD\uD1B5\uD569\uB3C4\uC11C\uAD00";
|
|
3
|
+
export declare const homeUrl = "https://lib.wonju.go.kr/";
|
|
4
|
+
export declare const search: (opt: SearchOptions, callback?: import("../types").SearchCallback) => Promise<SearchResult | void>;
|
|
5
|
+
export declare function getLibraryNames(): string[];
|
|
6
|
+
//# sourceMappingURL=wonju.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wonju.d.ts","sourceRoot":"","sources":["../../src/localLibraryModule/wonju.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAIV,aAAa,EACb,YAAY,EACb,MAAM,UAAU,CAAC;AAElB,eAAO,MAAM,UAAU,2DAAc,CAAC;AACtC,eAAO,MAAM,OAAO,6BAA6B,CAAC;AA+FlD,eAAO,MAAM,MAAM,oGAA+B,CAAC;AAEnD,wBAAgB,eAAe,IAAI,MAAM,EAAE,CAE1C"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.search = exports.homeUrl = exports.moduleName = void 0;
|
|
4
|
+
exports.getLibraryNames = getLibraryNames;
|
|
5
|
+
const util_1 = require("../util");
|
|
6
|
+
const http_1 = require("../http");
|
|
7
|
+
const jsdom_1 = require("jsdom");
|
|
8
|
+
exports.moduleName = "원주시립통합도서관";
|
|
9
|
+
exports.homeUrl = "https://lib.wonju.go.kr/";
|
|
10
|
+
const libraryList = [
|
|
11
|
+
{ code: "MA", name: "시립중앙도서관" },
|
|
12
|
+
{ code: "MC", name: "중천철학도서관" },
|
|
13
|
+
{ code: "MQ", name: "미리내도서관" },
|
|
14
|
+
{ code: "MB", name: "태장도서관" },
|
|
15
|
+
{ code: "MS", name: "샘마루도서관" },
|
|
16
|
+
{ code: "MT", name: "그림책도서관" },
|
|
17
|
+
{ code: "MU", name: "생각자람어린이도서관" },
|
|
18
|
+
{ code: "ME", name: "귀래면작은도서관" },
|
|
19
|
+
{ code: "MO", name: "원주한도시한책읽기도서관" },
|
|
20
|
+
{ code: "MD", name: "개운동작은도서관" },
|
|
21
|
+
{ code: "MN", name: "치악산새마을문고작은도서관" },
|
|
22
|
+
{ code: "MF", name: "무실동작은도서관" },
|
|
23
|
+
{ code: "MG", name: "문막읍작은도서관" },
|
|
24
|
+
{ code: "MH", name: "봉산동작은도서관" },
|
|
25
|
+
{ code: "MM", name: "도란도란청소년도서관" },
|
|
26
|
+
{ code: "MR", name: "부론면작은도서관" },
|
|
27
|
+
];
|
|
28
|
+
const getLibraryCode = (0, util_1.createLibraryCodeLookup)(libraryList);
|
|
29
|
+
async function searchImpl(opt) {
|
|
30
|
+
const { title, libraryName, signal } = opt;
|
|
31
|
+
(0, util_1.validateSearchOptions)(opt);
|
|
32
|
+
const lcode = getLibraryCode(libraryName);
|
|
33
|
+
const { statusCode, body } = await (0, http_1.get)("https://lib.wonju.go.kr/ja/menu/234/book/search", {
|
|
34
|
+
qs: {
|
|
35
|
+
search: "true",
|
|
36
|
+
searchCondition: "searchTxt",
|
|
37
|
+
searchInput: title,
|
|
38
|
+
manageCodes: lcode,
|
|
39
|
+
size: 1000,
|
|
40
|
+
page: 1,
|
|
41
|
+
},
|
|
42
|
+
signal,
|
|
43
|
+
});
|
|
44
|
+
if (statusCode !== 200) {
|
|
45
|
+
throw new Error(`HTTP ${statusCode}`);
|
|
46
|
+
}
|
|
47
|
+
const dom = new jsdom_1.JSDOM(body);
|
|
48
|
+
const document = dom.window.document;
|
|
49
|
+
const countEl = document.querySelector("strong.point");
|
|
50
|
+
const count = (0, util_1.extractNumber)(countEl?.textContent);
|
|
51
|
+
const booklist = [];
|
|
52
|
+
const articles = document.querySelectorAll("article.book");
|
|
53
|
+
articles.forEach((article) => {
|
|
54
|
+
const bookData = article.querySelector(".bookData");
|
|
55
|
+
const bookTitle = bookData?.getAttribute("data-title") ??
|
|
56
|
+
article.querySelector(".title")?.textContent?.trim() ??
|
|
57
|
+
"";
|
|
58
|
+
const regNo = bookData?.getAttribute("data-reg-no") ?? "";
|
|
59
|
+
const bookUrl = regNo
|
|
60
|
+
? `https://lib.wonju.go.kr/ja/menu/234/book/view/${regNo}?booktype=`
|
|
61
|
+
: "";
|
|
62
|
+
const libNameEl = article.querySelector(".lib_name");
|
|
63
|
+
const libName = libNameEl?.firstChild?.textContent?.trim() ?? libraryName;
|
|
64
|
+
const loanEl = article.querySelector(".lib_name strong");
|
|
65
|
+
const loanText = loanEl?.textContent?.trim() ?? "";
|
|
66
|
+
const exist = loanText.includes("대출가능");
|
|
67
|
+
if (bookTitle) {
|
|
68
|
+
booklist.push({
|
|
69
|
+
libraryName: libName,
|
|
70
|
+
title: bookTitle,
|
|
71
|
+
bookUrl,
|
|
72
|
+
exist,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
startPage: opt.startPage,
|
|
78
|
+
totalBookCount: count,
|
|
79
|
+
booklist,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
exports.search = (0, util_1.wrapWithCallback)(searchImpl);
|
|
83
|
+
function getLibraryNames() {
|
|
84
|
+
return (0, util_1.getLibraryNames)(libraryList);
|
|
85
|
+
}
|
|
86
|
+
({ moduleName: exports.moduleName, homeUrl: exports.homeUrl, search: exports.search, getLibraryNames });
|
|
87
|
+
//# sourceMappingURL=wonju.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wonju.js","sourceRoot":"","sources":["../../src/localLibraryModule/wonju.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ export interface SearchOptionsMain {
|
|
|
105
105
|
* Public API interface for DongneLibrary
|
|
106
106
|
*/
|
|
107
107
|
export interface DongneLibraryAPI {
|
|
108
|
+
resolveLibraryCount: (libraryName: string | string[]) => number;
|
|
108
109
|
getAllLibraryNames: () => string[];
|
|
109
110
|
getAllModuleNames: () => string[];
|
|
110
111
|
getModuleHomeUrls: () => Record<string, string>;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 라이브러리와 CLI 인터페이스 설명
|
|
2
|
+
|
|
3
|
+
## 1. 현재 구조
|
|
4
|
+
|
|
5
|
+
| 파일 | 주요 API | 역할 |
|
|
6
|
+
|------|----------|------|
|
|
7
|
+
| `src/dongnelibrary.ts` | `dongneLibrary`(default export) <br> `getAllLibraryNames()`<br> `getAllModuleNames()`<br> `resolveLibraryCount(libraryName)`<br> `searchAsync(opt, onResult?)`<br> `search(opt, onResult?, onComplete?)` | 실제 검색 로직과 데이터 구조를 담당 |
|
|
8
|
+
| `src/cli.ts` | `dl = require("./dongnelibrary")` <br> `dl.getAllModuleNames()`<br> `dl.searchAsync(opts, ...)` | 명령줄 인자를 파싱하고 결과를 포맷·출력 |
|
|
9
|
+
|
|
10
|
+
### 왜 자연스러운가?
|
|
11
|
+
1. **책임 분리** – 라이브러리는 검색 로직만, CLI는 UI(입력·출력)만 담당 → 테스트가 쉽고 유지보수 용이
|
|
12
|
+
2. **필요 API가 모두 공개** – CLI에서 바로 호출할 수 있는 `searchAsync`, `getAllLibraryNames` 등 필수 기능이 모두 제공
|
|
13
|
+
3. **타입 안전성** – `src/types.ts`에서 정의한 타입을 두 파일 모두 공유 → 컴파일 타임에 오류를 잡음
|
|
14
|
+
|
|
15
|
+
## 2. 개선·확장 제안 (필요 시 적용)
|
|
16
|
+
| 항목 | 현재 상태 | 추천 변경 |
|
|
17
|
+
|------|-----------|----------|
|
|
18
|
+
| **CLI 엔트리 포인트** | `#!/usr/bin/env node` + CommonJS `require()` | TypeScript/ESM(`import`)로 통일 → 번들 크기 감소 |
|
|
19
|
+
| **옵션 타입** | `SearchOptionsMain` 은 `libraryName: string | string[]` 허용 | CLI에서 전달 시 자동 변환 함수 추가 |
|
|
20
|
+
| **에러 처리** | `searchAsync` 에서 콜백으로 전달 | Promise `reject` 사용 → `try/catch` 로 간단히 처리 |
|
|
21
|
+
| **결과 포맷** | CLI 내부에서 `printBooks` 구현 | 라이브러리에 `formatResult()` 같은 포맷터 추가 → CLI 호출만으로 완료 |
|
|
22
|
+
| **테스트** | CLI 테스트(`cli.spec.js`) | 라이브러리 단위 테스트 강화, CLI는 통합 테스트에 집중 |
|
|
23
|
+
| **문서** | README 에 기본 사용법만 | JSDoc 풍부화 + CLI 샘플 스크립트 추가 |
|
|
24
|
+
|
|
25
|
+
## 3. 예시: “CLI 전용 래퍼” 도입
|
|
26
|
+
```ts
|
|
27
|
+
// src/dongnelibrary.ts (추가 예시)
|
|
28
|
+
export interface DongneLibraryCLI {
|
|
29
|
+
/** CLI에서 바로 호출할 검색 래퍼 */
|
|
30
|
+
searchBooks(opts: SearchOptionsMain, showUrl?: boolean): Promise<SearchResult[]>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const dongneLibraryCLI: DongneLibraryCLI = {
|
|
34
|
+
async searchBooks(opts, showUrl) {
|
|
35
|
+
const results = await searchAsync(opts);
|
|
36
|
+
return results.map(r => ({
|
|
37
|
+
...r,
|
|
38
|
+
booklist: r.booklist.map(b => ({ ...b, url: showUrl ? b.bookUrl : undefined })),
|
|
39
|
+
}));
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
// src/cli.ts (간소화 예시)
|
|
46
|
+
import { dongneLibraryCLI as dl } from "./dongnelibrary";
|
|
47
|
+
|
|
48
|
+
const results = await dl.searchBooks({ title, libraryName: libList }, showUrl);
|
|
49
|
+
results.forEach(r => console.log(formatResult(r)));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **장점**
|
|
53
|
+
- CLI는 “검색 + 출력”만 담당 → 코드가 훨씬 깔끔
|
|
54
|
+
- 라이브러리는 순수 데이터 로직 + 포맷터만 제공 → 재사용성 증가
|
|
55
|
+
|
|
56
|
+
## 4. 결론
|
|
57
|
+
- 현재 구조는 **자연스럽고** 잘 설계돼 있음.
|
|
58
|
+
- 필요에 따라 위에서 제안한 **“CLI 전용 래퍼”**나 **포맷터 함수를 추가**하면 사용 편의성과 테스트 용이성이 한층 더 향상됩니다.
|
|
59
|
+
|
|
60
|
+
궁금한 점이나 실제 구현을 도와드릴 부분이 있으면 알려 주세요!
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Claude vs OpenCode 답변 비교
|
|
2
|
+
|
|
3
|
+
## 질문
|
|
4
|
+
|
|
5
|
+
> 라이브러리와 cli 가 같이 공존합니다.
|
|
6
|
+
> 이 둘 사이에 인터페이스가 자연스럽게 연결되고 있는지 확인해주세요.
|
|
7
|
+
> 권장하는 인터페이스 방법이 있다면 알려주세요
|
|
8
|
+
|
|
9
|
+
## 비교 대상
|
|
10
|
+
|
|
11
|
+
- 1번: `docs/claude-lm-library_cli_interface.md` — lmstudio (openai/gpt-oss-20b) + Claude (12분)
|
|
12
|
+
- 2번: `docs/opencode-lm-interface_description.md` — lmstudio (openai/gpt-oss-20b) + OpenCode (28분)
|
|
13
|
+
|
|
14
|
+
## 공통점
|
|
15
|
+
|
|
16
|
+
두 문서 모두 동일한 결론에 도달합니다:
|
|
17
|
+
|
|
18
|
+
- **현재 구조는 잘 설계되어 있다** — 라이브러리(`dongnelibrary.ts`)와 CLI(`cli.ts`)의 책임 분리가 명확
|
|
19
|
+
- **타입 안전성이 확보되어 있다** — `types.ts`를 공유하여 컴파일 타임 오류 검출
|
|
20
|
+
- **API가 적절히 공개되어 있다** — `searchAsync`, `getAllLibraryNames` 등 CLI에 필요한 함수가 모두 export됨
|
|
21
|
+
|
|
22
|
+
## 차이점
|
|
23
|
+
|
|
24
|
+
| 항목 | 1번 (Claude, 12분) | 2번 (OpenCode, 28분) |
|
|
25
|
+
|------|---------------------|----------------------|
|
|
26
|
+
| **구조** | 표 + 제안 목록 + 코드 예시 | 표 4개 + 코드 예시, 더 세분화된 섹션 |
|
|
27
|
+
| **분석 깊이** | 큰 그림 위주, 실용적 제안 | 코드 레벨까지 구체적 문제 지적 (중복 export, namespace import 등) |
|
|
28
|
+
| **핵심 제안** | "CLI 전용 래퍼" 패턴 도입 | `index.ts` 루트 재내보내기 + named import 전환 |
|
|
29
|
+
| **문제 지적** | 개선 "가능한" 사항 나열 (에러 처리, 포맷터 등) | 실제 코드의 **구체적 버그/비효율** 지적 (중복 export, default/named 충돌 가능성) |
|
|
30
|
+
| **실용성** | 래퍼 패턴 코드 예시 제공 — 당장 적용 가능 | 패턴 권장 사항 5개 제시 — 구조적 리팩터링 가이드 |
|
|
31
|
+
|
|
32
|
+
## 평가
|
|
33
|
+
|
|
34
|
+
**1번 (Claude)** — 간결하고 실용적입니다. "CLI 전용 래퍼" 제안은 바로 적용할 수 있는 구체적 코드까지 포함하고 있어 행동 지향적입니다. 다만 현재 코드의 실제 문제점(중복 export 등)을 놓쳤습니다.
|
|
35
|
+
|
|
36
|
+
**2번 (OpenCode)** — 더 꼼꼼합니다. 실제 코드에서 중복 export, namespace import 비효율, default/named export 충돌 가능성 같은 **구체적 문제**를 짚어냈습니다. `index.ts` 도입, named import 전환 등의 제안은 NPM 패키지화를 고려한 장기적 관점입니다. 대신 시간이 2배 이상 걸렸고, 제안이 다소 분산되어 있습니다.
|
|
37
|
+
|
|
38
|
+
## 요약
|
|
39
|
+
|
|
40
|
+
1번은 "빠르고 실용적", 2번은 "느리지만 더 정확한 진단". 둘 다 현재 구조가 좋다는 결론은 같지만, 개선 방향의 초점이 다릅니다 — 1번은 사용 편의성(래퍼), 2번은 코드 위생(중복 제거, 구조 정리).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
## 1️⃣ 현재 인터페이스가 어떻게 연결돼 있는지
|
|
2
|
+
|
|
3
|
+
| 구성요소 | 제공되는 것 | CLI에서의 사용 방식 |
|
|
4
|
+
|----------|-------------|---------------------|
|
|
5
|
+
| **`src/dongnelibrary.ts`** | • `DongneLibraryAPI` 인터페이스를 구현한 **기본(default) export** (`dongneLibrary`).<br>• 모든 공개 함수(`getAllLibraryNames`, `searchAsync` 등)를 **개별 export**로 내보냄.<br>• 타입 정의(`SearchOptionsMain` 등)를 `export type { … } from "./types"` 로 내보냄. | • CLI가 `import * as dl from "./dongnelibrary"` 로 네임스페이스를 가져온 뒤, `dl.getAllModuleNames()`, `dl.searchAsync(...)` 같은 함수를 호출함.<br>• 옵션 객체 타입(`SearchOptionsMain`)도 `./types` 에서 가져와 사용함. |
|
|
6
|
+
|
|
7
|
+
이렇게 하면 CLI가 필요로 하는 모든 API를 `dongnelibrary.ts` 에서 가져오기 때문에 두 모듈이 자연스럽게 연결됩니다. 타입스크립트가 인터페이스의 형태를 검사해 주므로, 코드가 서로 맞지 않으면 컴파일 단계에서 오류가 발생합니다.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 2️⃣ 잘 동작하고 있는 부분
|
|
12
|
+
|
|
13
|
+
| ✅ | 이유 |
|
|
14
|
+
|----|------|
|
|
15
|
+
| **모듈 분리** – `dongnelibrary.ts`(라이브러리)와 `cli.ts`(실행파일)를 명확히 분리해 두었음. |
|
|
16
|
+
| **명시적 API** – `DongneLibraryAPI` 에 모든 공개 함수가 선언돼 있고, 파일에서도 한 번씩만 `export const …` 로 내보냄. |
|
|
17
|
+
| **타입 안전성** – `SearchOptionsMain`, `SearchResult` 등 타입을 두 파일에서 동일하게 가져와 사용해 오류를 방지함. |
|
|
18
|
+
| **기본(default) export** – `export default dongneLibrary` 로 한 번에 전체 API를 가져올 수 있음. |
|
|
19
|
+
| **확장성** – `LIBRARY_MODULES` 배열에 새로운 모듈을 추가하면 CLI 로직은 변하지 않음. |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 3️⃣ 개선이 필요한 부분
|
|
24
|
+
|
|
25
|
+
| ⚠️ | 현재 문제 / 비효율성 | 해결 방법 |
|
|
26
|
+
|----|---------------------|-----------|
|
|
27
|
+
| **중복된 named export** | `export const getAllLibraryNames = …;` 뒤에 다시 `export { getAllLibraryNames, … };` 가 있어 같은 함수가 두 번 export 됨. | **한 가지 방식만 사용** (예: 개별 `export const` 로 끝내거나, 한 번에 블록 형태로만 내보냄). |
|
|
28
|
+
| **네임스페이스 import** | `import * as dl from "./dongnelibrary"` 은 전체 모듈을 가져오므로 사용하지 않는 함수가 포함될 수 있음. | **명시적 named import** 로 바꾸면 읽기 쉽고 트리쉐이크가 더 잘 동작합니다. |
|
|
29
|
+
| **기본 export와 named export 충돌** | `export const dongneLibrary` 와 함께 `export { … }` 를 두면 TypeScript에서 중복 오류가 발생할 수 있음. | **기본 export만 남기고, 필요하면 `export { dongneLibrary }` 로 별도 명시**. |
|
|
30
|
+
| **타입 재내보내기** | CLI가 `./types` 에서 직접 타입을 가져오는데, 라이브러리를 별도 NPM 패키지로 만들 경우 사용자가 `import { SearchOptionsMain } from "dongnelibrary"` 으로 가져오게 하면 좋음. | **루트에 `index.ts` 를 두고 `export * from "./types"` 로 재내보내기**. |
|
|
31
|
+
| **코드 중복 방지** | 라이브러리와 CLI가 공유해야 할 헬퍼 함수가 있다면 별도 모듈(`src/utils.ts`)에 두고 import 하면 좋음. | **공통 유틸리티를 별도 파일에 두고 둘 다 import**. |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 4️⃣ 인터페이스를 깔끔하게 유지하기 위한 권장 패턴
|
|
36
|
+
|
|
37
|
+
1. **루트 `index.ts` 만들기**
|
|
38
|
+
```ts
|
|
39
|
+
// src/index.ts
|
|
40
|
+
export * from "./dongnelibrary"; // 모든 함수, 기본 export 포함
|
|
41
|
+
export * from "./types"; // 타입 정의 재내보내기
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
2. **CLI에서 named import 사용**
|
|
45
|
+
```ts
|
|
46
|
+
import {
|
|
47
|
+
getAllModuleNames,
|
|
48
|
+
searchAsync,
|
|
49
|
+
resolveLibraryCount,
|
|
50
|
+
// …필요한 함수들
|
|
51
|
+
} from "./index";
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. **기본 export 한 번만 남기기**
|
|
55
|
+
```ts
|
|
56
|
+
// src/dongnelibrary.ts
|
|
57
|
+
const dongneLibrary: DongneLibraryAPI = { … };
|
|
58
|
+
export default dongneLibrary; // 한 줄만 남김
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
4. **타입 재내보내기**
|
|
62
|
+
- NPM 패키지로 배포할 때는 `src/index.ts` 에서 타입도 함께 내보내면 사용자는 `import { SearchOptionsMain } from "dongnelibrary"` 으로 바로 가져올 수 있음.
|
|
63
|
+
|
|
64
|
+
5. **옵션 – 팩토리 함수**
|
|
65
|
+
- 나중에 HTTP 클라이언트 같은 의존성을 주입해야 할 때는
|
|
66
|
+
```ts
|
|
67
|
+
export const createDongneLibrary = (deps: { http?: HttpSession }) => {
|
|
68
|
+
// deps.http 를 내부에서 사용
|
|
69
|
+
};
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
이렇게 하면 **라이브러리와 CLI**가 서로 명확히 분리되고, 타입스크립트가 인터페이스를 검증해 주기 때문에 런타임 오류 없이 두 모듈이 자연스럽게 연결됩니다. 🚀
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 도서관 정보나루 API 조사
|
|
3
|
+
date: 2026-03-15
|
|
4
|
+
description: 도서관 이름으로 위도/경도를 조회할 수 있는 data4library.kr 공공 API 조사 결과
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 도서관 정보나루 API 조사
|
|
8
|
+
|
|
9
|
+
국립중앙도서관이 운영하는 도서관 빅데이터 플랫폼으로, 1,588개 도서관 / 1.98억 장서 / 4천만 회원 / 24억 대출 기록을 보유하고 있다.
|
|
10
|
+
|
|
11
|
+
- Base URL: `http://data4library.kr/api/`
|
|
12
|
+
- 공식 사이트: https://www.data4library.kr/
|
|
13
|
+
|
|
14
|
+
## 도서관 위치 조회 — `/api/libSrch`
|
|
15
|
+
|
|
16
|
+
도서관 이름으로 검색하면 위도/경도를 바로 반환한다. 별도 지오코딩 서비스가 필요 없다.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
GET http://data4library.kr/api/libSrch?authKey=YOUR_KEY&libName=판교&format=json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 요청 파라미터
|
|
23
|
+
|
|
24
|
+
| 파라미터 | 필수 | 설명 |
|
|
25
|
+
| ------------ | ---- | ---------------------------- |
|
|
26
|
+
| `authKey` | O | API 인증키 |
|
|
27
|
+
| `libName` | - | 도서관 이름 (부분 검색 가능) |
|
|
28
|
+
| `region` | - | 지역 코드 (예: `11` = 서울) |
|
|
29
|
+
| `dtl_region` | - | 세부 지역 코드 (예: `11110`) |
|
|
30
|
+
| `format` | - | `json` 또는 XML (기본값) |
|
|
31
|
+
| `pageNo` | - | 페이지 번호 (기본 1) |
|
|
32
|
+
| `pageSize` | - | 페이지 크기 (기본 10) |
|
|
33
|
+
|
|
34
|
+
### 응답 필드
|
|
35
|
+
|
|
36
|
+
| 필드 | 설명 |
|
|
37
|
+
| --------------- | ------------------------- |
|
|
38
|
+
| `libCode` | 도서관 코드 (고유 식별자) |
|
|
39
|
+
| `libName` | 도서관 이름 |
|
|
40
|
+
| `address` | 주소 |
|
|
41
|
+
| `latitude` | 위도 |
|
|
42
|
+
| `longitude` | 경도 |
|
|
43
|
+
| `homepage` | 홈페이지 URL |
|
|
44
|
+
| `tel` | 전화번호 |
|
|
45
|
+
| `fax` | 팩스 |
|
|
46
|
+
| `closed` | 휴관일 |
|
|
47
|
+
| `operatingTime` | 운영시간 |
|
|
48
|
+
| `BookCount` | 장서 수 |
|
|
49
|
+
|
|
50
|
+
### 응답 JSON 구조
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"response": {
|
|
55
|
+
"request": { ... },
|
|
56
|
+
"resultNum": 3,
|
|
57
|
+
"numFound": 3,
|
|
58
|
+
"libs": [
|
|
59
|
+
{
|
|
60
|
+
"lib": {
|
|
61
|
+
"libCode": "111016",
|
|
62
|
+
"libName": "판교도서관",
|
|
63
|
+
"address": "경기도 성남시 ...",
|
|
64
|
+
"latitude": "37.xxxxx",
|
|
65
|
+
"longitude": "127.xxxxx",
|
|
66
|
+
"homepage": "https://...",
|
|
67
|
+
"tel": "031-xxx-xxxx",
|
|
68
|
+
"closed": "매주 월요일",
|
|
69
|
+
"operatingTime": "09:00~18:00",
|
|
70
|
+
"BookCount": "120000"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API 키 발급
|
|
79
|
+
|
|
80
|
+
1. https://data4library.kr/joinPage 에서 회원가입
|
|
81
|
+
2. 로그인 → 마이페이지 → **인증키** 메뉴
|
|
82
|
+
3. 이용 목적 선택, 개인정보 동의 후 신청
|
|
83
|
+
4. **다음 영업일 오전** 승인 완료
|
|
84
|
+
5. 승인 후 마이페이지에서 `authKey` 확인
|
|
85
|
+
|
|
86
|
+
문의처: libdata@korea.kr / 02-595-6131 (도서관 빅데이터 사무국)
|
|
87
|
+
|
|
88
|
+
## 호출 제한
|
|
89
|
+
|
|
90
|
+
| 구분 | 일일 호출 한도 |
|
|
91
|
+
| ---------------- | --------------- |
|
|
92
|
+
| 기본 (IP 미등록) | **500건/일** |
|
|
93
|
+
| 서버 IP 등록 시 | **30,000건/일** |
|
|
94
|
+
|
|
95
|
+
- IP 등록: 마이페이지 → 인증키 관리 → 서버IP 입력
|
|
96
|
+
- 2023년 11월 20일부터 무제한 접근 중단, 최대 30,000건/일
|
|
97
|
+
|
|
98
|
+
## 기타 주요 엔드포인트
|
|
99
|
+
|
|
100
|
+
총 18개의 API 엔드포인트가 제공된다. 이 프로젝트에 유용한 항목:
|
|
101
|
+
|
|
102
|
+
| 엔드포인트 | 설명 |
|
|
103
|
+
| ------------------------ | ------------------------------------------ |
|
|
104
|
+
| `/api/libSrch` | 도서관 검색 (위치, 연락처 등) |
|
|
105
|
+
| `/api/extends/libSrch` | 도서관 정보 + 대출 트렌드 통합 |
|
|
106
|
+
| `/api/bookExist` | 특정 도서관의 도서 소장 여부 확인 |
|
|
107
|
+
| `/api/libSrchByBook` | ISBN으로 소장 도서관 검색 |
|
|
108
|
+
| `/api/srchBooks` | 키워드로 도서 검색 |
|
|
109
|
+
| `/api/srchDtlList` | 도서 상세 정보 (서지, 표지, 대출 통계) |
|
|
110
|
+
| `/api/loanItemSrch` | 인기 대출 도서 (성별, 연령, 지역별 필터) |
|
|
111
|
+
| `/api/hotTrend` | 급상승 대출 도서 (7일 기준) |
|
|
112
|
+
| `/api/usageAnalysisList` | 도서 이용 분석 (서지 + 대출 + 키워드 통합) |
|
|
113
|
+
|
|
114
|
+
## 이 프로젝트와의 연관성
|
|
115
|
+
|
|
116
|
+
- 현재 `LibraryInfo`는 `{ code, name }` 두 필드만 보유 — 좌표 정보 없음
|
|
117
|
+
- `/api/libSrch`로 190+개 도서관 좌표를 일괄 수집 가능 (500건/일이면 하루 완료)
|
|
118
|
+
- 한 번 수집한 좌표를 `LibraryInfo`에 캐싱하면 지도 기능 구현 가능
|
|
119
|
+
- `/api/bookExist`는 현재 프로젝트의 도서 소장 확인 기능과 유사
|
package/opencode.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dongnelibrary",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.13",
|
|
4
4
|
"description": "책을 빌릴 수 있는지 확인한다.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.22.0"
|
|
@@ -19,11 +19,16 @@
|
|
|
19
19
|
"gg": "npm run build && node --test test/gg.spec.js",
|
|
20
20
|
"gunpo": "npm run build && node --test test/gunpo.spec.js",
|
|
21
21
|
"hscity": "npm run build && node --test test/hscity.spec.js",
|
|
22
|
+
"ice": "npm run build && node --test test/ice.spec.js",
|
|
22
23
|
"osan": "npm run build && node --test test/osan.spec.js",
|
|
23
24
|
"snlib": "npm run build && node --test test/snlib.spec.js",
|
|
24
25
|
"suwon": "npm run build && node --test test/suwon.spec.js",
|
|
25
26
|
"yjlib": "npm run build && node --test test/yjlib.spec.js",
|
|
26
27
|
"yongin": "npm run build && node --test test/yongin.spec.js",
|
|
28
|
+
"jeju": "npm run build && node --test test/jeju.spec.js",
|
|
29
|
+
"wonju": "npm run build && node --test test/wonju.spec.js",
|
|
30
|
+
"cancel": "npm run build && node --test test/cancel.spec.js",
|
|
31
|
+
"util": "npm run build && node --test test/util.spec.js",
|
|
27
32
|
"cli": "npm run build && node --test test/cli.spec.js"
|
|
28
33
|
},
|
|
29
34
|
"repository": {
|
|
@@ -53,7 +58,7 @@
|
|
|
53
58
|
"@inquirer/prompts": "^7.0.0",
|
|
54
59
|
"async": "^2.5.0",
|
|
55
60
|
"colors": "^1.4.0",
|
|
56
|
-
"commander": "^
|
|
61
|
+
"commander": "^14.0.3",
|
|
57
62
|
"configstore": "^4.0.0",
|
|
58
63
|
"figlet": "^1.2.1",
|
|
59
64
|
"jsdom": "^21.1.1",
|