Post

공공 API를 활용한 아파트 실거래가 원시 데이터 수집 설계

공공 API를 활용한 아파트 실거래가 원시 데이터 수집 설계

Rate Limit 환경에서 장기 운영 가능한 수집 구조 설계

아파트 가격 분석을 위해 가장 먼저 떠오르는 자료는 한국부동산원의 실거래가지수입니다. 하지만 지수는 가격의 변화 방향을 보여줄 뿐, 실제 거래의 분포나 평형·지역별 체감 가격을 설명해주지는 않습니다. 이에 이번에는 공공 API를 통해 실제 아파트 실거래가 원시 데이터를 직접 수집하고, 이를 기반으로 장기적인 가격 흐름과 정책 변화의 영향을 분석해보기로 했습니다.


왜 ‘원시 데이터(raw)’부터 다시 수집했는가

과거에도 실거래가 데이터를 수집한 경험이 있습니다. 2020년에는 DB에 저장했고, 2022년에는 CSV 형태로 정리해두었습니다.

문제는 다시 사용하려고 했을 때 발생했습니다.

  • 컬럼 기준이 변경되면 기존 CSV로는 모델링이 어려웠고
  • 일부 정제 로직이 데이터에 포함되어 원본 복원이 불가능했으며
  • 새로운 분석 관점(평형별, 동 단위 등)을 적용하기가 쉽지 않았습니다

결국 정제된 데이터부터 저장하는 방식은 장기적으로 확장성과 재사용성에 한계가 있다는 결론에 이르렀습니다.

그래서 이번에는 방향을 바꾸었습니다.

가공 이전의 XML 원본(raw)을 그대로 저장하고,

이후 CSV, DB, OLAP 등 어떤 방식으로든 자유롭게 가공할 수 있는 구조를 목표로 설계했습니다.

원본 데이터만 확보되어 있다면, 분석 기준이나 저장소가 바뀌더라도 언제든 다시 가공할 수 있기 때문입니다.


아파트 실거래가 API 개요

아파트 실거래가 API는 data.go.kr에서 제공하고 있습니다.

  • 국토교통부 아파트 매매 실거래가 상세 자료 : https://www.data.go.kr/data/15126468/openapi.do
  • 국토교통부 아파트 전월세 실거래가 자료 : https://www.data.go.kr/data/15126474/openapi.do
1
2
3
4
5
6
7
{
    "serviceKey": SERVICE_KEY,
    "pageNo": "1",
    "numOfRows": "9999",
    "LAWD_CD": "11110",
    "DEAL_YMD": "202511",
}
  • 조회 조건: 지역 코드(LAWD_CD) × 계약 연월(DEAL_YMD)
  • 응답 포맷: XML

구조 자체는 단순하지만, 다음 두 가지 제약이 있습니다.

  1. 인증키 기준 일 10,000건 호출 제한
  2. 지역 코드(lawd_cd)를 API에서 직접 제공하지 않음

Rate Limit은 예외가 아니라 전제 조건입니다

실거래가 데이터는 2006년부터 현재까지 월 단위로 누적되어 있습니다. 이를 실제 호출 단위로 환산하면 생각보다 규모가 큽니다.

  • 시군구 지역코드(lawd_cd) : 252개
  • 수집기간 : 2006년 ~ 2025년
  • 월 단위 환산 : 약 240개월

이를 단순 계산하면 다음과 같습니다.

252개 지역 × 240개월 = 약 60,480건의 API 호출

이는 단 한 번 전체 데이터를 수집하는 데 필요한 최소 호출 수입니다. data.go.kr 실거래가 API의 인증키 기준 일일 호출 제한이 10,000건임을 고려하면, 아무런 전략 없이 과거 데이터부터 호출할 경우 하루 만에 한도를 초과하는 구조입니다.

과거부터 무작정 수집하면 하루 호출 한도를 금방 초과하게 됩니다. 선택지는 크게 두 가지입니다.

  • 운영 계정을 신청해 호출 쿼터를 늘리는 방법(100,000건으로 상향 가능)
  • 매일 안정적으로 수집되는 구조를 설계하는 방법

이번 프로젝트에서는 후자를 선택했습니다. 또한 이는 단순히 “한 번만 수집하면 끝”인 구조도 아닙니다.

  • 매월 신규 거래 데이터가 추가되며
  • 일부 월은 거래 정정 또는 누락 보완으로 데이터가 변경될 수 있고
  • 장애 또는 실패로 인해 재수집이 필요한 경우도 발생합니다

즉, 실거래가 수집은 일회성 작업이 아니라 장기적으로 반복 실행되는 배치 작업이 필요합니다.

핵심 수집 전략

  • 날짜 기준 증분 수집
  • 이미 수집된 (lawd_cd, deal_ymd) 조합은 스킵
  • Rate Limit 감지 시 즉시 중단(Fail Fast)
  • 다음 날 동일 지점부터 자동 재개

이와 같은 접근 방식이 아니면, 실거래가와 같이 장기간 누적되는 공공 데이터를 안정적으로 운영하는 것은 현실적으로 어렵습니다


API 응답은 HTTP 상태 코드만으로 판단할 수 없습니다

공공 API를 운영하면서 가장 신경 쓴 부분은 성공 여부 판단 기준입니다. 실제 사용 중 다음과 같은 사례가 자주 발생했습니다.

  • HTTP 200 응답이지만 데이터가 없는 경우
  • XML 포맷은 정상이나 내부 resultCode가 에러인 경우
  • 해당 월에 거래가 없어 빈 결과가 정상인 경우

이에 따라 다음과 같은 기준을 적용했습니다.

  • HTTP 상태 코드와 XML 내부 상태를 모두 검증
  • “호출 성공”과 “데이터 유효”를 분리하여 판단
  • 사용량 초과(resultCode = 22)는 즉시 중단 처리

이러한 방식이 아니면 불필요한 재시도로 쿼터를 소모하게 됩니다.


지역 코드(lawd_cd) 수집 및 정제 방식

실거래가 API는 지역 코드를 요구하지만, 정작 이 코드를 API 자체에서는 제공하지 않습니다. 이를 해결하기 위해 국토교통부 법정동 데이터를 별도로 수집하여 가공했습니다.

국토교통부 법정동 코드 : https://www.data.go.kr/data/15123287/fileData.do

정제 기준

  • 법정동 코드 중 시군구 단위(앞 5자리)만 사용
  • 삭제된 행정구역 제외
  • 실제 하위 법정동이 하나라도 존재하는 시군구만 인정
  • 시도명 + 시군구명 → lawd_cd 형태의 JSON 맵 구성

이 과정을 통해 API 호출에 바로 사용할 수 있는 district_code.json 파일을 생성했습니다.

1
2
3
4
5
6
7
8
9
{
  ...
  "서울특별시강남구": "11680",
  "서울특별시강동구": "11740",
  "서울특별시강북구": "11305",
  "서울특별시강서구": "11500",
  "서울특별시관악구": "11620",
  ...
}

저장 전략: S3 + gzip + 파티션 구조

XML 원본을 그대로 저장한 이유

  • 가공 로직은 언제든 변경될 수 있음
  • 원본이 없으면 과거 데이터를 재생성할 수 없음
  • 스키마 변경에도 영향을 받지 않아야 함

XML은 데이터 크기가 크지만, gzip 압축 시 약 90% 수준의 용량 감소 효과를 얻어 약 550MB에 약 6만5천개의 파일을 저장할 수 있습니다.

디렉토리 구조

1
2
3
4
5
6
7
raw/
 └─ trade_type=SALE/
    └─ deal_ymd=200601/
       └─ lawd_cd=11110/
          ├─ snapshots/
          │   └─ 2025-12-17.xml.gz
          └─ latest.json

이 구조의 장점

  • 거래 유형 / 연월 / 지역 기준 조회가 용이함
  • 특정 월·지역 단위 재처리가 가능함
  • Raw데이터와 상태 메타데이터 명확한 분리로 상태 파일만 읽어도 판단 가능
  • 날짜 기준 스냅샷으로 누적 저장하기 때문에 데이터 변경 이력 추적, 데이터 신뢰성 검증 가능
  • OLAP 또는 Parquet 변환 시 파티션 구조와 자연스럽게 매핑됨
  • 디렉토리 단위로 정책을 적용할 수 있기 때문에 S3 Lifecycle 정책 적용이 쉬움

latest.json의 역할

latest.json은 단순한 메타데이터 파일이 아닙니다. 수집 상태를 코드가 아닌 데이터로 관리하기 위한 핵심 요소입니다. 이 파일을 통해 다음이 가능해집니다.

  • 데이터 변경 여부 판단(hash 기반)
  • 재수집 필요 여부 결정
  • 부분 실패 후 안전한 재시작
  • 수집 상태에 대한 가시성 확보
1
2
3
4
5
6
7
8
9
10
11
{
  "trade_type":"SALE",
  "region_name":"서울특별시종로구",
  "lawd_cd":"11110",
  "deal_ymd":"200601",
  "latest_snapshot_date":"2025-12-17",
  "latest_file":"snapshots/2025-12-17.xml.gz",
  "content_hash":"...",
  "record_count":24,
  "checked_at":"2025-12-17T01:41:57.882293"
}

결론

공공 데이터는 무료로 제공되지만, 사용량 제한과 불완전한 응답 등으로 인해 운영 관점의 설계가 필수적입니다. 이번에 설계한 구조의 핵심은 속도가 아니라 지속성입니다.

이 방식으로 축적한 원시 데이터는 향후 CSV, DB, OLAP, 시각화 등 어떤 형태로든 유연하게 확장할 수 있는 기반이 될 수 있습니다.

예시코드

모든 코드는 깃헙에 있습니다.

This post is licensed under CC BY 4.0 by the author.