kpubdata 0.2.1__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. {kpubdata-0.2.1 → kpubdata-0.2.3}/ARCHITECTURE.md +14 -34
  2. {kpubdata-0.2.1 → kpubdata-0.2.3}/PKG-INFO +8 -2
  3. {kpubdata-0.2.1 → kpubdata-0.2.3}/PROVIDER_ADAPTER_CONTRACT.md +9 -1
  4. {kpubdata-0.2.1 → kpubdata-0.2.3}/README.md +7 -1
  5. {kpubdata-0.2.1 → kpubdata-0.2.3}/SUPPORTED_DATA.md +7 -1
  6. {kpubdata-0.2.1 → kpubdata-0.2.3}/pyproject.toml +1 -1
  7. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/core/dataset.py +7 -2
  8. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/datago/adapter.py +14 -1
  9. kpubdata-0.2.3/src/kpubdata/providers/datago/catalogue.json +197 -0
  10. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/lofin/adapter.py +19 -5
  11. kpubdata-0.2.3/tests/fixtures/datago/success_apt_rent.json +55 -0
  12. kpubdata-0.2.3/tests/fixtures/datago/success_offi_rent.json +57 -0
  13. kpubdata-0.2.3/tests/fixtures/datago/success_offi_trade.json +57 -0
  14. kpubdata-0.2.3/tests/fixtures/datago/success_rh_rent.json +57 -0
  15. kpubdata-0.2.3/tests/fixtures/datago/success_rh_trade.json +61 -0
  16. kpubdata-0.2.3/tests/fixtures/datago/success_sh_rent.json +51 -0
  17. kpubdata-0.2.3/tests/fixtures/datago/success_sh_trade.json +55 -0
  18. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_dataset.py +37 -0
  19. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_to_pandas.py +29 -1
  20. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/datago/test_adapter.py +95 -5
  21. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/datago/test_fixtures.py +101 -1
  22. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/lofin/test_lofin_adapter.py +35 -0
  23. {kpubdata-0.2.1 → kpubdata-0.2.3}/uv.lock +1 -1
  24. kpubdata-0.2.1/src/kpubdata/providers/datago/catalogue.json +0 -92
  25. {kpubdata-0.2.1 → kpubdata-0.2.3}/.github/ISSUE_TEMPLATE/new-provider-adapter.yml +0 -0
  26. {kpubdata-0.2.1 → kpubdata-0.2.3}/.github/workflows/ci.yml +0 -0
  27. {kpubdata-0.2.1 → kpubdata-0.2.3}/.github/workflows/integration.yml +0 -0
  28. {kpubdata-0.2.1 → kpubdata-0.2.3}/.github/workflows/publish-pypi.yml +0 -0
  29. {kpubdata-0.2.1 → kpubdata-0.2.3}/.gitignore +0 -0
  30. {kpubdata-0.2.1 → kpubdata-0.2.3}/.sisyphus/remove-get-record-plan.md +0 -0
  31. {kpubdata-0.2.1 → kpubdata-0.2.3}/AGENTS.md +0 -0
  32. {kpubdata-0.2.1 → kpubdata-0.2.3}/API_SPEC.md +0 -0
  33. {kpubdata-0.2.1 → kpubdata-0.2.3}/CANONICAL_MODEL.md +0 -0
  34. {kpubdata-0.2.1 → kpubdata-0.2.3}/CHANGELOG.md +0 -0
  35. {kpubdata-0.2.1 → kpubdata-0.2.3}/CONTRIBUTING.md +0 -0
  36. {kpubdata-0.2.1 → kpubdata-0.2.3}/LICENSE +0 -0
  37. {kpubdata-0.2.1 → kpubdata-0.2.3}/PACKAGING.md +0 -0
  38. {kpubdata-0.2.1 → kpubdata-0.2.3}/PRD.md +0 -0
  39. {kpubdata-0.2.1 → kpubdata-0.2.3}/ROADMAP.md +0 -0
  40. {kpubdata-0.2.1 → kpubdata-0.2.3}/VALIDATION.md +0 -0
  41. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/adrs/0001-dialect-inspired-architecture.md +0 -0
  42. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/adrs/0002-standardize-ux-not-native-shape.md +0 -0
  43. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/architecture-diagrams.md +0 -0
  44. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/datago-api-reference.md +0 -0
  45. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/product-family-architecture.md +0 -0
  46. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/providers/bok.md +0 -0
  47. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/providers/datago.md +0 -0
  48. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/providers/kosis.md +0 -0
  49. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/quickstart.md +0 -0
  50. {kpubdata-0.2.1 → kpubdata-0.2.3}/docs/tutorial-ai-agent-workflow.md +0 -0
  51. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/__init__.py +0 -0
  52. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/catalog.py +0 -0
  53. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/client.py +0 -0
  54. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/config.py +0 -0
  55. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/core/__init__.py +0 -0
  56. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/core/capability.py +0 -0
  57. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/core/models.py +0 -0
  58. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/core/protocol.py +0 -0
  59. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/core/representation.py +0 -0
  60. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/exceptions.py +0 -0
  61. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/__init__.py +0 -0
  62. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/_common.py +0 -0
  63. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/bok/__init__.py +0 -0
  64. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/bok/adapter.py +0 -0
  65. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/bok/catalogue.json +0 -0
  66. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/datago/__init__.py +0 -0
  67. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/kosis/__init__.py +0 -0
  68. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/kosis/adapter.py +0 -0
  69. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/kosis/catalogue.json +0 -0
  70. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/lofin/__init__.py +0 -0
  71. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/providers/lofin/catalogue.json +0 -0
  72. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/py.typed +0 -0
  73. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/registry.py +0 -0
  74. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/transport/__init__.py +0 -0
  75. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/transport/decode.py +0 -0
  76. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/transport/http.py +0 -0
  77. {kpubdata-0.2.1 → kpubdata-0.2.3}/src/kpubdata/transport/retry.py +0 -0
  78. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/__init__.py +0 -0
  79. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/contract/__init__.py +0 -0
  80. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/contract/provider_adapter.py +0 -0
  81. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/contract/test_bok.py +0 -0
  82. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/contract/test_datago.py +0 -0
  83. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/contract/test_kosis.py +0 -0
  84. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/contract/test_lofin.py +0 -0
  85. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/bok/error_auth.json +0 -0
  86. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/bok/success_empty.json +0 -0
  87. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/bok/success_single_page.json +0 -0
  88. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/error_auth_30.json +0 -0
  89. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/error_invalid_request_10.json +0 -0
  90. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/error_rate_limit_22.json +0 -0
  91. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/error_service_unavailable_01.json +0 -0
  92. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/error_xml_auth_30.xml +0 -0
  93. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_apt_trade.json +0 -0
  94. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_empty.json +0 -0
  95. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_multi_page_1.json +0 -0
  96. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_multi_page_2.json +0 -0
  97. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_single_item.json +0 -0
  98. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_single_page.json +0 -0
  99. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_string_numerics.json +0 -0
  100. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_xml.xml +0 -0
  101. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/datago/success_xml_single_item.xml +0 -0
  102. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/kosis/error_auth.json +0 -0
  103. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/kosis/success_empty.json +0 -0
  104. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/kosis/success_single_page.json +0 -0
  105. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/lofin/error_auth.json +0 -0
  106. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/lofin/success_fiacrv.json +0 -0
  107. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/fixtures/lofin/success_single_page.json +0 -0
  108. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/__init__.py +0 -0
  109. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/conftest.py +0 -0
  110. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_bok_live.py +0 -0
  111. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_client_flow.py +0 -0
  112. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_datago_live.py +0 -0
  113. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_exception_propagation.py +0 -0
  114. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_kosis_live.py +0 -0
  115. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_lofin_live.py +0 -0
  116. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/integration/test_transport_http.py +0 -0
  117. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/__init__.py +0 -0
  118. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/__init__.py +0 -0
  119. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_dataset_coverage.py +0 -0
  120. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_exceptions.py +0 -0
  121. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_exceptions_coverage.py +0 -0
  122. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_models.py +0 -0
  123. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/core/test_models_fixtures.py +0 -0
  124. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/__init__.py +0 -0
  125. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/bok/test_bok_adapter.py +0 -0
  126. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/datago/__init__.py +0 -0
  127. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/datago/conftest.py +0 -0
  128. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/providers/datago/test_adapter_coverage.py +0 -0
  129. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_catalog.py +0 -0
  130. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_catalog_coverage.py +0 -0
  131. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_catalogue_validation.py +0 -0
  132. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_client_coverage.py +0 -0
  133. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_client_transport_requirements.py +0 -0
  134. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_config.py +0 -0
  135. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_config_coverage.py +0 -0
  136. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_registry.py +0 -0
  137. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/test_registry_coverage.py +0 -0
  138. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/__init__.py +0 -0
  139. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_decode.py +0 -0
  140. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_decode_coverage.py +0 -0
  141. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_http_coverage.py +0 -0
  142. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_http_coverage_extra.py +0 -0
  143. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_logging.py +0 -0
  144. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_retry.py +0 -0
  145. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_retry_after.py +0 -0
  146. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_retry_coverage.py +0 -0
  147. {kpubdata-0.2.1 → kpubdata-0.2.3}/tests/unit/transport/test_transport_requirements.py +0 -0
@@ -210,46 +210,26 @@ sequenceDiagram
210
210
 
211
211
  **핵심 메서드**: 모든 어댑터는 `_require_api_key()` → `self._config.require_provider_key("slug")` 패턴을 사용합니다.
212
212
 
213
- ### 6.4 기관별 키 주입 방식
213
+ ## 7. 페이지네이션 전략 (Pagination Strategy)
214
214
 
215
- 기관마다 키를 HTTP 요청에 넣는 위치와 파라미터 이름이 다릅니다.
215
+ KPubData는 대량의 데이터를 효율적으로 가져오기 위해 두 가지 페이지네이션 전략을 사용합니다.
216
216
 
217
- | Provider | 파라미터 이름 | 주입 위치 | 예시 | 특이사항 |
218
- |---|---|---|---|---|
219
- | `datago` | `serviceKey` | Query parameter | `?serviceKey=KEY` | dataset별 `service_key_param` 메타데이터로 파라미터명 오버라이드 가능 |
220
- | `bok` | _(URL 경로)_ | URL path segment | `/{KEY}/json/{operation}/...` | 쿼리 파라미터가 아닌 **URL 경로**에 직접 삽입 |
221
- | `kosis` | `apiKey` | Query parameter | `?apiKey=KEY` | 표준적인 쿼리 파라미터 방식 |
222
- | `lofin` | `Key` | Query parameter | `?Key=KEY` | 대문자 `K` 필수. `Type=json`도 대문자 `T` 필수 |
217
+ ### 7.1 전략 종류
218
+ - **오프셋 기반 (Offset-based)**: 페이지 번호를 이용해 데이터를 요청합니다. `next_page` 값이 반환됩니다.
219
+ - **커서 기반 (Cursor-based)**: 특정 지점을 가리키는 포인터(커서)를 이용해 다음 데이터를 요청합니다. `next_cursor` 값이 반환됩니다.
223
220
 
224
- ```text
225
- datago: GET https://apis.data.go.kr/.../getVilageFcst?serviceKey=KEY&...
226
- bok: GET https://ecos.bok.or.kr/api/KEY/json/StatisticSearch/...
227
- kosis: GET https://kosis.kr/openapi/Idx/indikatorList.do?apiKey=KEY&...
228
- lofin: GET https://www.lofin365.go.kr/lf/hub/AJGCF?Key=KEY&Type=json&...
229
- ```
230
-
231
- ### 6.5 새 어댑터 추가 시 인증 구현 가이드
232
-
233
- 새로운 데이터 기관을 추가할 때, 인증 관련 구현은 다음 3단계를 따릅니다.
234
-
235
- **1단계**: `_require_api_key()` 메서드를 정의합니다.
236
- ```python
237
- def _require_api_key(self) -> str:
238
- return self._config.require_provider_key("your_provider_slug")
239
- ```
221
+ ### 7.2 동작 원리 및 휴리스틱 (Heuristic)
222
+ 어댑터는 가용한 정보에 따라 다음과 같이 다음 페이지 존재 여부를 결정합니다.
223
+ 1. **정밀 계산**: `total_count` 정보를 알 수 있는 경우(예: bok, lofin), 현재까지 가져온 개수와 전체 개수를 비교해 `next_page`를 정확히 계산합니다.
224
+ 2. **Best-effort 휴리스틱**: 전체 개수 정보가 없는 경우(예: datago), `len(items) == page_size` 공식을 사용합니다.
225
+ - 현재 페이지의 아이템 개수가 요청한 페이지 크기와 같다면, 다음 페이지가 더 있을 것으로 가정합니다.
226
+ - 이 방식은 마지막 데이터가 딱 페이지 크기에 맞춰 끝날 경우, 실제로 데이터가 없는 다음 페이지를 한 번 더 호출하는 **추가 fetch(Extra empty fetch)**가 발생할 수 있습니다. 이는 문서화된 트레이드오프(trade-off)입니다.
240
227
 
241
- **2단계**: 요청 생성 시 키를 해당 기관의 방식에 맞게 주입합니다.
242
- ```python
243
- # Query parameter 방식 (가장 일반적)
244
- params = {"apiKey": self._require_api_key(), ...}
245
-
246
- # URL path 방식 (bok처럼 경로에 넣는 경우)
247
- url = f"{base_url}/{self._require_api_key()}/json/{operation}"
248
- ```
228
+ ### 7.3 list_all()의 처리
229
+ 사용자가 `list_all()` 메서드를 호출하면, KPubData는 어댑터가 반환하는 전략(`next_cursor` 우선, 없으면 `next_page`)을 자동으로 판단하여 모든 데이터를 순회합니다.
249
230
 
250
- **3단계**: `Client`의 `_BUILTIN_PROVIDERS` 튜플에 등록하면, `from_env()`가 자동으로 `KPUBDATA_{SLUG}_API_KEY` 환경변수를 스캔합니다.
231
+ ## 8. 자주 묻는 질문 (FAQ)
251
232
 
252
- ## 7. 자주 묻는 질문 (FAQ)
253
233
 
254
234
  **Q: 새 데이터셋을 추가하려면 어디를 수정하나요?**
255
235
  A: 해당 기관의 어댑터(`providers/<provider>/adapter.py`)와 데이터 목록 파일(`catalogue.json`)을 수정하면 됩니다.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kpubdata
3
- Version: 0.2.1
3
+ Version: 0.2.3
4
4
  Summary: Dialect-inspired Python access framework for Korean public data
5
5
  Author: Yeongseon Choe
6
6
  License: MIT
@@ -340,6 +340,13 @@ print(df.head())
340
340
  | 공공데이터포털 (`datago`) | 버스도착정보 (`bus_arrival`) | 지원 |
341
341
  | 공공데이터포털 (`datago`) | 병원정보 (`hospital_info`) | 지원 |
342
342
  | 공공데이터포털 (`datago`) | 아파트매매 실거래가 (`apt_trade`) | 지원 |
343
+ | 공공데이터포털 (`datago`) | 아파트 전월세 실거래가 (`apt_rent`) | 지원 |
344
+ | 공공데이터포털 (`datago`) | 오피스텔 매매 실거래가 (`offi_trade`) | 지원 |
345
+ | 공공데이터포털 (`datago`) | 오피스텔 전월세 실거래가 (`offi_rent`) | 지원 |
346
+ | 공공데이터포털 (`datago`) | 연립다세대 매매 실거래가 (`rh_trade`) | 지원 |
347
+ | 공공데이터포털 (`datago`) | 연립다세대 전월세 실거래가 (`rh_rent`) | 지원 |
348
+ | 공공데이터포털 (`datago`) | 단독/다가구 매매 실거래가 (`sh_trade`) | 지원 |
349
+ | 공공데이터포털 (`datago`) | 단독/다가구 전월세 실거래가 (`sh_rent`) | 지원 |
343
350
  | 한국은행 ECOS (`bok`) | 기준금리 (`base_rate`) | 지원 |
344
351
  | 통계청 KOSIS (`kosis`) | 인구이동 (`population_migration`) | 지원 |
345
352
  | 지방재정365 (`lofin`) | 세출결산총괄 (`expenditure_budget`) | 지원 |
@@ -412,4 +419,3 @@ KPubData의 설계 철학과 사용 방법을 안내하는 문서 목록입니
412
419
 
413
420
  ### v0.3
414
421
  - 표준 코어를 기반으로 한 경량 [MCP](https://modelcontextprotocol.io/)(Model Context Protocol — AI 모델이 외부 도구를 사용하는 표준 규약) 어댑터 지원
415
-
@@ -167,7 +167,15 @@ classDiagram
167
167
  }
168
168
  ```
169
169
 
170
- ## 4. Capability rules
170
+ ## 4. 페이지네이션 반환 규약 (Pagination Return Contract)
171
+
172
+ 어댑터는 `query_records` 결과로 반환되는 `RecordBatch`에 다음 페이지 정보를 포함해야 합니다.
173
+
174
+ - **`next_page` 또는 `next_cursor` 반환**: 어댑터는 반드시 둘 중 하나를 반환하거나, 마지막 페이지인 경우 둘 다 `None`을 반환해야 합니다.
175
+ - **우선순위**: `list_all()`은 `next_cursor`가 존재할 경우 이를 우선적으로 사용하며, 없을 경우 `next_page`를 사용합니다.
176
+ - **권장 방식**: 가급적 `total_count` 기반의 정밀한 계산 방식을 선호합니다. 하지만 전체 개수 정보를 알 수 없는 경우 `len(items) == page_size` 휴리스틱을 사용하는 것도 허용됩니다.
177
+
178
+ ## 5. Capability rules
171
179
 
172
180
  - declare only what the adapter truly supports
173
181
  - if an operation is unavailable, raise `UnsupportedCapabilityError`
@@ -303,6 +303,13 @@ print(df.head())
303
303
  | 공공데이터포털 (`datago`) | 버스도착정보 (`bus_arrival`) | 지원 |
304
304
  | 공공데이터포털 (`datago`) | 병원정보 (`hospital_info`) | 지원 |
305
305
  | 공공데이터포털 (`datago`) | 아파트매매 실거래가 (`apt_trade`) | 지원 |
306
+ | 공공데이터포털 (`datago`) | 아파트 전월세 실거래가 (`apt_rent`) | 지원 |
307
+ | 공공데이터포털 (`datago`) | 오피스텔 매매 실거래가 (`offi_trade`) | 지원 |
308
+ | 공공데이터포털 (`datago`) | 오피스텔 전월세 실거래가 (`offi_rent`) | 지원 |
309
+ | 공공데이터포털 (`datago`) | 연립다세대 매매 실거래가 (`rh_trade`) | 지원 |
310
+ | 공공데이터포털 (`datago`) | 연립다세대 전월세 실거래가 (`rh_rent`) | 지원 |
311
+ | 공공데이터포털 (`datago`) | 단독/다가구 매매 실거래가 (`sh_trade`) | 지원 |
312
+ | 공공데이터포털 (`datago`) | 단독/다가구 전월세 실거래가 (`sh_rent`) | 지원 |
306
313
  | 한국은행 ECOS (`bok`) | 기준금리 (`base_rate`) | 지원 |
307
314
  | 통계청 KOSIS (`kosis`) | 인구이동 (`population_migration`) | 지원 |
308
315
  | 지방재정365 (`lofin`) | 세출결산총괄 (`expenditure_budget`) | 지원 |
@@ -375,4 +382,3 @@ KPubData의 설계 철학과 사용 방법을 안내하는 문서 목록입니
375
382
 
376
383
  ### v0.3
377
384
  - 표준 코어를 기반으로 한 경량 [MCP](https://modelcontextprotocol.io/)(Model Context Protocol — AI 모델이 외부 도구를 사용하는 표준 규약) 어댑터 지원
378
-
@@ -23,6 +23,13 @@
23
23
  | 상태 | 검증 | 실API 최종 검증일 | Provider | Dataset ID | 데이터셋명 | 인증 | 공식 문서 | 비고 |
24
24
  |---|---|---|---|---|---|---|---|---|
25
25
  | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `apt_trade` | 아파트매매 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
26
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `apt_rent` | 아파트 전월세 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
27
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `offi_trade` | 오피스텔 매매 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
28
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `offi_rent` | 오피스텔 전월세 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
29
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `rh_trade` | 연립다세대 매매 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
30
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `rh_rent` | 연립다세대 전월세 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
31
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `sh_trade` | 단독/다가구 매매 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
32
+ | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `sh_rent` | 단독/다가구 전월세 실거래가 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 국토교통부 제공 |
26
33
  | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `village_fcst` | 단기예보 조회서비스 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 기상청 제공 |
27
34
  | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `ultra_srt_ncst` | 초단기실황 조회서비스 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | 기상청 제공 |
28
35
  | 지원 | 테스트 검증 | - | 공공데이터포털 (`datago`) | `air_quality` | 대기오염정보 조회서비스 | [공공데이터포털](https://www.data.go.kr) 서비스키 | [data.go.kr](https://www.data.go.kr) | |
@@ -41,7 +48,6 @@
41
48
 
42
49
  | 상태 | Provider | Dataset ID | 데이터셋명 | 메모 |
43
50
  |---|---|---|---|---|
44
- | 예정 | 공공데이터포털 (`datago`) | `apt_rent` | 아파트 전월세 실거래가 | `apt_trade`와 인접한 확장 후보 |
45
51
  | 예정 | 기상청 | `weather_forecast` | 동네예보 | provider/adapter 구조 확정 후 착수 |
46
52
 
47
53
  ## 갱신 규칙
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "kpubdata"
7
- version = "0.2.1"
7
+ version = "0.2.3"
8
8
  description = "Dialect-inspired Python access framework for Korean public data"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -135,8 +135,13 @@ class Dataset:
135
135
  page_kwargs = dict(kwargs)
136
136
  batch = self.list(**page_kwargs)
137
137
  yield batch
138
- while batch.next_page is not None:
139
- page_kwargs["page"] = batch.next_page
138
+ while batch.next_page is not None or batch.next_cursor is not None:
139
+ if batch.next_cursor is not None:
140
+ page_kwargs["cursor"] = batch.next_cursor
141
+ _ = page_kwargs.pop("page", None)
142
+ else:
143
+ page_kwargs["page"] = batch.next_page
144
+ _ = page_kwargs.pop("cursor", None)
140
145
  batch = self.list(**page_kwargs)
141
146
  yield batch
142
147
 
@@ -29,6 +29,19 @@ from kpubdata.transport.http import HttpTransport, TransportConfig
29
29
  logger = logging.getLogger("kpubdata.provider.datago")
30
30
 
31
31
 
32
+ def _is_success_code(code: str) -> bool:
33
+ """Return True for any data.go.kr resultCode that signals success.
34
+
35
+ Different endpoint families use different widths for the "no error" code:
36
+ "00" (most APIs) and "000" (RTMS family under apis.data.go.kr/1613000).
37
+ Both — and any zero-valued numeric variant — should be treated as success.
38
+ """
39
+ try:
40
+ return int(code) == 0
41
+ except ValueError:
42
+ return False
43
+
44
+
32
45
  class DataGoAdapter:
33
46
  """Adapter for data.go.kr (공공데이터포털).
34
47
 
@@ -245,7 +258,7 @@ class DataGoAdapter:
245
258
  "data.go.kr result",
246
259
  extra={"result_code": result_code, "result_msg": result_msg, "dataset_id": dataset_id},
247
260
  )
248
- if result_code != "00":
261
+ if not _is_success_code(result_code):
249
262
  self._raise_for_result_code(result_code, result_msg, dataset_id)
250
263
 
251
264
  body_obj = response_dict.get("body")
@@ -0,0 +1,197 @@
1
+ [
2
+ {
3
+ "dataset_key": "village_fcst",
4
+ "name": "동네예보 조회서비스 (Village Forecast)",
5
+ "base_url": "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0",
6
+ "default_operation": "getVilageFcst",
7
+ "representation": "api_json",
8
+ "service_key_param": "serviceKey",
9
+ "format_param": "dataType",
10
+ "description": "Korea Meteorological Administration short-range forecast",
11
+ "operations": ["list", "raw"],
12
+ "query_support": {
13
+ "pagination": "offset",
14
+ "max_page_size": 1000
15
+ }
16
+ },
17
+ {
18
+ "dataset_key": "ultra_srt_ncst",
19
+ "name": "초단기실황 조회서비스 (Ultra Short-term Nowcast)",
20
+ "base_url": "http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0",
21
+ "default_operation": "getUltraSrtNcst",
22
+ "representation": "api_json",
23
+ "service_key_param": "serviceKey",
24
+ "format_param": "dataType",
25
+ "description": "KMA ultra short-term observed weather conditions",
26
+ "operations": ["list", "raw"],
27
+ "query_support": {
28
+ "pagination": "offset",
29
+ "max_page_size": 1000
30
+ }
31
+ },
32
+ {
33
+ "dataset_key": "air_quality",
34
+ "name": "대기오염정보 조회서비스 (Air Quality)",
35
+ "base_url": "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc",
36
+ "default_operation": "getCtprvnRltmMesureDnsty",
37
+ "representation": "api_json",
38
+ "service_key_param": "serviceKey",
39
+ "format_param": "returnType",
40
+ "description": "AirKorea real-time particulate matter and atmospheric pollution",
41
+ "operations": ["list", "raw"],
42
+ "query_support": {
43
+ "pagination": "offset",
44
+ "max_page_size": 1000
45
+ }
46
+ },
47
+ {
48
+ "dataset_key": "bus_arrival",
49
+ "name": "경기도 버스도착정보 조회서비스 (Bus Arrival)",
50
+ "base_url": "http://apis.data.go.kr/6410000/busarrivalservice",
51
+ "default_operation": "getBusArrivalList",
52
+ "representation": "api_json",
53
+ "service_key_param": "serviceKey",
54
+ "format_param": "_type",
55
+ "description": "Gyeonggi-do real-time bus arrival information",
56
+ "operations": ["list", "raw"],
57
+ "query_support": {
58
+ "pagination": "offset",
59
+ "max_page_size": 1000
60
+ }
61
+ },
62
+ {
63
+ "dataset_key": "hospital_info",
64
+ "name": "병원정보서비스 (Hospital Information)",
65
+ "base_url": "http://apis.data.go.kr/B551182/hospInfoServicev2",
66
+ "default_operation": "getHospBasisList",
67
+ "representation": "api_json",
68
+ "service_key_param": "serviceKey",
69
+ "format_param": "_type",
70
+ "description": "Hospital basic information and medical institution lookup",
71
+ "operations": ["list", "raw"],
72
+ "query_support": {
73
+ "pagination": "offset",
74
+ "max_page_size": 1000
75
+ }
76
+ },
77
+ {
78
+ "dataset_key": "apt_trade",
79
+ "name": "아파트매매 실거래자료 (Apartment Trade)",
80
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcAptTradeDev",
81
+ "default_operation": "getRTMSDataSvcAptTradeDev",
82
+ "representation": "api_json",
83
+ "service_key_param": "serviceKey",
84
+ "format_param": "resultType",
85
+ "description": "MOLIT apartment trade real transaction data",
86
+ "operations": ["list", "raw"],
87
+ "query_support": {
88
+ "pagination": "offset",
89
+ "max_page_size": 1000
90
+ }
91
+ },
92
+ {
93
+ "dataset_key": "apt_rent",
94
+ "name": "아파트 전월세 실거래자료 (Apartment Rent)",
95
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcAptRent",
96
+ "default_operation": "getRTMSDataSvcAptRent",
97
+ "representation": "api_json",
98
+ "service_key_param": "serviceKey",
99
+ "format_param": "resultType",
100
+ "description": "MOLIT apartment rent real transaction data",
101
+ "operations": ["list", "raw"],
102
+ "query_support": {
103
+ "pagination": "offset",
104
+ "max_page_size": 1000
105
+ }
106
+ },
107
+ {
108
+ "dataset_key": "offi_trade",
109
+ "name": "오피스텔 매매 실거래자료 (Officetel Trade)",
110
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcOffiTrade",
111
+ "default_operation": "getRTMSDataSvcOffiTrade",
112
+ "representation": "api_json",
113
+ "service_key_param": "serviceKey",
114
+ "format_param": "resultType",
115
+ "description": "MOLIT officetel trade real transaction data",
116
+ "operations": ["list", "raw"],
117
+ "query_support": {
118
+ "pagination": "offset",
119
+ "max_page_size": 1000
120
+ }
121
+ },
122
+ {
123
+ "dataset_key": "offi_rent",
124
+ "name": "오피스텔 전월세 실거래자료 (Officetel Rent)",
125
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcOffiRent",
126
+ "default_operation": "getRTMSDataSvcOffiRent",
127
+ "representation": "api_json",
128
+ "service_key_param": "serviceKey",
129
+ "format_param": "resultType",
130
+ "description": "MOLIT officetel rent real transaction data",
131
+ "operations": ["list", "raw"],
132
+ "query_support": {
133
+ "pagination": "offset",
134
+ "max_page_size": 1000
135
+ }
136
+ },
137
+ {
138
+ "dataset_key": "rh_trade",
139
+ "name": "연립다세대 매매 실거래자료 (Row House Trade)",
140
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcRHTrade",
141
+ "default_operation": "getRTMSDataSvcRHTrade",
142
+ "representation": "api_json",
143
+ "service_key_param": "serviceKey",
144
+ "format_param": "resultType",
145
+ "description": "MOLIT row house trade real transaction data",
146
+ "operations": ["list", "raw"],
147
+ "query_support": {
148
+ "pagination": "offset",
149
+ "max_page_size": 1000
150
+ }
151
+ },
152
+ {
153
+ "dataset_key": "rh_rent",
154
+ "name": "연립다세대 전월세 실거래자료 (Row House Rent)",
155
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcRHRent",
156
+ "default_operation": "getRTMSDataSvcRHRent",
157
+ "representation": "api_json",
158
+ "service_key_param": "serviceKey",
159
+ "format_param": "resultType",
160
+ "description": "MOLIT row house rent real transaction data",
161
+ "operations": ["list", "raw"],
162
+ "query_support": {
163
+ "pagination": "offset",
164
+ "max_page_size": 1000
165
+ }
166
+ },
167
+ {
168
+ "dataset_key": "sh_trade",
169
+ "name": "단독/다가구 매매 실거래자료 (Single House Trade)",
170
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcSHTrade",
171
+ "default_operation": "getRTMSDataSvcSHTrade",
172
+ "representation": "api_json",
173
+ "service_key_param": "serviceKey",
174
+ "format_param": "resultType",
175
+ "description": "MOLIT single house trade real transaction data",
176
+ "operations": ["list", "raw"],
177
+ "query_support": {
178
+ "pagination": "offset",
179
+ "max_page_size": 1000
180
+ }
181
+ },
182
+ {
183
+ "dataset_key": "sh_rent",
184
+ "name": "단독/다가구 전월세 실거래자료 (Single House Rent)",
185
+ "base_url": "http://apis.data.go.kr/1613000/RTMSDataSvcSHRent",
186
+ "default_operation": "getRTMSDataSvcSHRent",
187
+ "representation": "api_json",
188
+ "service_key_param": "serviceKey",
189
+ "format_param": "resultType",
190
+ "description": "MOLIT single house rent real transaction data",
191
+ "operations": ["list", "raw"],
192
+ "query_support": {
193
+ "pagination": "offset",
194
+ "max_page_size": 1000
195
+ }
196
+ }
197
+ ]
@@ -15,12 +15,29 @@ from kpubdata.exceptions import (
15
15
  )
16
16
  from kpubdata.providers._common import build_schema_from_metadata, coerce_int, load_catalogue
17
17
  from kpubdata.transport.decode import decode_json
18
- from kpubdata.transport.http import HttpTransport, TransportConfig
18
+ from kpubdata.transport.http import HttpTransport, TransportConfig, TransportRequirements
19
19
 
20
20
  logger = logging.getLogger("kpubdata.provider.lofin")
21
21
 
22
22
 
23
+ def _lofin_ssl_context() -> ssl.SSLContext:
24
+ """Create an SSL context compatible with the LOFIN server.
25
+
26
+ The LOFIN server (www.lofin365.go.kr) uses TLSv1.2 with AES256-SHA256,
27
+ which requires a relaxed security level in OpenSSL 3.x.
28
+ """
29
+ ctx = ssl.create_default_context()
30
+ ctx.check_hostname = False
31
+ ctx.verify_mode = ssl.CERT_NONE
32
+ ctx.set_ciphers("DEFAULT:@SECLEVEL=1")
33
+ return ctx
34
+
35
+
23
36
  class LofinAdapter:
37
+ transport_requirements: TransportRequirements = TransportRequirements(
38
+ ssl_context_factory=_lofin_ssl_context,
39
+ )
40
+
24
41
  def __init__(
25
42
  self,
26
43
  *,
@@ -32,10 +49,7 @@ class LofinAdapter:
32
49
  if transport is not None:
33
50
  self._transport: HttpTransport = transport
34
51
  else:
35
- ssl_ctx = ssl.create_default_context()
36
- ssl_ctx.check_hostname = False
37
- ssl_ctx.verify_mode = ssl.CERT_NONE
38
- ssl_ctx.set_ciphers("DEFAULT:@SECLEVEL=1")
52
+ ssl_ctx = _lofin_ssl_context()
39
53
  transport_config = TransportConfig(
40
54
  timeout=self._config.timeout,
41
55
  max_retries=self._config.max_retries,
@@ -0,0 +1,55 @@
1
+ {
2
+ "response": {
3
+ "header": {
4
+ "resultCode": "00",
5
+ "resultMsg": "NORMAL SERVICE."
6
+ },
7
+ "body": {
8
+ "items": {
9
+ "item": [
10
+ {
11
+ "sggCd": "11680",
12
+ "umdNm": "역삼동",
13
+ "aptNm": "역삼자이",
14
+ "jibun": "754-1",
15
+ "excluUseAr": "84.97",
16
+ "dealYear": "2024",
17
+ "dealMonth": "2",
18
+ "dealDay": "14",
19
+ "deposit": "120,000",
20
+ "monthlyRent": "80",
21
+ "floor": "15",
22
+ "buildYear": "2016",
23
+ "contractTerm": "202402-202502",
24
+ "contractType": "갱신",
25
+ "useRRRight": "N",
26
+ "preDeposit": "115,000",
27
+ "preMonthlyRent": "75"
28
+ },
29
+ {
30
+ "sggCd": "41135",
31
+ "umdNm": "백현동",
32
+ "aptNm": "판교푸르지오그랑블",
33
+ "jibun": "531",
34
+ "excluUseAr": "59.88",
35
+ "dealYear": "2024",
36
+ "dealMonth": "3",
37
+ "dealDay": "8",
38
+ "deposit": "90,000",
39
+ "monthlyRent": "55",
40
+ "floor": "9",
41
+ "buildYear": "2011",
42
+ "contractTerm": "202403-202503",
43
+ "contractType": "신규",
44
+ "useRRRight": "N",
45
+ "preDeposit": "0",
46
+ "preMonthlyRent": "0"
47
+ }
48
+ ]
49
+ },
50
+ "numOfRows": 10,
51
+ "pageNo": 1,
52
+ "totalCount": 2
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "response": {
3
+ "header": {
4
+ "resultCode": "00",
5
+ "resultMsg": "NORMAL SERVICE."
6
+ },
7
+ "body": {
8
+ "items": {
9
+ "item": [
10
+ {
11
+ "sggCd": "11680",
12
+ "sggNm": "강남구",
13
+ "umdNm": "삼성동",
14
+ "jibun": "158-20",
15
+ "offiNm": "삼성 서초리버뷰",
16
+ "excluUseAr": "27.41",
17
+ "dealYear": "2024",
18
+ "dealMonth": "4",
19
+ "dealDay": "3",
20
+ "deposit": "30,000",
21
+ "monthlyRent": "120",
22
+ "floor": "14",
23
+ "buildYear": "2020",
24
+ "contractType": "신규",
25
+ "contractTerm": "202404-202504",
26
+ "useRRRight": "N",
27
+ "preDeposit": "0",
28
+ "preMonthlyRent": "0"
29
+ },
30
+ {
31
+ "sggCd": "41131",
32
+ "sggNm": "성남수정구",
33
+ "umdNm": "창곡동",
34
+ "jibun": "509",
35
+ "offiNm": "위례중앙타워",
36
+ "excluUseAr": "35.92",
37
+ "dealYear": "2024",
38
+ "dealMonth": "4",
39
+ "dealDay": "21",
40
+ "deposit": "20,000",
41
+ "monthlyRent": "95",
42
+ "floor": "7",
43
+ "buildYear": "2017",
44
+ "contractType": "갱신",
45
+ "contractTerm": "202404-202604",
46
+ "useRRRight": "Y",
47
+ "preDeposit": "18,000",
48
+ "preMonthlyRent": "90"
49
+ }
50
+ ]
51
+ },
52
+ "numOfRows": 10,
53
+ "pageNo": 1,
54
+ "totalCount": 2
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "response": {
3
+ "header": {
4
+ "resultCode": "00",
5
+ "resultMsg": "NORMAL SERVICE."
6
+ },
7
+ "body": {
8
+ "items": {
9
+ "item": [
10
+ {
11
+ "sggCd": "11680",
12
+ "sggNm": "강남구",
13
+ "umdNm": "논현동",
14
+ "jibun": "211-21",
15
+ "offiNm": "논현 아이파크",
16
+ "excluUseAr": "29.76",
17
+ "dealYear": "2024",
18
+ "dealMonth": "1",
19
+ "dealDay": "19",
20
+ "dealAmount": "31,500",
21
+ "floor": "11",
22
+ "buildYear": "2018",
23
+ "cdealType": "-",
24
+ "cdealDay": "",
25
+ "dealingGbn": "중개거래",
26
+ "estateAgentSggNm": "서울 강남구",
27
+ "slerGbn": "개인",
28
+ "buyerGbn": "개인"
29
+ },
30
+ {
31
+ "sggCd": "26110",
32
+ "sggNm": "해운대구",
33
+ "umdNm": "우동",
34
+ "jibun": "1505",
35
+ "offiNm": "센텀스카이비즈",
36
+ "excluUseAr": "42.13",
37
+ "dealYear": "2024",
38
+ "dealMonth": "2",
39
+ "dealDay": "27",
40
+ "dealAmount": "22,800",
41
+ "floor": "18",
42
+ "buildYear": "2015",
43
+ "cdealType": "-",
44
+ "cdealDay": "",
45
+ "dealingGbn": "직거래",
46
+ "estateAgentSggNm": "",
47
+ "slerGbn": "법인",
48
+ "buyerGbn": "개인"
49
+ }
50
+ ]
51
+ },
52
+ "numOfRows": 10,
53
+ "pageNo": 1,
54
+ "totalCount": 2
55
+ }
56
+ }
57
+ }