budongsan-api
Version:
국토교통부 아파트 실거래가, 전월세, 단지 정보, 용적률 등 공공데이터 API 래퍼
291 lines (286 loc) • 11.9 kB
JavaScript
;
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
});