budongsan-api
Version:
국토교통부 아파트 실거래가, 전월세, 단지 정보, 용적률 등 공공데이터 API 래퍼
429 lines (422 loc) • 17.2 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);
// node_modules/tsup/assets/cjs_shims.js
var getImportMetaUrl = () => typeof document === "undefined" ? new URL("file:" + __filename).href : document.currentScript && document.currentScript.src || new URL("main.js", document.baseURI).href;
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
// 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/AptBasisInfoServiceV4/getAphusBassInfoV4?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/AptBasisInfoServiceV4/getAphusDtlInfoV4?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 import_url = require("url");
var SigunguServiceClass = class _SigunguServiceClass {
constructor() {
this._dataCache = null;
this._dataPath = this._getLibraryDataPath();
}
_getLibraryDataPath() {
let currentDir;
if (typeof __dirname !== "undefined") {
currentDir = __dirname;
} else if (typeof importMetaUrl !== "undefined") {
currentDir = import_path.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
} else {
currentDir = process.cwd();
}
const localPath = import_path.default.join(currentDir, "sigungu.json");
if (import_fs.default.existsSync(localPath)) {
return localPath;
}
const parentPath = import_path.default.join(currentDir, "..", "sigungu.json");
if (import_fs.default.existsSync(parentPath)) {
return parentPath;
}
const cwd = process.cwd();
const possiblePaths = [
import_path.default.join(cwd, "node_modules", "budongsan-api", "dist", "sigungu.json"),
import_path.default.join(cwd, "node_modules", "budongsan-api", "sigungu.json")
];
for (const possiblePath of possiblePaths) {
if (import_fs.default.existsSync(possiblePath)) {
return possiblePath;
}
}
return localPath;
}
static getInstance() {
if (!_SigunguServiceClass._instance) {
_SigunguServiceClass._instance = new _SigunguServiceClass();
}
return _SigunguServiceClass._instance;
}
_loadDataSync() {
if (!this._dataCache) {
try {
const raw = import_fs.default.readFileSync(this._dataPath, "utf8");
this._dataCache = JSON.parse(raw);
} catch (error) {
let currentDir;
try {
currentDir = import_path.default.dirname((0, import_url.fileURLToPath)(importMetaUrl));
} catch {
currentDir = __dirname;
}
let errorMessage = `sigungu.json \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
`;
errorMessage += `\uC2DC\uB3C4\uD55C \uACBD\uB85C\uB4E4:
`;
const localPath = import_path.default.join(currentDir, "sigungu.json");
errorMessage += `1. \uB85C\uCEEC \uACBD\uB85C: ${localPath} - ${import_fs.default.existsSync(localPath) ? "\uC874\uC7AC\uD568" : "\uC874\uC7AC\uD558\uC9C0 \uC54A\uC74C"}
`;
const parentPath = import_path.default.join(currentDir, "..", "sigungu.json");
errorMessage += `2. \uC0C1\uC704 \uACBD\uB85C: ${parentPath} - ${import_fs.default.existsSync(parentPath) ? "\uC874\uC7AC\uD568" : "\uC874\uC7AC\uD558\uC9C0 \uC54A\uC74C"}
`;
const cwd = process.cwd();
const nodeModulesPath = import_path.default.join(cwd, "node_modules", "budongsan-api", "sigungu.json");
errorMessage += `3. node_modules \uACBD\uB85C: ${nodeModulesPath} - ${import_fs.default.existsSync(nodeModulesPath) ? "\uC874\uC7AC\uD568" : "\uC874\uC7AC\uD558\uC9C0 \uC54A\uC74C"}
`;
errorMessage += `
\uD574\uACB0 \uBC29\uBC95:
`;
errorMessage += `- sigungu.json \uD30C\uC77C\uC774 \uD328\uD0A4\uC9C0 \uB8E8\uD2B8\uC5D0 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694
`;
errorMessage += `- package.json\uC758 files \uBC30\uC5F4\uC5D0 "sigungu.json"\uC774 \uD3EC\uD568\uB418\uC5B4 \uC788\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694`;
throw new Error(errorMessage);
}
}
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 ?? [])
);
}
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 ?? []);
});
return map;
}
};
var SigunguService = SigunguServiceClass.getInstance();
var sigungu_service_default = SigunguService;
// src/budongsan-util.ts
var import_axios2 = __toESM(require("axios"), 1);
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;
}
};
BudongsanUtil.getGoogleMapLatitudeAndlongitude = async (krjuso, googleApikey) => {
const encodedJuso = encodeURI(krjuso);
const latitudeNlongitude = await (0, import_axios2.default)({
method: "get",
url: `https://maps.googleapis.com/maps/api/geocode/json?address=${encodedJuso}&key=${googleApikey}`
}).then((res) => {
const latitude = res.data.results[0].geometry.location.lat;
const longitude = res.data.results[0].geometry.location.lng;
return {
latitude,
longitude
};
}).catch(() => {
return {
latitude: "",
longitude: ""
};
});
return latitudeNlongitude;
};
BudongsanUtil.getKakaoMapPosition = async (param_juso, kakaoApikey) => {
return await (0, import_axios2.default)({
method: "get",
url: `https://dapi.kakao.com/v2/local/search/address.json?query=${encodeURIComponent(param_juso)}`,
headers: { "Authorization": `KakaoAK ${kakaoApikey}` }
}).then((res) => {
if (res.data.documents && res.data.documents[0] && res.data.documents[0].address && res.data.documents[0].road_address) {
const y = res.data.documents[0].y;
const x = res.data.documents[0].x;
return {
addressName: res.data.documents[0].address.address_name,
roadAddressName: res.data.documents[0].road_address.address_name,
apartKakaoName: res.data.documents[0].road_address.building_name,
latitude: y,
longitude: x
};
} else {
return {
addressName: "",
roadAddressName: "",
apartKakaoName: "",
latitude: "",
longitude: ""
};
}
}).catch(() => {
return {
addressName: "",
roadAddressName: "",
apartKakaoName: "",
latitude: "",
longitude: ""
};
});
};
BudongsanUtil.getKakaoCategory = async (param_y, param_x, param_category_group_code, kakaoApikey) => {
return await (0, import_axios2.default)({
method: "get",
url: `https://dapi.kakao.com/v2/local/search/category.json`,
headers: { "Authorization": `KakaoAK ${kakaoApikey}` },
params: {
y: param_y,
x: param_x,
category_group_code: param_category_group_code,
radius: 4e3
}
}).then((res) => {
return res.data.documents;
}).catch(() => {
return [];
});
};
var budongsan_util_default = BudongsanUtil;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BudongsanAPIClass,
BudongsanUtil,
SigunguService,
SigunguServiceClass
});