도서관 책을 빌릴 수 있는지 알려주는 Web API 서비스
A Korean library book availability checker that provides both a web interface and REST API to search for books across multiple libraries in the Seoul metropolitan area.
- 🔍 Search books across multiple Korean libraries (판교, 동탄, 성남, etc.)
- 📚 Real-time availability checking
- 🔗 Direct links to book detail pages
- 🌐 Web UI and REST API endpoints
- 🎨 Modern responsive interface using Tailwind CSS framework
- 📱 Mobile-friendly design with touch-optimized controls
- 🔎 Incremental library search filter
- 📜 Search history with localStorage persistence
- ⭐ "My libraries": libraries searched one at a time are remembered (up to 5) and shown as one-tap chips
- 🗺️ Region filter: pick 시·도 → 시·군·구 to narrow the library list and the module list (mapping in
src/data/libraryRegions.json) - ⚛️ React 19 frontend with Vite
- 🛑 Cancel-safe requests — client disconnect aborts in-flight scraping via
res.on("close")
Backend:
- Node.js (>=22.22.0)
- Express.js
- dongnelibrary npm package
Frontend:
- React 19
- Vite 7
- Tailwind CSS
git clone https://github.com/afrontend/dlserver.git
cd dlserver
npm install
npm run build
npm run webapp
Server will start on port 3000 (or use PORT environment variable to customize)
ANTHROPIC_API_KEY: Required for the/api/agent-streamendpoint. Set in your deployment environment (e.g. Render environment variables). Never exposed to the client.UNREACHABLE_MODULES: Optional comma-separated module names the server cannot reach (e.g.화성시립도서관blocks overseas IPs). Searches for their libraries answer "unreachable" at once instead of waiting for a timeout, and/moduleListmarks them withunreachable: true. Modules that fail to connect twice in a row are also marked automatically for 10 minutes.VITE_GA_ID: Optional GA4 measurement ID for the Vite web app. Example:G-XXXXXXXXXX. If unset, production builds use the project's default ID insrc/analytics.ts. Development builds send nothing.- Events:
page_view,search(search_scope:single/group/all,library_count),search_complete(library_count,failed_count,result_count,available_count,duration_ms),search_cancel(library_count,completed_count,duration_ms),my_library_select
- Events:
src/data/libraryRegions.json maps each library to 시·도 → 시·군·구 → module (kept per module because the same library name exists in several modules). It is generated from /moduleList and the public "전국도서관표준데이터" CSV (cp949). Regenerate and commit it whenever library modules change:
curl -s http://localhost:3000/moduleList > /tmp/modules.json
python3 scripts/build-library-regions.py /tmp/modules.json <national_library_standard.csv>
Libraries that cannot be placed in a 시·군·구 (multi-region 교육청 modules) are listed under "" and appear only when the whole 시·도 is selected.
Copy .env.example to .env or set variables in your deployment environment before building.
Development with hot reload:
npm run webapp # Terminal 1: Start Express server
npm run dev # Terminal 2: Start Vite dev server with hot reload
Build for production:
npm run build # Outputs to dist/
To run the MCP server instead:
npm start
GET /
Main web application interface for searching books
GET /:title/:libraryName
Returns formatted HTML with book availability markers (✓/✖)
Example: http://localhost:3000/javascript/판교
GET /search?title=<title>&libraryName=<libraryName>
Returns JSON array of book objects with availability data
Example: http://localhost:3000/search?title=javascript&libraryName=판교
GET /libraryList
Returns JSON array of all library names
로컬 서버에서 확인 (http://localhost:3000/)
서버에서 확인 (https://dongne.onrender.com/)
- Open http://localhost:3000/ in your browser
- Use the library search filter to find a specific library by typing part of its name
- Select a library from the dropdown (or select "도서관을 선택하세요." to search all libraries)
- Enter a book title (recent searches appear in a dropdown when focusing the input)
- Click search to see availability across selected library/libraries
Get all libraries:
curl http://localhost:3000/libraryListSearch for a book:
curl "http://localhost:3000/search?title=javascript&libraryName=판교"Response format:
[
{
"title": "Book Title",
"exist": true,
"libraryName": "Library Name",
"bookUrl": "https://..."
}
]- Backend (server.ts): Express.js server with endpoints for book search, library list, and module list retrieval
- Frontend (src/): React 19 application built with Vite, featuring search bar, library selector, and book list components
- Data Source: Uses the
dongnelibrarynpm package for library API integration - Styling: Tailwind CSS framework for modern, responsive UI
POST /api/agent-stream
Content-Type: application/json
{ "query": "판교 도서관에서 채식주의자 있어?" }
Server-Sent Events response — streams agent events as Claude Haiku processes the query:
data: {"type":"core_agent", "data":{"intent":"Knowledge","confidence":0.95}}
data: {"type":"domain_agent","data":{"name":"도서관 검색 Agent (dlserver)"}}
data: {"type":"tool_call", "data":{"name":"search_books","input":{"title":"채식주의자","libraryName":"판교"}}}
data: {"type":"tool_result", "data":{"toolName":"search_books","output":"[...]"}}
data: {"type":"response", "data":"판교도서관에 채식주의자가 있으며 현재 대출 가능합니다."}
Implementation notes:
- Uses
res.on("close")(notreq) to detect client disconnect and abort in-flight requests - Sends
: pingheartbeat every 1 s to keep the SSE connection alive during Anthropic API latency tool_choice: { type: "any" }on the first Anthropic call forces tool use;"auto"on subsequent iterationsANTHROPIC_API_KEYis read fromprocess.env— never sent to the browser
This endpoint powers the Live mode in AgentSandbox.
This project includes MCP (Model Context Protocol) server implementations:
| 파일 | 언어 | 방식 | 상세 문서 |
|---|---|---|---|
mcp-server-STDIO.ts |
TypeScript | STDIO | MCPSTDIO_README.md |
mcp-server-SSE.ts |
TypeScript | HTTP/SSE | MCPSSE_README.md |
mcp-server-STDIO.py |
Python (FastMCP) | STDIO | PYMCPSTDIO_README.md |
mcp-server-SSE.py |
Python (FastMCP) | HTTP/SSE | PYMCPSSE_README.md |
MCP Inspector를 사용하면 브라우저 UI에서 MCP 서버 도구를 바로 테스트할 수 있습니다.
nvm을 사용하는 경우 아래 명령어에서 node 경로를
~/.nvm/versions/node/$(node -v)/bin으로 교체하세요.
PATH=/path/to/node/bin:$PATH npx @modelcontextprotocol/inspector npx tsx mcp-server-STDIO.tsInspector UI에서 Transport Type이 STDIO로 자동 설정됩니다.
1단계 — 서버 실행:
npm run mcpsse2단계 — Inspector 실행:
PATH=/path/to/node/bin:$PATH npx @modelcontextprotocol/inspectorInspector UI에서 다음과 같이 설정 후 Connect:
- Transport Type: Streamable HTTP
- URL:
http://localhost:3000/mcp
list_libraries— 검색 가능한 전체 도서관 목록 반환search_books— 도서 제목과 도서관 이름으로 대출 가능 여부 검색 (libraryName생략 시 전체 검색)
Claude에서 원격 MCP 서버를 연결하여 사용할 수 있다.
Claude 설정 > 커넥터 > 커스텀 커넥터 추가 > 원격 MCP 서버 URL 입력
이 프로젝트는 아래 두 사이드 프로젝트와 하나의 포트폴리오 서사를 구성합니다.
| AgentSandbox | Dongne (dlserver) | IVI Playground |
|---|---|---|
| AI 에이전트 라우팅 시각화 | MCP 서버 + 풀스택 API | 차량 WebView 개발 도구 |
공통: AbortController · SSE 스트리밍 · Mock-first
| 연결 | 내용 |
|---|---|
| → AgentSandbox | list_libraries / search_books MCP Tool이 AgentSandbox Live 모드에서 실연동. Claude Haiku가 이 서버를 실제 도구로 호출하며 AgentEvent 스트림으로 시각화됨 |
| ← IVI Playground | AbortController 기반 요청 취소 패턴 공유. 클라이언트 연결 해제 시 스크래핑까지 즉시 중단하는 구조가 IVI의 ScenarioPlayer 중단 로직과 동일 |
Live 데모: AgentSandbox · Dongne · IVI Playground
MIT © Bob Hwang