UNPKG

budongsan-api

Version:

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

251 lines (248 loc) 10 kB
// src/budongsan-api.ts import axios from "axios"; 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 axios.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 (axios.isAxiosError(error)) { throw new Error(`Network Error: ${error.message}`); } throw error; } } }; // src/sigungu-service.ts import fs from "fs"; import path from "path"; var SigunguServiceClass = class _SigunguServiceClass { constructor(dataPath = "./sigungu.json") { this._dataCache = null; this._dataPath = path.resolve(dataPath); } static getInstance() { if (!_SigunguServiceClass._instance) { _SigunguServiceClass._instance = new _SigunguServiceClass(); } return _SigunguServiceClass._instance; } _loadDataSync() { if (!this._dataCache) { const raw = fs.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; export { BudongsanAPIClass, budongsan_util_default as BudongsanUtil, sigungu_service_default as SigunguService, SigunguServiceClass };