728x90
반응형
국토교통부 API (교통, 부동산 정보) 완전 정복
개요
국토교통부에서 제공하는 공공데이터 API는 교통, 부동산, 건축물 등 국토 전반에 관한 다양한 정보를 제공합니다. 부동산 앱, 교통 정보 서비스, 건축물 관리 시스템 등에서 핵심적으로 활용되는 데이터입니다.
주요 API 서비스 분류
🏠 부동산 관련 API
1. 아파트매매 실거래가 조회
- 용도: 아파트 실제 거래 가격 정보
- 제공 정보: 거래금액, 거래년월, 동, 아파트명, 전용면적 등
- 갱신 주기: 월 1회 (매월 말일 기준)
2. 연립다세대 매매 실거래가 조회
- 용도: 연립주택, 다세대주택 거래 정보
- 제공 정보: 거래금액, 건축년도, 대지면적, 연면적 등
- 갱신 주기: 월 1회
3. 단독주택 매매 실거래가 조회
- 용도: 단독주택 거래 정보
- 제공 정보: 거래금액, 대지면적, 건물면적, 건축년도 등
- 갱신 주기: 월 1회
4. 오피스텔 매매 실거래가 조회
- 용도: 오피스텔 거래 정보
- 제공 정보: 거래금액, 전용면적, 건축년도, 층 등
- 갱신 주기: 월 1회
5. 상업업무용 부동산 매매 실거래가 조회
- 용도: 상가, 사무실 등 상업용 부동산 정보
- 제공 정보: 거래금액, 건물용도, 대지면적, 건물면적 등
- 갱신 주기: 월 1회
🚗 교통 관련 API
1. 전국 고속도로 교통량 정보
- 용도: 고속도로 구간별 교통량
- 제공 정보: 교통량, 통행속도, 교통상황 등
- 갱신 주기: 5분마다
2. 국도 교통량 정보
- 용도: 일반국도 교통량 데이터
- 제공 정보: 차종별 교통량, 속도 정보
- 갱신 주기: 5분마다
3. 고속도로 휴게소 정보
- 용도: 휴게소 위치, 시설 정보
- 제공 정보: 휴게소명, 위치, 편의시설, 연락처 등
- 갱신 주기: 월 1회
4. 버스정류장 위치정보
- 용도: 전국 버스정류장 좌표 및 정보
- 제공 정보: 정류장명, 위경도 좌표, 관리기관 등
- 갱신 주기: 분기 1회
🏢 건축물 관련 API
1. 건축물대장 정보
- 용도: 건축물 기본 정보
- 제공 정보: 건축면적, 연면적, 용도, 구조, 층수 등
- 갱신 주기: 일 1회
2. 건축인허가 정보
- 용도: 건축 허가 및 신고 정보
- 제공 정보: 허가일자, 건축면적, 용도, 구조 등
- 갱신 주기: 일 1회
API 사용 준비하기
1단계: 서비스 키 발급
- 공공데이터포털 접속
- 원하는 서비스 검색 (예: "아파트매매 실거래가")
- 활용신청 후 승인 대기
- 승인 후 서비스키 확인
2단계: 지역 코드 이해
국토교통부 API는 법정동 코드를 기반으로 합니다.
- 시도코드: 2자리 (11: 서울, 26: 부산 등)
- 시군구코드: 5자리 (11110: 서울 종로구 등)
- 읍면동코드: 8자리 또는 10자리
핵심 API 상세 가이드
아파트 실거래가 조회 (가장 인기)
API 엔드포인트:
http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTradeDev
필수 파라미터:
- serviceKey: 발급받은 API 인증키
- LAWD_CD: 지역코드 (5자리 법정동코드)
- DEAL_YMD: 계약월 (YYYYMM 형식)
선택 파라미터:
- numOfRows: 한 페이지 결과 수 (기본값: 10)
- pageNo: 페이지 번호 (기본값: 1)
호출 예시:
const API_KEY = 'your_service_key_here';
const REGION_CODE = '11110'; // 서울 종로구
const DEAL_MONTH = '202312'; // 2023년 12월
const url = `http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc/getRTMSDataSvcAptTradeDev?serviceKey=${API_KEY}&LAWD_CD=${REGION_CODE}&DEAL_YMD=${DEAL_MONTH}&numOfRows=1000`;
fetch(url)
.then(response => response.text())
.then(xmlText => {
// XML 파싱 필요
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
console.log('아파트 거래 데이터:', xmlDoc);
})
.catch(error => {
console.error('API 호출 오류:', error);
});
XML 응답 데이터 구조
국토교통부 API는 XML 형태로 데이터를 제공합니다:
<response>
<header>
<resultCode>00</resultCode>
<resultMsg>NORMAL SERVICE.</resultMsg>
</header>
<body>
<items>
<item>
<거래금액>82,000</거래금액>
<건축년도>2008</건축년도>
<년>2023</년>
<동>1204</동>
<등기일자>2023-12-15</등기일자>
<매매일>15</매매일>
<매매월>12</매매월>
<매매년>2023</매매년>
<아파트>래미안 첼리투스</아파트>
<전용면적>84.93</전용면적>
<지번>189</지번>
<지역코드>11110</지역코드>
<층>12</층>
</item>
</items>
<numOfRows>1000</numOfRows>
<pageNo>1</pageNo>
<totalCount>158</totalCount>
</body>
</response>
실용적인 XML 파싱 함수
class RealEstateAPI {
constructor(serviceKey) {
this.serviceKey = serviceKey;
this.baseUrl = 'http://openapi.molit.go.kr/OpenAPI_ToolInstallPackage/service/rest/RTMSOBJSvc';
}
// XML을 JavaScript 객체로 변환
parseXMLResponse(xmlText) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
// 에러 체크
const resultCode = xmlDoc.querySelector('resultCode')?.textContent;
if (resultCode !== '00') {
const resultMsg = xmlDoc.querySelector('resultMsg')?.textContent;
throw new Error(`API 오류: ${resultMsg}`);
}
// 데이터 추출
const items = xmlDoc.querySelectorAll('item');
const result = [];
items.forEach(item => {
const data = {};
const children = item.children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
data[child.nodeName] = child.textContent;
}
result.push(data);
});
return result;
}
// 아파트 실거래가 조회
async getApartmentDeals(regionCode, dealMonth) {
try {
const url = `${this.baseUrl}/getRTMSDataSvcAptTradeDev?` +
`serviceKey=${this.serviceKey}&` +
`LAWD_CD=${regionCode}&` +
`DEAL_YMD=${dealMonth}&` +
`numOfRows=1000`;
const response = await fetch(url);
const xmlText = await response.text();
const data = this.parseXMLResponse(xmlText);
return this.formatApartmentData(data);
} catch (error) {
console.error('아파트 실거래가 조회 실패:', error);
throw error;
}
}
// 데이터를 사용자 친화적 형태로 변환
formatApartmentData(rawData) {
return rawData.map(item => ({
apartmentName: item['아파트'],
dealPrice: this.parsePrice(item['거래금액']),
dealDate: `${item['년']}-${item['월'].padStart(2, '0')}-${item['일'].padStart(2, '0')}`,
exclusiveArea: parseFloat(item['전용면적']),
floor: parseInt(item['층']),
buildYear: parseInt(item['건축년도']),
dong: item['법정동'],
jibun: item['지번'],
regionCode: item['지역코드']
}));
}
// 거래금액 파싱 (쉼표 제거, 숫자 변환)
parsePrice(priceString) {
const cleaned = priceString.replace(/,/g, '').trim();
return parseInt(cleaned) * 10000; // 만원 단위를 원 단위로 변환
}
}
// 사용법
const realEstateAPI = new RealEstateAPI('your_service_key');
realEstateAPI.getApartmentDeals('11110', '202312')
.then(deals => {
console.log('아파트 거래 정보:', deals);
// 평균 거래가격 계산
const avgPrice = deals.reduce((sum, deal) => sum + deal.dealPrice, 0) / deals.length;
console.log('평균 거래가격:', avgPrice.toLocaleString() + '원');
})
.catch(error => {
console.error('오류:', error);
});
교통량 정보 조회
API 엔드포인트:
http://data.ex.co.kr/openapi/business/trafficAmountBySection
호출 예시:
class TrafficAPI {
constructor(serviceKey) {
this.serviceKey = serviceKey;
}
async getHighwayTraffic(routeCode, startDate, endDate) {
try {
const url = `http://data.ex.co.kr/openapi/business/trafficAmountBySection?` +
`key=${this.serviceKey}&` +
`type=json&` +
`routeCode=${routeCode}&` +
`startDate=${startDate}&` +
`endDate=${endDate}`;
const response = await fetch(url);
const data = await response.json();
return this.formatTrafficData(data.list);
} catch (error) {
console.error('교통량 정보 조회 실패:', error);
throw error;
}
}
formatTrafficData(rawData) {
return rawData.map(item => ({
routeName: item.routeName,
sectionName: item.sectionName,
totalTraffic: parseInt(item.trafficAmountSum),
avgTraffic: Math.round(parseInt(item.trafficAmountSum) / 24), // 시간당 평균
date: item.dateCode
}));
}
}
실제 프로젝트 구현 예제
부동산 가격 분석 대시보드
class RealEstateDashboard {
constructor(serviceKey) {
this.api = new RealEstateAPI(serviceKey);
this.regionCodes = {
'서울 강남구': '11680',
'서울 서초구': '11650',
'서울 송파구': '11710',
'부산 해운대구': '26440',
'대구 수성구': '27200'
};
}
// 여러 지역의 최근 3개월 거래 데이터 수집
async collectMultiRegionData() {
const results = {};
const recentMonths = this.getRecentMonths(3);
for (const [regionName, regionCode] of Object.entries(this.regionCodes)) {
results[regionName] = [];
for (const month of recentMonths) {
try {
const deals = await this.api.getApartmentDeals(regionCode, month);
results[regionName] = results[regionName].concat(deals);
// API 호출 제한 고려 (1초 대기)
await this.delay(1000);
} catch (error) {
console.error(`${regionName} ${month} 데이터 수집 실패:`, error);
}
}
}
return results;
}
// 지역별 평균 가격 분석
analyzeRegionalPrices(data) {
const analysis = {};
for (const [region, deals] of Object.entries(data)) {
if (deals.length === 0) continue;
const prices = deals.map(deal => deal.dealPrice);
const areas = deals.map(deal => deal.exclusiveArea);
analysis[region] = {
totalDeals: deals.length,
avgPrice: Math.round(prices.reduce((a, b) => a + b, 0) / prices.length),
medianPrice: this.getMedian(prices),
avgPricePerSquare: Math.round(
prices.reduce((sum, price, idx) => sum + (price / areas[idx]), 0) / prices.length
),
priceRange: {
min: Math.min(...prices),
max: Math.max(...prices)
},
mostExpensiveApt: deals.find(deal => deal.dealPrice === Math.max(...prices))?.apartmentName
};
}
return analysis;
}
// 시계열 분석 (월별 가격 추이)
analyzePriceTrends(data) {
const trends = {};
for (const [region, deals] of Object.entries(data)) {
const monthlyData = {};
deals.forEach(deal => {
const month = deal.dealDate.substring(0, 7); // YYYY-MM
if (!monthlyData[month]) {
monthlyData[month] = [];
}
monthlyData[month].push(deal.dealPrice);
});
trends[region] = Object.entries(monthlyData).map(([month, prices]) => ({
month,
avgPrice: Math.round(prices.reduce((a, b) => a + b, 0) / prices.length),
dealCount: prices.length
})).sort((a, b) => a.month.localeCompare(b.month));
}
return trends;
}
// 유틸리티 함수들
getRecentMonths(count) {
const months = [];
const now = new Date();
for (let i = 0; i < count; i++) {
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
const yearMonth = date.getFullYear().toString() +
(date.getMonth() + 1).toString().padStart(2, '0');
months.push(yearMonth);
}
return months;
}
getMedian(numbers) {
const sorted = numbers.slice().sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
return sorted.length % 2 === 0 ?
(sorted[mid - 1] + sorted[mid]) / 2 :
sorted[mid];
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 대시보드 실행
async generateReport() {
console.log('🏠 부동산 시장 분석 리포트 생성 중...\n');
const data = await this.collectMultiRegionData();
const priceAnalysis = this.analyzeRegionalPrices(data);
const trends = this.analyzePriceTrends(data);
// 리포트 출력
console.log('📊 지역별 평균 가격 분석');
console.log('==========================');
for (const [region, analysis] of Object.entries(priceAnalysis)) {
console.log(`\n🏢 ${region}`);
console.log(` 총 거래 건수: ${analysis.totalDeals}건`);
console.log(` 평균 가격: ${analysis.avgPrice.toLocaleString()}원`);
console.log(` 중간값 가격: ${analysis.medianPrice.toLocaleString()}원`);
console.log(` 평당 평균 가격: ${analysis.avgPricePerSquare.toLocaleString()}원/㎡`);
console.log(` 가격 범위: ${analysis.priceRange.min.toLocaleString()}원 ~ ${analysis.priceRange.max.toLocaleString()}원`);
console.log(` 최고가 아파트: ${analysis.mostExpensiveApt}`);
}
console.log('\n📈 월별 가격 추이');
console.log('================');
for (const [region, trend] of Object.entries(trends)) {
console.log(`\n📍 ${region}`);
trend.forEach(monthData => {
console.log(` ${monthData.month}: ${monthData.avgPrice.toLocaleString()}원 (${monthData.dealCount}건)`);
});
}
return { priceAnalysis, trends };
}
}
// 사용법
const dashboard = new RealEstateDashboard('your_service_key');
dashboard.generateReport()
.then(report => {
console.log('\n✅ 리포트 생성 완료!');
})
.catch(error => {
console.error('리포트 생성 실패:', error);
});
지역 코드 조회 및 관리
법정동 코드 검색 함수
class RegionCodeManager {
constructor() {
// 주요 지역 코드 (실제로는 외부 파일에서 로드)
this.codes = {
'서울특별시': {
code: '11',
districts: {
'종로구': '11110',
'중구': '11140',
'용산구': '11170',
'성동구': '11200',
'광진구': '11215',
'동대문구': '11230',
'중랑구': '11260',
'성북구': '11290',
'강북구': '11305',
'도봉구': '11320',
'노원구': '11350',
'은평구': '11380',
'서대문구': '11410',
'마포구': '11440',
'양천구': '11470',
'강서구': '11500',
'구로구': '11530',
'금천구': '11545',
'영등포구': '11560',
'동작구': '11590',
'관악구': '11620',
'서초구': '11650',
'강남구': '11680',
'송파구': '11710',
'강동구': '11740'
}
},
'부산광역시': {
code: '26',
districts: {
'중구': '26110',
'서구': '26140',
'동구': '26170',
'영도구': '26200',
'부산진구': '26230',
'동래구': '26260',
'남구': '26290',
'북구': '26320',
'해운대구': '26440',
'사하구': '26470',
'금정구': '26500',
'강서구': '26530',
'연제구': '26560',
'수영구': '26590',
'사상구': '26620',
'기장군': '26710'
}
}
// 다른 시도도 추가...
};
}
// 지역명으로 코드 검색
findCode(city, district = null) {
const cityData = this.codes[city];
if (!cityData) {
throw new Error(`${city}를 찾을 수 없습니다.`);
}
if (district) {
const districtCode = cityData.districts[district];
if (!districtCode) {
throw new Error(`${city} ${district}를 찾을 수 없습니다.`);
}
return districtCode;
}
return cityData.code;
}
// 자동완성용 지역 목록
getRegionSuggestions(query) {
const suggestions = [];
for (const [city, cityData] of Object.entries(this.codes)) {
if (city.includes(query)) {
suggestions.push(city);
}
for (const district of Object.keys(cityData.districts)) {
if (district.includes(query)) {
suggestions.push(`${city} ${district}`);
}
}
}
return suggestions.slice(0, 10); // 최대 10개만 반환
}
}
자주 발생하는 오류와 해결책
1. SERVICE_ACCESS_DENIED_ERROR
원인: 잘못된 서비스키 또는 미승인 해결:
- 공공데이터포털에서 해당 서비스 승인 상태 확인
- 서비스키 인코딩 문제 체크 (encodeURIComponent() 사용)
2. NO_DATA_ERROR
원인: 해당 조건에 맞는 데이터가 없음 해결:
- 지역코드가 정확한지 확인
- 조회 월이 유효한지 확인 (너무 최근 월은 데이터 없을 수 있음)
3. XML_PARSE_ERROR
원인: XML 응답 파싱 실패 해결:
- 응답 텍스트가 유효한 XML인지 확인
- 브라우저에서는 CORS 문제로 프록시 서버 필요할 수 있음
4. CORS 오류 (브라우저에서)
원인: 브라우저의 동일 출처 정책 해결:
// 프록시 서버 사용 또는 서버사이드에서 호출
const proxyUrl = 'https://cors-anywhere.herokuapp.com/';
const apiUrl = 'http://openapi.molit.go.kr/...';
fetch(proxyUrl + apiUrl)
고급 활용 팁
1. 데이터 캐싱과 배치 처리
class DataCache {
constructor() {
this.cache = new Map();
this.cacheExpiry = new Map();
}
set(key, data, ttl = 3600000) { // 1시간 기본 TTL
this.cache.set(key, data);
this.cacheExpiry.set(key, Date.now() + ttl);
}
get(key) {
if (this.cacheExpiry.get(key) > Date.now()) {
return this.cache.get(key);
}
this.cache.delete(key);
this.cacheExpiry.delete(key);
return null;
}
}
class BatchProcessor {
constructor(api) {
this.api = api;
this.queue = [];
this.isProcessing = false;
this.delay = 1000; // API 호출 간 1초 대기
}
addTask(task) {
this.queue.push(task);
this.processQueue();
}
async processQueue() {
if (this.isProcessing || this.queue.length === 0) return;
this.isProcessing = true;
while (this.queue.length > 0) {
const task = this.queue.shift();
try {
await task();
await new Promise(resolve => setTimeout(resolve, this.delay));
} catch (error) {
console.error('배치 처리 오류:', error);
}
}
this.isProcessing = false;
}
}
2. 데이터 시각화 준비
class ChartDataProcessor {
// 가격 추이 차트용 데이터 변환
static formatPriceTrendChart(trendsData) {
return Object.entries(trendsData).map(([region, trend]) => ({
id: region,
data: trend.map(item => ({
x: item.month,
y: item.avgPrice
}))
}));
}
// 지역별 평균가 비교 차트용 데이터 변환
static formatRegionComparisonChart(analysisData) {
return Object.entries(analysisData).map(([region, data]) => ({
region,
avgPrice: data.avgPrice,
dealCount: data.totalDeals,
pricePerSquare: data.avgPricePerSquare
}));
}
// 가격대별 분포 히스토그램용 데이터
static formatPriceDistribution(dealsData, binCount = 10) {
const prices = dealsData.map(deal => deal.dealPrice);
const min = Math.min(...prices);
const max = Math.max(...prices);
const binSize = (max - min) / binCount;
const bins = Array.from({ length: binCount }, (_, i) => ({
range: `${Math.round((min + i * binSize) / 10000)}만원~${Math.round((min + (i + 1) * binSize) / 10000)}만원`,
count: 0
}));
prices.forEach(price => {
const binIndex = Math.min(Math.floor((price - min) / binSize), binCount - 1);
bins[binIndex].count++;
});
return bins;
}
}
3. 알림 시스템
class PriceAlert {
constructor(api) {
this.api = api;
this.alerts = [];
}
addPriceAlert(regionCode, threshold, callback) {
this.alerts.push({
regionCode,
threshold,
callback,
lastCheck: null
});
}
async checkAlerts() {
const currentMonth = new Date().toISOString().substring(0, 7).replace('-', '');
for (const alert of this.alerts) {
try {
const deals = await this.api.getApartmentDeals(alert.regionCode, currentMonth);
const avgPrice = deals.reduce((sum, deal) => sum + deal.dealPrice, 0) / deals.length;
if (avgPrice > alert.threshold && alert.lastCheck !== currentMonth) {
alert.callback(avgPrice, deals);
alert.lastCheck = currentMonth;
}
} catch (error) {
console.error('알림 체크 오류:', error);
}
}
}
}
결론
국토교통부 API는 부동산과 교통 분야에서 핵심적인 데이터를 제공하는 중요한 자원입니다. XML 파싱과 지역코드 관리만 잘 이해하면 다양한 부동산 서비스와 교통 정보 시스템을 구축할 수 있습니다.
특히 아파트 실거래가 데이터는 부동산 시장 분석, 투자 의사결정 지원, 지역별 시세 비교 등에 매우 유용하며, 교통량 데이터는 물류 최적화, 교통 정보 서비스, 도시 계획 등에 활용할 수 있습니다.
참고 자료
728x90
반응형
'IT와 과학 > API' 카테고리의 다른 글
| 구글 시트·엑셀 API 자동화 완전 가이드 | 인증·쿼터·오류 대처 + 10분 템플릿 (2025.10월 최신) (0) | 2025.10.04 |
|---|---|
| 기상청 API (날씨, 기후 데이터) 완전 정복 (3) | 2025.08.08 |
| 🏧 한국 결제 플랫폼 API 완전 분석: 개발자가 알아야 할 모든 것 (3) | 2025.08.08 |
| 한국 주식 API 생태계 완전 가이드 (2025년 12월 최신판) (2) | 2024.10.12 |
| API 테스트 자동화 도구 총정리: 효율적이고 신뢰성 높은 API 테스트 수행법 (2025-08 업데이트) (3) | 2024.10.12 |