init commit, input data parsing logic and page

master
jaehwang 2025-08-19 13:08:28 +09:00
commit a60d49ae92
12 changed files with 1262 additions and 0 deletions

189
.gitignore vendored Normal file
View File

@ -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

37
README.md Normal file
View File

@ -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"

61
backend/main.py Normal file
View File

@ -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)

15
backend/models.py Normal file
View File

@ -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

68
backend/openai_parser.py Normal file
View File

@ -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
)

428
docs/api_guide.md Normal file
View File

@ -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)

127
docs/project_logs.txt Normal file
View File

@ -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 작성 (설치 및 실행 방법)
- 모든 작업 완료

38
docs/project_plan.md Normal file
View File

@ -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 업데이트

46
frontend/index.html Normal file
View File

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>부동산 검색 - 자연어 분석</title>
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div class="container">
<header>
<h1>🏠 부동산 자연어 검색</h1>
<p>원하시는 부동산 조건을 자유롭게 입력해주세요</p>
</header>
<main>
<div class="search-section">
<textarea id="searchInput" placeholder="예: 2
서초동 30평대 아파트 매매 10억 이하
방 3개짜리 월세 100/50 이하 판교"></textarea>
<button id="searchBtn" onclick="parseQuery()">분석하기</button>
</div>
<div class="examples">
<h3>입력 예시:</h3>
<div class="example-chips">
<span class="chip" onclick="setExample(this)">강남 전세 3억 방 2개</span>
<span class="chip" onclick="setExample(this)">판교 30평 아파트 매매</span>
<span class="chip" onclick="setExample(this)">서초동 월세 200/100</span>
</div>
</div>
<div id="resultSection" class="result-section" style="display: none;">
<h2>📊 분석 결과</h2>
<div id="resultContent"></div>
</div>
<div id="loadingSection" class="loading" style="display: none;">
<div class="spinner"></div>
<p>AI가 분석 중입니다...</p>
</div>
</main>
</div>
<script src="/static/script.js"></script>
</body>
</html>

97
frontend/script.js Normal file
View File

@ -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 += `<div class="result-item">
<strong>거래 유형:</strong> ${data.transaction_type}
</div>`;
}
if (data.price) {
html += `<div class="result-item">
<strong>가격:</strong> ${data.price}
</div>`;
}
if (data.location) {
html += `<div class="result-item">
<strong>위치:</strong> ${data.location}
</div>`;
}
if (data.area) {
html += `<div class="result-item">
<strong>면적:</strong> ${data.area}
</div>`;
}
if (data.rooms) {
html += `<div class="result-item">
<strong> 개수:</strong> ${data.rooms}
</div>`;
}
if (!html) {
html = '<p>추출된 정보가 없습니다. 더 구체적으로 입력해주세요.</p>';
}
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();
}
});
});

152
frontend/style.css Normal file
View File

@ -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); }
}

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
fastapi
uvicorn[standard]
openai
python-dotenv