From a60d49ae9271cbfac90669710fd42132af62a030 Mon Sep 17 00:00:00 2001 From: jaehwang Date: Tue, 19 Aug 2025 13:08:28 +0900 Subject: [PATCH] init commit, input data parsing logic and page --- .gitignore | 189 +++++++++++++++++ README.md | 37 ++++ backend/main.py | 61 ++++++ backend/models.py | 15 ++ backend/openai_parser.py | 68 +++++++ docs/api_guide.md | 428 +++++++++++++++++++++++++++++++++++++++ docs/project_logs.txt | 127 ++++++++++++ docs/project_plan.md | 38 ++++ frontend/index.html | 46 +++++ frontend/script.js | 97 +++++++++ frontend/style.css | 152 ++++++++++++++ requirements.txt | 4 + 12 files changed, 1262 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 backend/main.py create mode 100644 backend/models.py create mode 100644 backend/openai_parser.py create mode 100644 docs/api_guide.md create mode 100644 docs/project_logs.txt create mode 100644 docs/project_plan.md create mode 100644 frontend/index.html create mode 100644 frontend/script.js create mode 100644 frontend/style.css create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82efd9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,189 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +Pipfile.lock + +# poetry +poetry.lock + +# pdm +.pdm.toml + +# PEP 582 +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# IDE - VSCode +.vscode/ +*.code-workspace + +# IDE - PyCharm +.idea/ +*.iml +*.iws +*.ipr + +# IDE - Sublime Text +*.sublime-project +*.sublime-workspace + +# IDE - Vim +*.swp +*.swo +*~ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +desktop.ini + +# Project specific +*.db +*.sqlite +*.sqlite3 +logs/ +temp/ +tmp/ +cache/ + +# FastAPI +.fastapi_cache/ + +# Security - API keys should never be committed +.env +.env.local +.env.*.local +secrets.json +config.json + +# Backup files +*.bak +*.backup +*.old \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ac1a493 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# 부동산 자연어 검색 시스템 + +FastAPI와 OpenAI를 활용한 부동산 자연어 검색 웹 애플리케이션 + +## 기능 + +- 자연어로 부동산 조건 입력 +- OpenAI API를 통한 자동 정보 추출 +- 가격, 위치, 면적, 방 수, 거래 유형 파싱 + +## 설치 + +```bash +cd C:\o2o\RealEstateSearch +pip install -r requirements.txt +``` + +## 설정 + +1. `.env` 파일에 OpenAI API 키 설정: +``` +OPENAI_API_KEY=your-actual-api-key +``` + +## 실행 + +```bash +cd C:\o2o\RealEstateSearch\backend +python main.py +``` + +브라우저에서 http://localhost:20001 접속 + +## 입력 예시 +- "강남역 근처 전세 2억 이하 투룸" +- "서초동 30평대 아파트 매매 10억 이하" +- "판교 방 3개 월세 100/50" \ No newline at end of file diff --git a/backend/main.py b/backend/main.py new file mode 100644 index 0000000..5ea3d7e --- /dev/null +++ b/backend/main.py @@ -0,0 +1,61 @@ +import os +from fastapi import FastAPI, HTTPException +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles +from fastapi.responses import FileResponse +from dotenv import load_dotenv +import uvicorn + +from models import RealEstateQuery, ParsedRealEstate +from openai_parser import OpenAIParser + +load_dotenv() + +app = FastAPI(title="부동산 검색 API") + +# CORS 설정 +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# OpenAI 파서 초기화 +try: + parser = OpenAIParser() +except ValueError as e: + print(f"Warning: {e}") + parser = None + +@app.get("/") +async def serve_index(): + """메인 페이지 제공""" + return FileResponse("../frontend/index.html") + +@app.post("/api/parse", response_model=ParsedRealEstate) +async def parse_real_estate(query: RealEstateQuery): + """자연어 입력을 파싱하여 부동산 정보 추출""" + if not parser: + raise HTTPException( + status_code=500, + detail="OpenAI API key not configured. Please set OPENAI_API_KEY in .env file" + ) + + try: + result = await parser.parse_real_estate_query(query.text) + print(result) + return result + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) + +# 정적 파일 서빙 (CSS, JS) +app.mount("/static", StaticFiles(directory="../frontend"), name="static") + +if __name__ == "__main__": + host = os.getenv("HOST", "0.0.0.0") + port = int(os.getenv("PORT", 20001)) + + print(f"Starting server at http://localhost:{port}") + uvicorn.run(app, host=host, port=port, reload=True) \ No newline at end of file diff --git a/backend/models.py b/backend/models.py new file mode 100644 index 0000000..99d825b --- /dev/null +++ b/backend/models.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel +from typing import Optional + +class RealEstateQuery(BaseModel): + """부동산 검색 쿼리 모델""" + text: str + +class ParsedRealEstate(BaseModel): + """파싱된 부동산 정보 모델""" + price: Optional[str] = None + location: Optional[str] = None + area: Optional[str] = None + rooms: Optional[str] = None + transaction_type: Optional[str] = None # 전세, 월세, 매매 + raw_text: str \ No newline at end of file diff --git a/backend/openai_parser.py b/backend/openai_parser.py new file mode 100644 index 0000000..0af9c15 --- /dev/null +++ b/backend/openai_parser.py @@ -0,0 +1,68 @@ +import os +import json +from openai import OpenAI +from dotenv import load_dotenv +from models import ParsedRealEstate + +load_dotenv() + +class OpenAIParser: + """OpenAI를 이용한 부동산 정보 파싱""" + + def __init__(self): + api_key = os.getenv("OPENAI_API_KEY") + if not api_key or api_key == "your-api-key-here": + raise ValueError("OpenAI API key not configured in .env file") + self.client = OpenAI(api_key=api_key) + + async def parse_real_estate_query(self, text: str) -> ParsedRealEstate: + """자연어 텍스트에서 부동산 정보 추출""" + + system_prompt = """ + 당신은 부동산 정보를 추출하는 전문가입니다. + 사용자의 자연어 입력에서 다음 정보를 추출하세요. + 단위는 붙여서 표기하세요. : + 1. price: 가격 (전세금, 월세, 매매가 등) (string) + 2. location: 위치 (지역명, 동, 구 등) (string) + 3. area: 면적 (평수, 제곱미터) (string) + 4. rooms: 방 개수 (string) + 5. transaction_type: 거래 유형 (전세, 월세, 매매) (string) + + JSON 형식으로만 응답하세요. + 정보가 없는 항목은 null로 표시하세요. + """ + + try: + response = self.client.chat.completions.create( + model="gpt-3.5-turbo", + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": text} + ], + temperature=0.1, + response_format={"type": "json_object"} + ) + + result = json.loads(response.choices[0].message.content) + print(result) + + return ParsedRealEstate( + price=result.get("price"), + location=result.get("location"), + area=result.get("area"), + rooms=result.get("rooms"), + transaction_type=result.get("transaction_type"), + raw_text=text + ) + + except Exception as e: + print(e) + # 에러 발생 시 기본값 반환 + return ParsedRealEstate( + raw_text=text, + price=None, + location=None, + area=None, + rooms=None, + transaction_type=None + ) \ No newline at end of file diff --git a/docs/api_guide.md b/docs/api_guide.md new file mode 100644 index 0000000..e943e51 --- /dev/null +++ b/docs/api_guide.md @@ -0,0 +1,428 @@ +# 부동산 정보 검색 API 가이드 + +## 1. 공공데이터 API (국토교통부) + +### 1.1 아파트 매매 실거래가 API +- **서비스명**: 국토교통부_아파트 매매 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: https://www.data.go.kr/data/15126469/openapi.do +- **Base URL**: `apis.data.go.kr/1613000/RTMSDataSvcAptTrade` +- **엔드포인트**: `/getRTMSDataSvcAptTrade` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +#### 주요 파라미터 +- `serviceKey`: 공공데이터포털에서 발급받은 인증키 +- `LAWD_CD`: 법정동 코드 5자리 (예: 서울 종로구 11110) +- `DEAL_YMD`: 계약년월 6자리 (예: 202501) + +### 1.2 아파트 전월세 실거래가 API +- **서비스명**: 국토교통부_아파트 전월세 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: https://www.data.go.kr/data/15126474/openapi.do +- **Base URL**: `apis.data.go.kr/1613000/RTMSDataSvcAptRent` +- **엔드포인트**: `/getRTMSDataSvcAptRent` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +#### 주요 파라미터 +- `serviceKey`: 공공데이터포털에서 발급받은 인증키 +- `LAWD_CD`: 법정동 코드 +- `DEAL_YMD`: 계약년월 + +### 1.3 연립/다세대 주택 실거래가 API +- **서비스명**: 국토교통부_연립다세대 매매/전월세 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: + - 매매: https://www.data.go.kr/data/15126467/openapi.do + - 전월세: https://www.data.go.kr/data/15126473/openapi.do +- **Base URL**: + - 매매: `apis.data.go.kr/1613000/RTMSDataSvcSHTrade` + - 전월세: `apis.data.go.kr/1613000/RTMSDataSvcSHRent` +- **엔드포인트**: + - 매매: `/getRTMSDataSvcSHTrade` + - 전월세: `/getRTMSDataSvcSHRent` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +### 1.4 오피스텔 실거래가 API +- **서비스명**: 국토교통부_오피스텔 매매/전월세 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: + - 매매: https://www.data.go.kr/data/15126464/openapi.do + - 전월세: https://www.data.go.kr/data/15126475/openapi.do +- **Base URL**: + - 매매: `apis.data.go.kr/1613000/RTMSDataSvcOffiTrade` + - 전월세: `apis.data.go.kr/1613000/RTMSDataSvcOffiRent` +- **엔드포인트**: + - 매매: `/getRTMSDataSvcOffiTrade` + - 전월세: `/getRTMSDataSvcOffiRent` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +### 1.5 단독/다가구 주택 실거래가 API +- **서비스명**: 국토교통부_단독다가구 매매/전월세 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: + - 매매: https://www.data.go.kr/data/15126465/openapi.do + - 전월세: https://www.data.go.kr/data/15126472/openapi.do +- **Base URL**: + - 매매: `apis.data.go.kr/1613000/RTMSDataSvcSHHouseTrade` + - 전월세: `apis.data.go.kr/1613000/RTMSDataSvcSHHouseRent` +- **엔드포인트**: + - 매매: `/getRTMSDataSvcSHHouseTrade` + - 전월세: `/getRTMSDataSvcSHHouseRent` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +### 1.6 토지 실거래가 API +- **서비스명**: 국토교통부_토지 매매 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: https://www.data.go.kr/data/15126466/openapi.do +- **Base URL**: `apis.data.go.kr/1613000/RTMSDataSvcLandTrade` +- **엔드포인트**: `/getRTMSDataSvcLandTrade` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +### 1.7 상업/업무용 부동산 실거래가 API +- **서비스명**: 국토교통부_상업업무용 부동산 매매 실거래가 자료 +- **제공기관**: 국토교통부 +- **URL**: https://www.data.go.kr/data/15126463/openapi.do +- **Base URL**: `apis.data.go.kr/1613000/RTMSDataSvcNrgTrade` +- **엔드포인트**: `/getRTMSDataSvcNrgTrade` +- **데이터 형식**: XML +- **트래픽 제한**: 개발계정 10,000/일 + +## 2. 한국부동산원 API + +### 2.1 부동산 통계 조회 서비스 +- **서비스명**: 한국부동산원_부동산통계 조회 서비스 +- **제공기관**: 한국부동산원 +- **URL**: https://www.data.go.kr/data/15134761/openapi.do +- **Base URL**: https://www.reb.or.kr/r-one/portal/openapi +- **주요 통계**: + - 주택가격동향조사 + - 지가변동률 + - 상업용부동산 임대동향 + - 오피스텔 가격동향 + - 공동주택 실거래가격지수 + +### 2.2 아파트 단지 정보 API +- **서비스명**: 한국부동산원_전국 아파트단지 정보 +- **특징**: 전국 아파트 단지의 기본 정보 및 동 정보 제공 +- **데이터**: 단지명, 주소, 세대수, 준공년월 등 + +## 3. LH 한국토지주택공사 API + +### 3.1 공공임대주택 단지정보 조회 서비스 +- **서비스명**: 한국토지주택공사_공공임대주택 단지정보 조회 서비스 +- **URL**: https://www.data.go.kr/data/15058476/openapi.do +- **Base URL**: `apis.data.go.kr/B552555/lhPublicRentalHousingComplexInquireSvc` +- **엔드포인트**: `/getPublicRentalHousingComplexList` +- **특징**: LH에서 관리하는 공공임대주택 정보 제공 +- **데이터 형식**: XML/JSON + +### 3.2 임대주택단지 조회 서비스 +- **서비스명**: 한국토지주택공사_임대주택단지 조회 서비스 +- **URL**: https://www.data.go.kr/data/15059475/openapi.do +- **Base URL**: `apis.data.go.kr/B552555/lhLeaseholdBuldListService` +- **엔드포인트**: `/lhLeaseholdBuldList` +- **데이터**: 임대유형, 임대료, 위치 정보 등 +- **데이터 형식**: XML/JSON + +### 3.3 분양/임대 공고문 조회 서비스 +- **서비스명**: 한국토지주택공사_분양임대공고문 조회 서비스 +- **URL**: https://www.data.go.kr/data/15058530/openapi.do +- **Base URL**: `apis.data.go.kr/B552555/lhLeaseNoticeInfo` +- **엔드포인트**: `/lhLeaseNoticeInfo` +- **특징**: LH 분양/임대 공고 정보 실시간 제공 +- **데이터 형식**: XML/JSON + +## 4. 지자체 부동산 API + +### 4.1 서울시 부동산 API +- **제공기관**: 서울특별시 +- **플랫폼**: 서울 열린데이터광장 (data.seoul.go.kr) +- **주요 API**: + - 서울시 부동산 실거래가 정보 + - 서울시 전월세가 정보 + - 서울시 부동산 중개업소 정보 + +### 4.2 경기도 부동산 API +- **제공기관**: 경기도 +- **플랫폼**: 경기데이터드림 (data.gg.go.kr) +- **주요 API**: + - 경기도 임대주택 현황 + - 경기도 부동산 중개업소 현황 + +### 4.3 SH 서울주택도시공사 API +- **제공기관**: 서울주택도시공사 +- **URL**: https://www.i-sh.co.kr +- **주요 데이터**: + - SH 임대주택 정보 + - SH 분양 정보 + - 상가 및 공장 정보 + +## 5. 주택금융 관련 API + +### 5.1 주택도시보증공사 (HUG) API +- **제공기관**: 주택도시보증공사 +- **URL**: https://www.khug.or.kr +- **Base URL**: `openapi.khug.or.kr` +- **주요 API**: + - 전세보증금 보증 정보: `/api/guarantee/deposit` + - 분양보증 정보: `/api/guarantee/sale` + - 주택금융 통계: `/api/statistics/housing` + +### 5.2 마이홈포털 API +- **서비스명**: 국토교통부_마이홈포털 공공임대주택 단지정보 조회 서비스 +- **URL**: https://www.data.go.kr/data/15110581/openapi.do +- **Base URL**: `apis.data.go.kr/1613000/MyHomeService` +- **엔드포인트**: `/getPublicRentalHousingInfo` +- **특징**: 전국 공공임대주택 통합 정보 제공 +- **데이터 형식**: XML/JSON + +## 6. Python을 이용한 API 호출 예제 + +### 6.1 공공데이터 API 호출 예제 +```python +import requests +import xml.etree.ElementTree as ET + +def get_apartment_trade_data(service_key, lawd_cd, deal_ymd): + """ + 아파트 매매 실거래가 데이터 조회 + """ + base_url = "http://apis.data.go.kr/1613000/RTMSDataSvcAptTrade/getRTMSDataSvcAptTrade" + + params = { + 'serviceKey': service_key, + 'LAWD_CD': lawd_cd, + 'DEAL_YMD': deal_ymd, + 'pageNo': 1, + 'numOfRows': 100 + } + + response = requests.get(base_url, params=params) + + if response.status_code == 200: + # XML 파싱 + root = ET.fromstring(response.content) + items = root.findall('.//item') + + results = [] + for item in items: + data = { + '아파트명': item.findtext('아파트'), + '전용면적': item.findtext('전용면적'), + '거래금액': item.findtext('거래금액'), + '거래년': item.findtext('년'), + '거래월': item.findtext('월'), + '거래일': item.findtext('일'), + '층': item.findtext('층'), + '건축년도': item.findtext('건축년도'), + '도로명': item.findtext('도로명'), + '법정동': item.findtext('법정동') + } + results.append(data) + + return results + else: + return None +``` + +### 6.2 오피스텔 전월세 데이터 조회 예제 +```python +def get_officetel_rent_data(service_key, lawd_cd, deal_ymd): + """ + 오피스텔 전월세 실거래가 데이터 조회 + """ + base_url = "http://apis.data.go.kr/1613000/RTMSDataSvcOffiRent/getRTMSDataSvcOffiRent" + + params = { + 'serviceKey': service_key, + 'LAWD_CD': lawd_cd, + 'DEAL_YMD': deal_ymd, + 'pageNo': 1, + 'numOfRows': 100 + } + + response = requests.get(base_url, params=params) + + if response.status_code == 200: + root = ET.fromstring(response.content) + items = root.findall('.//item') + + results = [] + for item in items: + data = { + '단지명': item.findtext('단지'), + '전용면적': item.findtext('전용면적'), + '보증금': item.findtext('보증금액'), + '월세': item.findtext('월세금액'), + '층': item.findtext('층'), + '건축년도': item.findtext('건축년도'), + '법정동': item.findtext('법정동') + } + results.append(data) + + return results + else: + return None +``` + +### 6.3 통합 API 호출 클래스 예제 +```python +class RealEstateAPI: + """ + 국토교통부 부동산 실거래가 통합 API 클래스 + """ + + BASE_URL = "http://apis.data.go.kr/1613000" + + ENDPOINTS = { + 'apt_trade': '/RTMSDataSvcAptTrade/getRTMSDataSvcAptTrade', + 'apt_rent': '/RTMSDataSvcAptRent/getRTMSDataSvcAptRent', + 'sh_trade': '/RTMSDataSvcSHTrade/getRTMSDataSvcSHTrade', + 'sh_rent': '/RTMSDataSvcSHRent/getRTMSDataSvcSHRent', + 'offi_trade': '/RTMSDataSvcOffiTrade/getRTMSDataSvcOffiTrade', + 'offi_rent': '/RTMSDataSvcOffiRent/getRTMSDataSvcOffiRent', + 'house_trade': '/RTMSDataSvcSHHouseTrade/getRTMSDataSvcSHHouseTrade', + 'house_rent': '/RTMSDataSvcSHHouseRent/getRTMSDataSvcSHHouseRent', + 'land': '/RTMSDataSvcLandTrade/getRTMSDataSvcLandTrade', + 'business': '/RTMSDataSvcNrgTrade/getRTMSDataSvcNrgTrade' + } + + def __init__(self, service_key): + self.service_key = service_key + + def get_data(self, api_type, lawd_cd, deal_ymd, page=1, rows=100): + """ + 통합 데이터 조회 메서드 + + :param api_type: API 타입 (apt_trade, apt_rent, sh_trade 등) + :param lawd_cd: 법정동 코드 5자리 + :param deal_ymd: 거래년월 6자리 + :param page: 페이지 번호 + :param rows: 한 페이지당 행 수 + """ + if api_type not in self.ENDPOINTS: + raise ValueError(f"Invalid API type: {api_type}") + + url = self.BASE_URL + self.ENDPOINTS[api_type] + params = { + 'serviceKey': self.service_key, + 'LAWD_CD': lawd_cd, + 'DEAL_YMD': deal_ymd, + 'pageNo': page, + 'numOfRows': rows + } + + response = requests.get(url, params=params) + if response.status_code == 200: + return self._parse_xml(response.content) + else: + return None + + def _parse_xml(self, xml_content): + """XML 파싱 헬퍼 메서드""" + root = ET.fromstring(xml_content) + items = root.findall('.//item') + return [self._extract_item_data(item) for item in items] + + def _extract_item_data(self, item): + """아이템 데이터 추출""" + # XML 엘리먼트에서 모든 데이터 추출 + data = {} + for child in item: + data[child.tag] = child.text + return data +``` + +## 7. 민간 부동산 API + +### 7.1 네이버 부동산 +- **특징**: 공식 API 미제공, 웹 크롤링으로 데이터 수집 +- **주의사항**: robots.txt 확인 및 과도한 요청 자제 +- **대안**: Selenium을 이용한 동적 페이지 크롤링 + +### 7.2 직방 API (비공식) +- **특징**: 내부 API 엔드포인트 활용 가능 +- **주의사항**: 공식 지원 없음, 변경 가능성 있음 +- **주요 엔드포인트**: + - 지역 검색: `https://apis.zigbang.com/v2/search` + - 매물 리스트: `https://apis.zigbang.com/v2/items` + - 매물 상세: `https://apis.zigbang.com/v2/items/{item_id}` + - 단지 정보: `https://apis.zigbang.com/v2/complex/{complex_id}` + +### 7.3 호갱노노 +- **특징**: 아파트 실거래가 정보 특화 +- **URL**: https://hogangnono.com +- **데이터**: 실거래가, 시세 차트, 단지 정보 +- **주의사항**: 웹 크롤링 필요 + +### 7.4 부동산114 +- **특징**: 종합 부동산 정보 제공 +- **URL**: https://www.r114.com +- **데이터**: 시세, 실거래가, 분양 정보 +- **주의사항**: 회원가입 필요한 경우 있음 + +### 7.5 KB부동산 +- **특징**: KB국민은행 제공 부동산 데이터 +- **URL**: https://data.kbland.kr +- **데이터**: 주택가격동향, 시세, 통계 +- **주의사항**: 일부 데이터는 유료 + +## 8. API 활용 시 주의사항 + +1. **인증키 관리**: 환경변수나 별도 설정 파일로 관리 +2. **트래픽 제한**: 일일 호출 제한 확인 +3. **데이터 갱신 주기**: 실시간이 아닌 일정 주기로 갱신됨 +4. **에러 처리**: 네트워크 오류, 서비스 점검 대응 +5. **법적 제약**: 개인정보보호법 준수 + +## 9. 추천 활용 방법 + +1. **캐싱 구현**: 동일 요청 반복 방지 +2. **배치 처리**: 대량 데이터는 야간 배치로 처리 +3. **데이터베이스 저장**: 조회한 데이터 로컬 DB 저장 +4. **모니터링**: API 응답 시간 및 에러율 모니터링 + +## 10. API 종류별 요약 테이블 + +| 구분 | API 제공처 | 데이터 종류 | 비용 | 인증 필요 | +|------|------------|-------------|------|-----------| +| 국토교통부 | 공공데이터포털 | 아파트/오피스텔/연립/단독 실거래가 | 무료 | O | +| 한국부동산원 | 한국부동산원 | 부동산 통계, 가격지수 | 무료 | O | +| LH공사 | 공공데이터포털 | 공공임대주택 정보 | 무료 | O | +| SH공사 | SH/서울시 | 서울시 임대주택 정보 | 무료 | O | +| 지자체 | 각 지자체 | 지역별 부동산 정보 | 무료 | O | +| 네이버 | 네이버 | 매물, 시세 정보 | - | X | +| 직방 | 직방 | 원룸/오피스텔 매물 | - | X | +| 호갱노노 | 호갱노노 | 아파트 실거래가 | 무료 | X | +| 부동산114 | 부동산114 | 종합 부동산 정보 | 일부유료 | △ | +| KB부동산 | KB국민은행 | 부동산 통계/시세 | 일부유료 | △ | + +## 11. API 엔드포인트 빠른 참조 + +### 국토교통부 실거래가 API 엔드포인트 목록 + +| 부동산 종류 | 거래 유형 | Base URL | 엔드포인트 | +|------------|-----------|----------|------------| +| 아파트 | 매매 | apis.data.go.kr/1613000/RTMSDataSvcAptTrade | /getRTMSDataSvcAptTrade | +| 아파트 | 전월세 | apis.data.go.kr/1613000/RTMSDataSvcAptRent | /getRTMSDataSvcAptRent | +| 연립/다세대 | 매매 | apis.data.go.kr/1613000/RTMSDataSvcSHTrade | /getRTMSDataSvcSHTrade | +| 연립/다세대 | 전월세 | apis.data.go.kr/1613000/RTMSDataSvcSHRent | /getRTMSDataSvcSHRent | +| 오피스텔 | 매매 | apis.data.go.kr/1613000/RTMSDataSvcOffiTrade | /getRTMSDataSvcOffiTrade | +| 오피스텔 | 전월세 | apis.data.go.kr/1613000/RTMSDataSvcOffiRent | /getRTMSDataSvcOffiRent | +| 단독/다가구 | 매매 | apis.data.go.kr/1613000/RTMSDataSvcSHHouseTrade | /getRTMSDataSvcSHHouseTrade | +| 단독/다가구 | 전월세 | apis.data.go.kr/1613000/RTMSDataSvcSHHouseRent | /getRTMSDataSvcSHHouseRent | +| 토지 | 매매 | apis.data.go.kr/1613000/RTMSDataSvcLandTrade | /getRTMSDataSvcLandTrade | +| 상업/업무용 | 매매 | apis.data.go.kr/1613000/RTMSDataSvcNrgTrade | /getRTMSDataSvcNrgTrade | + +### 공통 파라미터 +- `serviceKey`: 공공데이터포털 인증키 (필수) +- `LAWD_CD`: 법정동 코드 5자리 (필수) +- `DEAL_YMD`: 거래년월 6자리 (필수) +- `pageNo`: 페이지 번호 (선택, 기본값: 1) +- `numOfRows`: 한 페이지 결과 수 (선택, 기본값: 10) diff --git a/docs/project_logs.txt b/docs/project_logs.txt new file mode 100644 index 0000000..045869a --- /dev/null +++ b/docs/project_logs.txt @@ -0,0 +1,127 @@ +[2025-08-19 10:55:09] 모든 부동산 API 엔드포인트 상세 정보 추가 +- 국토교통부 실거래가 API 엔드포인트 조사 + - 아파트 매매/전월세 API 엔드포인트 확인 + - 연립/다세대 매매/전월세 API 엔드포인트 추가 + - 오피스텔 매매/전월세 API 엔드포인트 추가 + - 단독/다가구 매매/전월세 API 엔드포인트 추가 + - 토지 매매 API 엔드포인트 추가 + - 상업/업무용 부동산 매매 API 엔드포인트 추가 +- docs/api_guide.md 대폭 업데이트 + - 모든 API의 Base URL 및 정확한 엔드포인트 경로 명시 + - LH 공사 API 3종 엔드포인트 추가 + - 마이홈포털 API 엔드포인트 추가 + - 직방 API 추가 엔드포인트 (매물 상세, 단지 정보) + - 통합 API 호출 클래스(RealEstateAPI) 예제 코드 추가 + - API 엔드포인트 빠른 참조 테이블 작성 + - 공통 파라미터 설명 추가 +- 웹 브라우저를 통한 공공데이터포털 직접 확인 +- docs/project_plan.md 업데이트 (API 엔드포인트 추가 완료) + +[2025-08-19 10:29:10] 추가 부동산 API 조사 및 문서 업데이트 +- 광범위한 부동산 API 웹 검색 수행 + - 국토교통부 전체 부동산 실거래가 API 조사 + - 한국부동산원 Open API 목록 확인 + - LH 한국토지주택공사 API 조사 + - 지자체별 부동산 API 정보 수집 +- docs/api_guide.md 대폭 확장 + - 연립/다세대, 오피스텔, 단독/다가구, 토지, 상업용 부동산 API 추가 + - 한국부동산원 부동산 통계 API 섹션 추가 + - LH 공공임대주택 관련 API 3종 추가 + - SH 서울주택도시공사 및 지자체 API 정보 추가 + - 주택금융 관련 API (HUG, 마이홈포털) 추가 + - 민간 부동산 서비스 확장 (호갱노노, 부동산114, KB부동산) + - 오피스텔 전월세 API 호출 예제 코드 추가 + - API 종류별 요약 테이블 작성 +- docs/project_plan.md 업데이트 (추가 API 조사 완료 표시) + +[2025-08-19 10:14:28] 부동산 정보 검색 API 가이드 문서 작성 +- 공공데이터포털 API 조사 + - 국토교통부 아파트 매매 실거래가 API 정보 확인 + - 국토교통부 아파트 전월세 실거래가 API 정보 확인 +- docs/api_guide.md 파일 생성 및 작성 + - 공공데이터 API (국토교통부) 섹션 + - Python API 호출 예제 코드 + - 민간 부동산 API 정보 (네이버, 직방) + - API 활용 시 주의사항 + - 추천 활용 방법 +- docs/project_plan.md 업데이트 (API 가이드 문서 추가) +- 웹 검색 및 브라우저를 통한 실제 API 정보 수집 + +[2025-08-19 11:29:38] .gitignore 파일 생성 작업 완료 +- Python 환경용 .gitignore 파일 생성 +- 포함 내용: + - Python 컴파일 파일 (__pycache__, *.pyc 등) + - 가상환경 (venv, env 등) + - IDE 설정 파일 (VSCode, PyCharm, Sublime Text, Vim) + - OS 생성 파일 (DS_Store, Thumbs.db 등) + - 환경 변수 파일 (.env, .env.local 등) + - 테스트 및 커버리지 파일 + - 로그 및 임시 파일 + - FastAPI 캐시 + - 백업 파일 +- 특히 .env 파일이 Git에 커밋되지 않도록 설정 + +[2025-08-19 11:28:27] .gitignore 파일 생성 작업 시작 +- Python 환경용 .gitignore 파일 생성 + +[2025-08-19 11:23:57] 불필요한 파일 정리 작업 완료 +- src/app.py 삭제 (Chainlit 메인 앱) +- src/search_engine.py 삭제 (Chainlit 검색 엔진) +- src 폴더 삭제 +- 최종 프로젝트 구조 정리 완료 +- FastAPI 기반 부동산 검색 시스템만 유지 + +[2025-08-19 11:22:54] 불필요한 파일 정리 작업 시작 +- Chainlit 관련 파일 삭제 예정 +- src 폴더 및 내부 파일 삭제 예정 + +[2025-08-19 11:20:14] FastAPI 및 OpenAI 기반 부동산 검색 웹사이트 개발 완료 +- backend 폴더 생성 +- frontend 폴더 생성 +- requirements.txt 업데이트 (FastAPI, uvicorn, openai, python-dotenv 등) +- .env 파일 생성 (OpenAI API 키 설정 템플릿) +- backend/models.py: Pydantic 데이터 모델 정의 + - RealEstateQuery: 사용자 입력 모델 + - ParsedRealEstate: 파싱 결과 모델 +- backend/openai_parser.py: OpenAI API 파싱 로직 + - GPT-3.5-turbo 모델 사용 + - JSON 응답 포맷으로 구조화된 데이터 추출 +- backend/main.py: FastAPI 서버 + - CORS 설정 + - /api/parse 엔드포인트 + - 정적 파일 서빙 + - 포트 20001에서 실행 +- frontend/index.html: 메인 페이지 + - 자연어 입력 텍스트 영역 + - 예시 제공 + - 결과 표시 섹션 +- frontend/style.css: 스타일시트 + - 그라디언트 배경 + - 반응형 디자인 + - 로딩 애니메이션 +- frontend/script.js: 클라이언트 로직 + - API 호출 + - 결과 파싱 및 표시 + - Enter 키 지원 +- README.md 업데이트 +- 기존 src 폴더의 Chainlit 파일들 유지 (app.py, search_engine.py) + +[2025-08-19 11:14:44] FastAPI 및 OpenAI 기반 부동산 검색 웹사이트 개발 시작 +- Chainlit에서 FastAPI로 전환 결정 +- OpenAI API를 활용한 자연어 처리 구현 예정 +- 입력: 자연어 텍스트 +- 출력: 가격, 위치, 면적, 방 수, 거래 유형 + +[2025-08-19 09:54:26] Chainlit 부동산 검색 프로그램 개발 완료 +- 프로젝트 폴더 구조 생성 (C:\o2o\RealEstateSearch) +- docs/project_plan.md 작성 +- requirements.txt 작성 (chainlit, requests, beautifulsoup4, selenium, pandas) +- src/app.py 작성 - Chainlit 메인 애플리케이션 + - 사용자 입력 파싱 기능 (전세금, 방 개수, 지역명) + - 검색 파라미터 관리 + - 검색 결과 포맷팅 및 표시 +- src/search_engine.py 작성 - 부동산 검색 엔진 + - RealEstateSearchEngine 클래스 + - 샘플 데이터 반환 (실제 API 연동 준비) +- README.md 작성 (설치 및 실행 방법) +- 모든 작업 완료 \ No newline at end of file diff --git a/docs/project_plan.md b/docs/project_plan.md new file mode 100644 index 0000000..371a47e --- /dev/null +++ b/docs/project_plan.md @@ -0,0 +1,38 @@ +# 부동산 검색 프로젝트 계획 + +## 프로젝트 개요 +- **목적**: FastAPI와 OpenAI를 이용한 부동산 검색 웹사이트 +- **주요 기능**: 자연어 입력을 받아 OpenAI로 파싱 후 부동산 정보 추출 + +## 기술 스택 +- **Backend**: Python, FastAPI, OpenAI API +- **Frontend**: HTML, CSS, JavaScript +- **NLP**: OpenAI GPT API + +## 폴더 구조 +``` +C:\o2o\RealEstateSearch\ +├── docs/ +│ ├── project_plan.md +│ └── project_logs.txt +├── backend/ +│ ├── main.py (FastAPI 서버) +│ ├── openai_parser.py (OpenAI 파싱 로직) +│ └── models.py (데이터 모델) +├── frontend/ +│ ├── index.html +│ ├── style.css +│ └── script.js +├── requirements.txt +├── .env (API 키 저장) +├── .gitignore (Git 제외 파일) +└── README.md +``` + +## 작업 단계 +- [x] 프로젝트 폴더 구조 생성 +- [x] 프로젝트 계획서 작성 +- [x] FastAPI 백엔드 개발 +- [x] OpenAI 파싱 로직 구현 +- [x] 프론트엔드 개발 +- [x] README.md 업데이트 diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..6c96eec --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,46 @@ + + + + + + 부동산 검색 - 자연어 분석 + + + +
+
+

🏠 부동산 자연어 검색

+

원하시는 부동산 조건을 자유롭게 입력해주세요

+
+ +
+
+ + +
+ +
+

입력 예시:

+
+ 강남 전세 3억 방 2개 + 판교 30평 아파트 매매 + 서초동 월세 200/100 +
+
+ + + + +
+
+ + + \ No newline at end of file diff --git a/frontend/script.js b/frontend/script.js new file mode 100644 index 0000000..9476df6 --- /dev/null +++ b/frontend/script.js @@ -0,0 +1,97 @@ +// 예시 텍스트 설정 +function setExample(element) { + document.getElementById('searchInput').value = element.textContent; +} + +// 쿼리 파싱 및 분석 +async function parseQuery() { + const input = document.getElementById('searchInput').value.trim(); + + if (!input) { + alert('검색할 내용을 입력해주세요!'); + return; + } + + // UI 상태 변경 + document.getElementById('loadingSection').style.display = 'block'; + document.getElementById('resultSection').style.display = 'none'; + document.getElementById('searchBtn').disabled = true; + + try { + const response = await fetch('/api/parse', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ text: input }) + }); + + if (!response.ok) { + throw new Error('서버 오류가 발생했습니다.'); + } + + const data = await response.json(); + displayResults(data); + + } catch (error) { + alert('오류: ' + error.message); + console.error('Error:', error); + } finally { + document.getElementById('loadingSection').style.display = 'none'; + document.getElementById('searchBtn').disabled = false; + } +} + +// 결과 표시 +function displayResults(data) { + const resultContent = document.getElementById('resultContent'); + + let html = ''; + + if (data.transaction_type) { + html += `
+ 거래 유형: ${data.transaction_type} +
`; + } + + if (data.price) { + html += `
+ 가격: ${data.price} +
`; + } + + if (data.location) { + html += `
+ 위치: ${data.location} +
`; + } + + if (data.area) { + html += `
+ 면적: ${data.area} +
`; + } + + if (data.rooms) { + html += `
+ 방 개수: ${data.rooms} +
`; + } + + if (!html) { + html = '

추출된 정보가 없습니다. 더 구체적으로 입력해주세요.

'; + } + + resultContent.innerHTML = html; + document.getElementById('resultSection').style.display = 'block'; +} + +// Enter 키로 검색 +document.addEventListener('DOMContentLoaded', function() { + document.getElementById('searchInput').addEventListener('keypress', function(e) { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + parseQuery(); + } + }); +}); \ No newline at end of file diff --git a/frontend/style.css b/frontend/style.css new file mode 100644 index 0000000..855c4ca --- /dev/null +++ b/frontend/style.css @@ -0,0 +1,152 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +.container { + width: 90%; + max-width: 800px; + background: white; + border-radius: 20px; + box-shadow: 0 20px 60px rgba(0,0,0,0.3); + padding: 40px; +} + +header { + text-align: center; + margin-bottom: 30px; +} + +header h1 { + color: #333; + font-size: 2.5em; + margin-bottom: 10px; +} + +header p { + color: #666; + font-size: 1.1em; +} + +.search-section { + margin-bottom: 30px; +} + +#searchInput { + width: 100%; + min-height: 120px; + padding: 15px; + font-size: 16px; + border: 2px solid #e0e0e0; + border-radius: 10px; + resize: vertical; + font-family: inherit; + transition: border-color 0.3s; +} + +#searchInput:focus { + outline: none; + border-color: #667eea; +} + +#searchBtn { + width: 100%; + padding: 15px; + margin-top: 15px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border: none; + border-radius: 10px; + font-size: 18px; + font-weight: bold; + cursor: pointer; + transition: transform 0.2s; +} + +#searchBtn:hover { + transform: translateY(-2px); +} + +.examples { + margin-bottom: 30px; +} + +.examples h3 { + color: #555; + margin-bottom: 10px; +} + +.example-chips { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.chip { + background: #f0f0f0; + padding: 8px 16px; + border-radius: 20px; + cursor: pointer; + transition: all 0.3s; +} + +.chip:hover { + background: #667eea; + color: white; +} + +.result-section { + background: #f8f9fa; + padding: 25px; + border-radius: 15px; + margin-top: 30px; +} + +.result-section h2 { + color: #333; + margin-bottom: 20px; +} + +.result-item { + background: white; + padding: 15px; + border-radius: 10px; + margin-bottom: 15px; + border-left: 4px solid #667eea; +} + +.result-item strong { + color: #667eea; + display: inline-block; + width: 100px; +} + +.loading { + text-align: center; + padding: 40px; +} + +.spinner { + width: 50px; + height: 50px; + border: 5px solid #f3f3f3; + border-top: 5px solid #667eea; + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 20px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4cb1d0f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +fastapi +uvicorn[standard] +openai +python-dotenv \ No newline at end of file