UNPKG

budongsan-api

Version:

국토교통부 아파트 실거래가, 전월세, 단지 정보, 용적률 등 공공데이터 API 래퍼

291 lines (286 loc) 11.9 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { BudongsanAPIClass: () => BudongsanAPIClass, BudongsanUtil: () => budongsan_util_default, SigunguService: () => sigungu_service_default, SigunguServiceClass: () => SigunguServiceClass }); module.exports = __toCommonJS(src_exports); // src/budongsan-api.ts var import_axios = __toESM(require("axios"), 1); var BudongsanAPIClass = class { /** * BudongsanAPI 인스턴스를 생성합니다. * @param serviceKey 공공데이터 포털에서 발급받은 서비스 키 */ constructor(serviceKey) { this.serviceKey = serviceKey; } /** * 아파트 단지 기본 정보를 조회합니다. * @param kaptCode 아파트 단지 코드 * @returns 단지 기본 정보 (object) * @throws API 호출 실패 시 예외가 발생합니다. */ async getApartmentBasicInfo(kaptCode) { const url = `https://apis.data.go.kr/1613000/AptBasisInfoServiceV3/getAphusBassInfoV3?serviceKey=${this.serviceKey}&kaptCode=${kaptCode}`; return this.fetchAndExtract(url); } /** * 아파트 단지 상세 정보를 조회합니다. * @param kaptCode 아파트 단지 코드 * @returns 단지 상세 정보 (object) * @throws API 호출 실패 시 예외가 발생합니다. */ async getApartmentDetailInfo(kaptCode) { const url = `https://apis.data.go.kr/1613000/AptBasisInfoServiceV3/getAphusDtlInfoV3?serviceKey=${this.serviceKey}&kaptCode=${kaptCode}`; return this.fetchAndExtract(url); } /** * 시군구 코드에 따른 아파트 단지 목록을 조회합니다. * @param sigunguCode 시군구 코드 * @param numOfRows 페이지당 결과 수 * @param pageNo 페이지 번호 * @returns 단지 목록 (배열) * @throws API 호출 실패 시 예외가 발생합니다. */ async getApartmentList(sigunguCode, numOfRows = "10000", pageNo = "1") { const url = `https://apis.data.go.kr/1613000/AptListService3/getSigunguAptList3?serviceKey=${this.serviceKey}&sigunguCode=${sigunguCode}&numOfRows=${numOfRows}&pageNo=${pageNo}`; return this.fetchAndExtract(url); } /** * 특정 거래 년월의 아파트 실거래가(기본)를 조회합니다. * @param sigunguCode 시군구 코드 (5자리) * @param DEAL_YMD 거래 년월 (YYYYMM) * @param numOfRows 페이지당 결과 수 * @param pageNo 페이지 번호 * @returns 실거래 정보 목록 (배열) * @throws API 호출 실패 시 예외가 발생합니다. */ async getApartmentTradeBasicList(sigunguCode, DEAL_YMD, numOfRows = "10000", pageNo = "1") { const url = `https://apis.data.go.kr/1613000/RTMSDataSvcAptTrade/getRTMSDataSvcAptTrade?serviceKey=${this.serviceKey}&LAWD_CD=${sigunguCode}&DEAL_YMD=${DEAL_YMD}&numOfRows=${numOfRows}&pageNo=${pageNo}`; return this.fetchAndExtract(url); } /** * 특정 거래 년월의 아파트 실거래가(상세)를 페이지 단위로 조회합니다. * @param sigunguCode 시군구 코드 (5자리) * @param DEAL_YMD 거래 년월 (YYYYMM) * @param numOfRows 페이지당 결과 수 * @param pageNo 페이지 번호 * @returns 실거래 상세 정보 목록 (배열) * @throws API 호출 실패 시 예외가 발생합니다. */ async getApartmentTradeDetailList(sigunguCode, DEAL_YMD, numOfRows = "10000", pageNo = "1") { const url = `https://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev/getRTMSDataSvcAptTradeDev?serviceKey=${this.serviceKey}&pageNo=${pageNo}&numOfRows=${numOfRows}&LAWD_CD=${sigunguCode}&DEAL_YMD=${DEAL_YMD}`; return this.fetchAndExtract(url); } /** * 특정 거래 년월의 아파트 전월세 정보를 조회합니다. * @param sigunguCode 시군구 코드 (5자리) * @param DEAL_YMD 거래 년월 (YYYYMM) * @param numOfRows 페이지당 결과 수 * @param pageNo 페이지 번호 * @returns 전월세 정보 목록 (배열) * @throws API 호출 실패 시 예외가 발생합니다. */ async getApartmentRentList(sigunguCode, DEAL_YMD, numOfRows = "10000", pageNo = "1") { const url = `https://apis.data.go.kr/1613000/RTMSDataSvcAptRent/getRTMSDataSvcAptRent?serviceKey=${this.serviceKey}&pageNo=${pageNo}&numOfRows=${numOfRows}&LAWD_CD=${sigunguCode}&DEAL_YMD=${DEAL_YMD}`; return this.fetchAndExtract(url); } /** * 건축물대장 총괄표제부 정보를 조회합니다. * * @param {string} sigunguCode - 시군구 코드 (예: '11710') * @param {string} bjdongCode - 법정동 코드 (예: '11200') * @param {string} bun - 번지 (예: '0138') * @param {string} ji - 지번 (예: '0000') * @param {string} [numOfRows="10"] - 페이지당 결과 수 (기본값: 10) * @param {string} [pageNo="1"] - 페이지 번호 (기본값: 1) * @returns {Promise<any>} 건축물대장 총괄표제부 API 응답 데이터 */ async getBrRecapTitleList(sigunguCode, bjdongCode, bun, ji, numOfRows = "10", pageNo = "1") { const baseUrl = "https://apis.data.go.kr/1613000/BldRgstHubService/getBrRecapTitleInfo"; const url = `${baseUrl}?serviceKey=${this.serviceKey}&sigunguCd=${sigunguCode}&bjdongCd=${bjdongCode}&platGbCd=0&bun=${bun}&ji=${ji}&startDate=19800101&endDate=20301231&_type=json&numOfRows=${numOfRows}&pageNo=${pageNo}`; return this.fetchAndExtract(url); } /** * 공통 fetch 및 응답 처리 로직 */ async fetchAndExtract(url) { var _a; try { const res = await import_axios.default.get(url); const { header, body } = res.data.response; if (header.resultCode === "00" || header.resultCode === "000") { return ((_a = body == null ? void 0 : body.items) == null ? void 0 : _a.item) || (body == null ? void 0 : body.item) || (body == null ? void 0 : body.items); } else { throw new Error(`API Error: ${header.resultMsg} (code: ${header.resultCode})`); } } catch (error) { if (import_axios.default.isAxiosError(error)) { throw new Error(`Network Error: ${error.message}`); } throw error; } } }; // src/sigungu-service.ts var import_fs = __toESM(require("fs"), 1); var import_path = __toESM(require("path"), 1); var SigunguServiceClass = class _SigunguServiceClass { constructor(dataPath = "./sigungu.json") { this._dataCache = null; this._dataPath = import_path.default.resolve(dataPath); } static getInstance() { if (!_SigunguServiceClass._instance) { _SigunguServiceClass._instance = new _SigunguServiceClass(); } return _SigunguServiceClass._instance; } _loadDataSync() { if (!this._dataCache) { const raw = import_fs.default.readFileSync(this._dataPath, "utf8"); this._dataCache = JSON.parse(raw); } return this._dataCache; } getSigunguList() { const data = this._loadDataSync(); return data.flatMap( ({ sido_name, sido_code, sigungu_array }) => sigungu_array.map(({ sigungu_name, sigungu_code, bjd_array }) => ({ sido_name, sido_code, sigungu_name, sigungu_code // bjd_array, })) ); } getSigunguMap(keyType = "code") { const list = this.getSigunguList(); const map = /* @__PURE__ */ new Map(); list.forEach(({ sigungu_name, sigungu_code, sido_name, sido_code }) => { const key = keyType === "name" ? sigungu_name : sigungu_code; map.set(key, { sigungu_name, sigungu_code, sido_name, sido_code }); }); return map; } getBjdList() { const data = this._loadDataSync(); return data.flatMap( ({ sigungu_array }) => sigungu_array.flatMap(({ bjd_array }) => bjd_array != null ? bjd_array : []) ); } getBjdMapBySigungu(keyType = "name") { const list = this.getSigunguList(); const map = /* @__PURE__ */ new Map(); list.forEach(({ sigungu_name, sigungu_code, bjd_array }) => { const key = keyType === "code" ? sigungu_code : sigungu_name; map.set(key, bjd_array != null ? bjd_array : []); }); return map; } }; var SigunguService = SigunguServiceClass.getInstance(); var sigungu_service_default = SigunguService; // src/budongsan-util.ts var BudongsanUtil = class { /** * 현재 한국 기준 연도와 월을 반환합니다. * @returns {{ year: string, month: string }} */ static getKoreanYearMonth() { const now = /* @__PURE__ */ new Date(); const utc = now.getTime() + now.getTimezoneOffset() * 6e4; const koreaTime = new Date(utc + 9 * 60 * 6e4); return { year: koreaTime.getFullYear().toString(), month: (koreaTime.getMonth() + 1).toString() }; } /** * 시작 연월부터 종료 연월까지의 YYYYMM 문자열 배열을 생성합니다. * @param {number} startYear 시작 연도 * @param {number} startMonth 시작 월 * @param {number} endYear 종료 연도 * @param {number} endMonth 종료 월 * @returns {string[]} YYYYMM 문자열 배열 */ static generateDealYMDRange(startYear, startMonth, endYear, endMonth) { const result = []; let year = startYear; let month = startMonth; while (year < endYear || year === endYear && month <= endMonth) { const ymd = `${year}${month.toString().padStart(2, "0")}`; result.push(ymd); month++; if (month > 12) { month = 1; year++; } } return result; } /** * 숫자 문자열을 한글 화폐 단위로 포맷팅합니다. (예: "55,000" → "5억 5000만") * @param {string|number} amount 원 단위 기준 숫자 또는 문자열 * @returns {string} 한글 화폐 단위 문자열 */ static formatKoreanCurrency(amount) { const unitMap = ["\uC6D0", "\uB9CC", "\uC5B5", "\uC870", "\uACBD", "\uD574"]; const isNegative = String(amount).startsWith("-"); const numericStr = String(amount).replace(/[^0-9]/g, "") + "0000"; const chunks = []; let temp = numericStr; while (temp.length > 0) { chunks.unshift(temp.slice(-4)); temp = temp.slice(0, -4); } const parts = chunks.map((chunk, index) => { const num = parseInt(chunk, 10); if (num === 0) return null; return `${num}${unitMap[chunks.length - 1 - index]}`; }).filter(Boolean); const result = parts.join(" "); return isNegative ? `( -${result} )` : result; } }; var budongsan_util_default = BudongsanUtil; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BudongsanAPIClass, BudongsanUtil, SigunguService, SigunguServiceClass });