네이버 부동산의 아파트 매물 정보를 수집, 분석, 관리할 수 있는 강력한 데스크톱 애플리케이션입니다.
다중 단지 동시 크롤링, 실시간 가격 변동 추적, 신규 매물 알림, 지도 기반 광역 탐색, 그리고 엑셀 내보내기 기능 등을 제공합니다.
Claude Opus 4.5, Gemini 3.0 Pro 를 이용하여 제작 및 지속 개선중입니다.
- 🧠 Playwright 기본 엔진: 기본 수집 엔진이
Playwright로 확장되었고, 기존Selenium엔진은 일반 단지 수집(complex모드) fallback 경로로 유지됩니다. (geo sweep은 Playwright 전용) - 🧭 지도 탐색 탭 추가: 별도
지도 탐색탭에서 위도/경도/줌 기준geo sweep수집을 실행할 수 있습니다. - 🏘️ APT + VL 동시 수집: 아파트(
APT)와 빌라/연립/다세대(VL)를 모두 탐색 대상으로 지원합니다. - 📡 응답 기반 목록 수집: 목록 단계는 Playwright response interception 기반으로 수집해 속도와 유연성을 높였습니다.
- 📱 모바일 상세 선택 수집: 모바일 상세 페이지 워커 풀은 가격/면적 필터를 통과한 매물에 우선 적용되어 중개사/전화/기전세금/전세 이력을 확장 수집합니다.
- 💰 갭 분석 필드 확장:
기전세금(원),갭금액(원),갭비율,자산유형,수집모드등이 UI/DB/export 전 구간에 반영됩니다. - 🗂️ 자동 단지 등록: 지도 탐색으로 발견한 단지는 DB에 자동 등록되어 이후 그룹/재수집에 재활용할 수 있습니다.
- 📦 PyInstaller 경량/번들 선택: 기본 배포는 Chromium 번들을 포함하며, 필요 시 환경변수로 slim 빌드로 전환할 수 있습니다.
- 🛡️ DB 복원 안정성 강화: SQLite online backup 기반 백업/복원, 무결성(
integrity_check) 및 필수 테이블 검증을 추가했습니다. - 🧰 유지보수 모드 복원 플로우: DB 복원 중 스케줄/크롤링/UI 액션을 안전하게 차단하고, 종료 후 자동 복구합니다.
- 🔍 고급 필터 동선 복원: 앱 메뉴(
🔍 필터)에서 고급 필터 진입/해제 경로를 공식 지원합니다. - 📉 가격변동 표기 정합성: UI/CSV/Excel에서 가격 하락값이 부호(
-) 포함 문자열로 일관 표시됩니다. - 📊 차트 폰트 fallback 개선: 한글 폰트 미지원 환경에서도 통계/대시보드 렌더 경고를 줄이도록 fallback을 적용했습니다.
- 🧵 중단/종료 반응성 강화: 재시도 대기 및 크롤링 sleep 구간을 인터럽트 가능하게 변경해 종료 실패 가능성을 낮췄습니다.
- 🪟 URL 배치 등록 비동기화: 단지명 조회를 worker thread로 분리하고 진행률/취소 버튼을 추가해 UI block을 제거했습니다.
- 🗃️ 데이터 정합성 강화: 단지 삭제 시 그룹 매핑 orphan이 남지 않도록 정리 로직과 FK 정책을 보강했습니다.
- 🧠 검색/캐시 동작 개선: 검색 이력 dedupe 키를 확장하고, Playwright 응답이 확인된 0건(
confirmed_empty)만 negative cache로 저장합니다(응답 drain timeout 시 저장 건너뜀). - 🧭 캐시 키 컨텍스트 분리: 캐시 키는
complex_id + trade_type기본 키에mode/asset_type/source_lat/source_lon/source_zoom/marker_id컨텍스트 네임스페이스를 추가해complex/geo_sweep간 메타데이터 오염을 방지합니다. - 🔖 버전/저장 카운트 정밀화:
APP_VERSION단일 소스(v15.0) 적용 및 DB 저장 결과를신규/기존/실패로 분리 표시합니다. - 🔒 DB 잠금/손상 대응 강화: DB write 경로를 직렬화하고 lock 재시도/빠른 실패를 적용해
database is locked에 대한 UI 멈춤을 완화했습니다. - 🧯 손상 감지 시 write 차단:
database disk image is malformed감지 시 write circuit-breaker를 활성화해 수집은 유지하고 추가 손상을 방지합니다.
- 🧪 Repo-wide Pyright 정리: workspace 기준
pyright오류를 다시 정리하고, 새live_smokeCLI와 테스트 더블 monkeypatch까지 타입 시그니처를 맞췄습니다. - 🔤 인코딩 가드 강화: root
.md/.spec/.gitignore/.editorconfig/.vscode/settings.json/.github/workflows/ci.yml까지 UTF-8/BOM/mojibake 스캔 대상으로 고정했습니다. - 🧭 Pylance 설정 고정:
.vscode/settings.json에include/exclude/extraPaths/encoding기준을 명시해 로컬 Pylance가 repo 기준과 같은 범위로 진단하도록 맞췄습니다. - 🚦 Live Smoke CLI 추가:
python app_entry.py --live-smoke [--smoke-headless] [--smoke-url ...] [--smoke-complex-id ...] [--smoke-article-id ...]로 GUI 없이 Playwright 실제 접근 경로를 점검할 수 있습니다. - 🌐 2026-03-25 smoke 확인 결과: headless 기준
fin.land200,new.land200,m.land는fin.landmap 경로로 redirect 되는 현재 동작을 확인했습니다.
- 🧩 상세 파서 실사이트 복구: 모바일 상세 수집은
front-api응답의brokerageName,brokerName,phone.{brokerage,mobile},prevJeonse*를 우선 해석하고, source 선택 기준도 body 길이 대신실제 필드 확보 점수로 전환했습니다. - ⚡ 단지명 조회 fast-fallback: direct API가
429를 반환하면 프로세스 단위 5분 cooldown을 걸고 즉시단지_{id}fallback을 반환합니다. 성공 조회명은 메모리 캐시에 재사용합니다. - 🔎 Live Smoke 확장: 기본 smoke가
home + complex + detailprobe를 수행하며,--smoke-complex-id,--smoke-article-id로 기본 샘플 ID를 덮어쓸 수 있습니다. - 🪟 URL family 정합화: URL 배치 등록과 Selenium complex fallback은 helper 기반 URL 생성으로 통일했고, 사용자 안내 문구는
URL family는 시점에 따라 달라질 수 있음기준으로 정리했습니다. - 🌐 2026-04-10 smoke 확인 결과: headless 기준
home/complex/detailprobe가 모두 성공했고, detail probe는front-api/v1/article/agent응답까지 확인했습니다.
- ⏰ 예약 실행 slot 소비 조건 보강:
complex와geo_sweep예약 실행은 이제 실제start_crawling()성공 시에만last_run_slot을 기록합니다. validation early-return이나 시작 실패는 같은 window 안 재시도를 막지 않습니다. - 🧾 예약 실패 시 수동 작업 목록 복원:
complex예약 실행은 기존 수동 task list를 snapshot한 뒤 예약 대상을 적재하며, 예약 시작 실패 또는 실행 가능한 APT 대상이 없을 때는 기존 목록과 선택 상태를 즉시 복원합니다. - 🏷️ 런타임 dedupe 자산 스코프 정합화:
_item_dedupe_key()는 이제(asset_type, complex_id, article_id, trade_type)기준을 사용하고, legacy blankasset_type은APT로 정규화합니다. - 🧪 CI 정책 조정: 2026-04-11 당시 GitHub Actions는
compileall + pyright + preflight중심으로 유지했으며, 현재 기준도 다시compileall + pyright + preflight입니다. - 📦 2026-04-11 .spec 재점검: 이번 변경은 runtime/test/documentation 레벨에 머물러
naverland-scrapper.spec의 hidden import/runtime hook/data bundle 수정은 추가로 필요하지 않았습니다.
- 🏘️ complex 타깃 자산 보존: URL 일괄 등록, DB/그룹/최근 검색 복원, 수동 task 목록이 이제
APT/VL자산유형을 끝까지 유지합니다. 같은 숫자cid라도APT와VL은 별도 task로 함께 다룰 수 있습니다. - 🔗 VL houses URL 정합화:
new.land.naver.com/houses/{id}계열 URL은VL스코프를 유지한 채 단지명 조회, task 등록, 단지 페이지 열기 경로로 연결됩니다. - 🎭 엔진 제한 명확화: Playwright
complex모드는APT와VL을 모두 처리하고, Seleniumcomplex모드는 현재APT만 지원합니다.VL대상은 Selenium 직접 시작과 fallback 모두 차단/건너뜀 처리됩니다. - 📊 대시보드 범위 정합화: 소멸 매물 카드는 DB 전체가 아니라 현재 화면에 잡힌
(asset_type, complex_id, trade_type)범위만 집계합니다. - 💸 월세 이력 기준 보정: 월세 이력/가격변동/알림 비교는
price_text에보증금/월세를 그대로 저장하면서, 비교 기준값은월세 금액을 우선 사용합니다. - ⭐ 즐겨찾기 UI 정리: 비어 있던
링크컬럼을 제거해 실제 동작하는 열만 남겼습니다. - 🧪 CI 단순화: GitHub Actions는 이제
compileall + pyright + preflight만 실행합니다. - 📦 2026-04-16 .spec 재점검: 이번 패스도 runtime/UI/test/doc 레벨 변경에 머물러
naverland-scrapper.spec의 hidden import/runtime hook/data bundle 수정은 추가로 필요하지 않았습니다.
- 📉 가격 변동 그래프: 이전 수집 기록과 비교하여 가격 상승/하락 내역을 그래프로 표시하고 추적합니다.
- 각종 버그 수정 및 리팩토링: 세세한 사용성 버그등이 수정되었습니다.
- 📉 가격 변동 추적: 이전 수집 기록과 비교하여 가격 상승/하락 내역을 추적하고 하이라이트 표시합니다.
- 🆕 신규 매물 배지: 새로 등록된 매물을 즉시 식별할 수 있도록 'NEW' 배지를 표시합니다.
- 🔍 고급 필터링: 가격 범위, 면적, 층수(저/중/고), 키워드 포함/제외 등 상세 조건 필터링을 지원합니다.
- 📊 엑셀 템플릿: 엑셀로 내보낼 때 원하는 컬럼만 선택하거나 순서를 변경하여 저장할 수 있습니다.
- 🔗 URL 일괄 등록: 네이버 부동산 URL이나 단지 ID 텍스트를 붙여넣어 여러 단지를 한 번에 등록합니다.
- 📝 특징 파싱 개선: 중개사 광고 멘트를 필터링하고 핵심 특징만 추출합니다.
- 다중 단지 수집: 여러 아파트 단지를 등록하고 한 번에 크롤링할 수 있습니다.
- 지도 기반 광역 탐색: 좌표 기반으로 주변 단지/매물을 sweep하며 자동 등록과 즉시 상세 수집까지 수행합니다.
- 데이터베이스 관리: 수집된 단지 정보와 매물 이력을 로컬 DB(sqlite3)에 안전하게 저장합니다.
- 그룹 관리: 관심 단지를 그룹별로 묶어 관리하고 예약 실행할 수 있습니다.
- 시세 히스토리: 단지별/평형별 시세 변화를 그래프 데이터로 축적합니다.
- 알림 시스템: 특정 가격이나 면적 조건에 맞는 매물이 발견되면 알림을 표시합니다.
- 편의 기능: 다크/라이트 모드, 트레이 최소화, 예상 소요 시간 표시, 결과 요약 대시보드.
- 중복 축약 표시: 동일 단지/가격/평수/층 조건의 유사 매물을
N건으로 묶어 결과를 간결하게 볼 수 있습니다. - 확장 상세 필드:
부동산상호,중개사이름,전화1,전화2,기전세금(원),갭금액(원),갭비율을 저장/내보내기할 수 있습니다.
- 이력 DB 배치 반영: 매물 이력 업데이트를 배치 업서트로 처리해 크롤링 중 DB 오버헤드를 감소시켰습니다.
- 결과 검색 최적화: 행별 검색 캐시/hidden 상태 캐시 + 디바운스로 대량 결과 필터링 지연을 줄였습니다.
- 대량 렌더링 개선: 결과 테이블 반영을 chunk 단위로 처리하여 UI 멈춤을 완화했습니다.
- 로그 렌더링 제한: 최대 라인 수를 유지해 장시간 실행 시 로그 탭 성능 저하를 방지합니다.
- 대시보드 집계 캐시: 대시보드 통계와 현재 결과 범위의 소멸 매물 집계를 캐시해 반복 새로고침 비용을 줄였습니다.
- 카드뷰 렌더 최적화: 카드 스타일 캐시와 배치 렌더링으로 대량 카드 표시 시 체감 속도를 개선했습니다.
- 대시보드 지연 초기화: 히스토리/통계/즐겨찾기/DB 탭은 즉시 로드하되, 대시보드는 첫 진입 시 생성해 시작 시간을 줄였습니다.
- compact 중복 묶기 실시간 최적화: compact 결과는 배치마다 전체 재렌더하지 않고 dirty row만 증분 갱신하며, 정렬은 사용자 액션이나 수집 완료 시점에 한 번만 다시 맞춥니다.
- 즐겨찾기 부분 갱신: app-level favorite key 초기화는 경량
(asset_type, article_id, complex_id)조회를 사용하고, 토글 시 전체 결과 재구성 대신 해당 카드/row 상태만 갱신합니다. - hidden-tab stale refresh: history/stats/favorites/dashboard는 크롤링 완료 후 숨겨진 상태면 즉시 다시 불러오지 않고 stale로 표시한 뒤 다음 탭 진입 때 1회만 갱신합니다.
- 크롤링 속도 즉시 저장: 속도 슬라이더 변경 시
settings.json의crawl_speed가 즉시 갱신됩니다. - 기본 정렬 적용:
default_sort_column/default_sort_order가 결과 테이블 정렬 기준에 실제 적용됩니다. - 검색 디바운스 즉시 반영: 설정창에서
result_filter_debounce_ms변경 후 즉시 검색 반응 주기에 반영됩니다. - 완료 알림음 반영:
play_sound_on_complete=true일 때 크롤링 완료 시 시스템 비프음이 재생됩니다. - 알림 중복 방지: 동일 규칙+매물은 하루 1회만 알림되며 DB(
article_alert_log)에 기록됩니다. - 소멸 판정 범위 제한: 소멸 매물 처리는 이번 실행 대상 단지/거래유형 범위로 제한됩니다.
- 고급 필터 경로 정리: 고급 필터는
수집 탭(CrawlerTab)의🔍 고급필터버튼에서 직접 설정합니다. - 월세 가격 정책 분리: 월세 필터는
보증금 범위와월세 금액 범위를 각각 입력하며, 두 조건을 모두 만족해야 통과합니다. - 캐시 정확도 개선: 캐시는 필터 통과 결과가 아니라 원본(raw) 매물 기준으로 저장하고, 조회 시 현재 필터로 재평가합니다.
- 종료 안전성 강화: 앱 종료 시 크롤링 스레드 종료 타임아웃이 발생하면 DB를 닫지 않고 종료를 중단하며 재시도를 안내합니다.
- Python 3.9 이상
- Google Chrome 브라우저 (Selenium fallback용)
- Playwright Chromium 브라우저
터미널(CMD)에서 아래 명령어를 입력하여 필수 라이브러리를 설치하세요.
pip install -r requirements.txt
Playwright Chromium 브라우저도 함께 설치해야 합니다.
playwright install chromium
기본 설정(crawl_engine=playwright)을 유지할 경우 소스 실행에는 로컬 Playwright Chromium이 필요하고, frozen 빌드는 기본 번들 Chromium으로 바로 실행됩니다.
crawl_engine=selenium으로 전환한 환경에서는 Playwright browser 미설치가 warning으로만 처리됩니다.
python -m src.main
또는
python src/main.py
프로그램 시작 시에는 필수 라이브러리/디렉토리/충돌 마커와 effective crawl engine 기준 브라우저만 경량 preflight로 점검합니다.
전체 internal import smoke를 포함한 full preflight는 app_entry.py --preflight 또는 python -m src.utils.preflight 경로에서 실행합니다.
추가로 data/settings.json 기준의 effective crawl_engine를 계산해, playwright를 실제로 사용할 런타임에서만 Playwright Chromium 존재 여부를 시작 차단 조건으로 확인합니다.
실사이트 접근 확인은 python app_entry.py --live-smoke --smoke-headless로 수행할 수 있습니다. 기본적으로 home + complex + detail probe를 실행하며, 필요 시 --smoke-url로 경로를 추가하고 --smoke-complex-id / --smoke-article-id로 샘플 ID를 덮어쓸 수 있습니다.
기본 배포 프로필은 naverland-scrapper.spec 기준 **onedir(Chromium 번들 포함)**입니다.
- 기본(onedir, Chromium 번들 포함):
pyinstaller naverland-scrapper.spec - onefile 강제: PowerShell에서
$env:NAVERLAND_ONEFILE='1'; pyinstaller naverland-scrapper.spec - onedir 명시: PowerShell에서
$env:NAVERLAND_ONEFILE='0'; pyinstaller naverland-scrapper.spec - slim(현재 프로필 유지): PowerShell에서
$env:NAVERLAND_BUNDLE_CHROMIUM='0'; pyinstaller naverland-scrapper.spec - 콘솔 디버깅 빌드(현재 프로필 유지): PowerShell에서
$env:NAVERLAND_CONSOLE='1'; pyinstaller naverland-scrapper.spec
현재 spec은 Playwright hidden imports를 포함하며, 기본 빌드에 Chromium 번들을 포함합니다.
NAVERLAND_CONSOLE=1을 주면 GUI 빌드에서도 콘솔 창을 띄워 시작 실패 로그를 직접 확인할 수 있습니다.
빌드 산출물 이름은 모드에 따라 다릅니다.
- onedir(Chromium 번들 포함, 기본):
dist/naverland/ - onedir(slim):
dist/naverland_slim/ - onefile(Chromium 번들 포함):
dist/naverland_onefile.exe - onefile(slim):
dist/naverland_onefile_slim.exe
-
수집 모드 선택
- 데이터 수집 탭: 기존 단지 ID/URL 기반 수집용입니다.
- 지도 탐색 탭: 위도/경도/줌 기준으로 주변 지역을 sweep하며 단지를 자동 등록합니다.
-
단지 등록
- 검색: '단지 목록' 탭에서 단지명과 단지 ID(네이버 URL의 숫자 부분)를 입력하여 추가합니다.
- 일괄 등록: 'URL등록' 버튼을 눌러 여러 URL을 붙여넣으면 자동으로 파싱하여 등록합니다.
new.land complex,new.land houses(VL),land.naver.com complexNo,m.land,fin.land article계열을 지원하며,housesURL은VL스코프를 유지한 채 등록됩니다. 단지명 direct lookup이 rate limit에 걸리면 일시적으로단지_{id}fallback 이름으로 표시될 수 있습니다.
-
조건 설정
- 거래 유형(매매/전세/월세)을 선택합니다.
- 필요 시 가격 및 면적 필터를 활성화하여 범위를 지정합니다.
- 월세는
보증금 범위와월세 금액 범위를 각각 설정하며, 두 조건을 모두 만족해야 결과에 포함됩니다. - 기본 엔진은 설정 또는 수집 탭에서
playwright/selenium을 선택할 수 있습니다. 현재Playwright complex는APT/VL을 모두 지원하고,Selenium complex는APT만 지원합니다. - 지도 탐색 탭에서는
APT,VL, 줌, 링 수, 그리드 간격, 지점 대기시간을 조정할 수 있습니다. - 지도 탐색(
geo sweep) 모드는 Playwright 전용이며 Selenium fallback은 지원하지 않습니다. - 고급 필터가 필요하면 상단 메뉴
🔍 필터 > ⚙️ 고급 결과 필터또는 수집 결과 영역의⚙️ 고급필터버튼으로 진입합니다. - 고급 필터 해제는 메뉴
🔍 필터 > 🧹 고급 필터 해제또는 결과 영역🧹 필터해제버튼으로 수행합니다.
-
크롤링 시작
▶️ 크롤링 시작 버튼을 누르거나 단축키 Ctrl+R을 입력합니다.- 진행 상황과 예상 남은 시간이 하단에 표시됩니다.
-
결과 확인 및 저장
- 수집된 매물은 테이블에 표시되며, 더블 클릭 시 네이버 부동산 페이지로 이동합니다.
- 결과 테이블에는
자산유형,기전세금,갭금액,갭비율이 기본 표시됩니다. - 💾 저장 버튼을 눌러 엑셀(xlsx), CSV, JSON 형식으로 데이터를 내보낼 수 있습니다.
-
DB 복원 시 주의
- DB 복원 중에는 앱이 유지보수 모드로 전환되어 크롤링/일부 UI 동작이 일시 차단됩니다.
- 진행 중 크롤링이 안전하게 종료되지 않으면 복원은 중단되며, 종료 후 다시 시도해야 합니다.
| 기능 | 단축키 | 설명 |
|---|---|---|
| 크롤링 시작 | Ctrl + R | 선택된 단지의 매물 수집을 시작합니다. |
| 크롤링 중지 | Ctrl + Shift + R | 진행 중인 작업을 중지합니다. |
| Excel 저장 | Ctrl + S | 결과를 엑셀 파일로 저장합니다. |
| CSV 저장 | Ctrl + Shift + S | 결과를 CSV 파일로 저장합니다. |
| 새로고침 | F5 | 현재 탭의 데이터를 새로고침합니다. |
| 검색 | Ctrl + F | 결과 내 검색창으로 포커스를 이동합니다. |
| 설정 | Ctrl + , | 설정 창을 엽니다. |
| 테마 변경 | Ctrl + T | 다크/라이트 모드를 전환합니다. |
| 트레이 최소화 | Ctrl + M | 프로그램을 시스템 트레이로 숨깁니다. |
| 종료 | Ctrl + Q | 프로그램을 종료합니다. |
프로그램 실행 시 자동으로 생성되는 디렉토리입니다.
- data/: 데이터베이스(complexes.db), 설정 파일(settings.json), 히스토리 등이 저장됩니다.
- logs/: 날짜별 실행 로그 파일이 저장되어 오류 추적에 사용됩니다.
naverland-scrapper.spec는Playwrighthidden import/runtime hook을 포함합니다.- 기본 빌드는
onedir + Chromium 번들 포함프로필이며, 필요 시NAVERLAND_ONEFILE=1로 onefile,NAVERLAND_BUNDLE_CHROMIUM=0으로 slim 빌드를 생성할 수 있습니다. - 카드뷰 위젯은
src/ui/widgets/cards.py, 대시보드 위젯은src/ui/widgets/dashboard.py로 분리되어 있으며 이번 정합성 패스에서도 추가 hidden import 수정은 필요하지 않았습니다. - 2026-04-10 실사이트 정합 재점검 기준으로 상세 파서
front-api매핑, 단지명 429 cooldown, live smoke CLI 확장, helper 기반 URL family 정리는 모두 runtime/UI 레벨 변경이라 추가 PyInstaller 수정이 필요하지 않았습니다. - 2026-04-11 예약/자산 스코프/CI 신뢰성 패스 기준으로도 schedule slot 소비 조건 보강, 예약 실패 시 task snapshot 복원, runtime item dedupe 자산 스코프 정렬, targeted pytest CI 추가는 모두 runtime/test/doc 레벨 변경이라 추가 PyInstaller 수정이 필요하지 않았습니다.
- 2026-04-16 자산 스코프/월세 이력/대시보드/CI 정합 패스 기준으로도 VL houses URL 보존, complex task
(asset_type, cid)dedupe, scoped disappeared count, 월세 rent 기준 비교, pytest CI 복원은 모두 runtime/test/doc 레벨 변경이라 추가 PyInstaller 수정이 필요하지 않았습니다.
- 이 프로그램은 개인적인 학습 및 편의를 위해 제작되었습니다.
- 과도한 속도로 크롤링을 시도할 경우 네이버 부동산의 접속이 일시 차단될 수 있으므로, '보통' 또는 '느림' 속도 사용을 권장합니다.
- 수집된 데이터의 상업적 이용에 대한 책임은 사용자에게 있습니다.
- 코드베이스 재점검 기준:
- 기본 PyInstaller 프로필은
onedir + Chromium bundle입니다. onefile은NAVERLAND_ONEFILE=1에서만 활성화됩니다.- 카드 뷰 위젯은
src/ui/widgets/cards.py, 대시보드 위젯은src/ui/widgets/dashboard.py에 분리되어 있습니다. startup_lazy_noncritical_tabs는 레거시 호환용False키로만 유지되며, 현재는 대시보드만 첫 진입 시 생성되고 history/stats/favorites는 hidden-tab stale refresh 정책으로 갱신됩니다.
- 기본 PyInstaller 프로필은
- 성능 정합 메모:
- 대시보드는 통계/소멸 집계 캐시와 지연 차트 캔버스 초기화, 첫 탭 진입 시 위젯 생성을 사용합니다.
- Playwright 모바일 상세는 현재 가격/면적 필터를 통과한 매물에 우선 적용됩니다.
- 결과 렌더링은 검색 캐시 사전 생성, 로그 block count 제한, 카드 스타일 캐시, compact dirty-row 갱신, hidden-tab stale refresh를 사용합니다.
.gitignore점검 결과:- 기존 build/log/data/Playwright 산출물 규칙은 유지하고, 개발 도구 캐시(
.mypy_cache/,.ruff_cache/,.nox/,node_modules/,coverage.xml)를 예방적으로 추가했습니다.
- 기존 build/log/data/Playwright 산출물 규칙은 유지하고, 개발 도구 캐시(
- 최근 본 매물 동선 정합화:
- 매물 열기 경로를 app-level handler로 통합했습니다.
- 결과 테이블 더블클릭, 카드뷰 클릭, 최근 본 매물 다이얼로그, 즐겨찾기 탭 열기 버튼이 모두 같은 최근 본 매물 기록 경로를 사용합니다.
recently_viewed_count가 실제 저장/표시 최대 개수에 반영되고, dedupe 키는(asset_type, complex_id, article_id)로 고정됩니다.
- 예약 실행 안정화:
- exact-minute match 대신 slot 기반 catch-up window(10분)로 동작합니다.
schedule_config에는last_run_slot,last_run_at내부 메타가 저장됩니다.- busy / no-target skip은 같은 window 안에서 재시도 가능하며, 실제 시작된 경우에만 slot을 소비합니다.
- 대시보드 / 설정 정합화:
- 빈 데이터 진입 시 카드/차트/trend 문구를 명시적으로 clear 하도록 정리했습니다.
show_trend_analysis가 실제 trend frame visibility를 제어합니다.- trend 영역은 placeholder 대신 현재 집계 기반 summary를 표시합니다.
result_tab_mode는 deprecated key로 제거했고,startup_lazy_noncritical_tabs는 레거시 no-op 키로만 유지됩니다.
- Packaging / docs / ignore review:
naverland-scrapper.spec는 이번 범위에서도 추가 hidden import/runtime hook/data bundle 수정이 필요하지 않았습니다..gitignore는 현재 build/log/data/Playwright/runtime artifact 규칙으로 충분하며 새 ignore 패턴은 추가하지 않았습니다.- GitHub CI는 현재 테스트를 실행하지 않고, 정적 검사와 preflight 점검만 수행합니다.
- Validation:
python -m pytest -q=>182 passednpx pyright=>0 errors
playwright_response_drain_timeout_ms설정을 추가했습니다. 기본값은3000이며, Settings에서 조정할 수 있습니다.complex모드 캐시 컨텍스트는 현재mode=complex,asset_type=<target asset_type>,marker_id=""로 정규화됩니다.- 과거 complex 캐시 키(예: marker 기반 키)는 읽기 호환만 유지하며, 적중 시 정규 키로 재저장됩니다.
geo_sweep모드는 계속 Playwright 전용이며 Selenium fallback은 지원하지 않습니다.- Geo 실행 중 운영 통계(
geo_discovered_count,geo_dedup_count,response_drain_wait_count,response_drain_timeout_count)를 로그/상태바에서 확인할 수 있습니다.
naverland-scrapper.spec를 점검한 결과, 이번 기능 반영 범위에서는 추가 hidden import나 runtime hook 변경 없이 현재 설정으로 충분합니다.- 빌드 정책은 기존과 동일합니다.
- 기본:
pyinstaller naverland-scrapper.spec(onedir) - 선택:
NAVERLAND_ONEFILE=1로 onefile
- 기본:
- 문서 정합 기준을 다음으로 고정합니다.
- Selenium fallback은
complex모드에서만 지원 geo_sweep는 Playwright 전용- 현재
complex캐시 컨텍스트 정규화(mode=complex,asset_type=<target asset_type>,marker_id="") - 레거시 complex 캐시 키는 읽기 호환만 유지
- Selenium fallback은
CrawlerThread가 실행 단위 pair 큐를 추적하고, Playwright 실패 시 Selenium fallback은 미처리 pair만 이어서 수행합니다.- 수집 결과 push 경로에 item dedupe를 추가해
(complex_id, article_id, trade_type)기준 중복 반영을 차단합니다(article_id없으면 dedupe skip). - Playwright negative cache는
response_seen=True+drain_timed_out=False일 때만 저장하며, 캐시 payload에reason=confirmed_empty메타를 포함합니다. - Playwright 경로에 메모리 워치독(500MB)을 추가해 임계치 초과 시 browser/context/page pool을 recycle하고 통계(
playwright_recycle_count,playwright_last_recycle_reason)를 노출합니다. - DB
complexes는(asset_type, complex_id)복합 unique로 자동 마이그레이션되며, legacy row는asset_type='APT'로 승격됩니다. - DB 탭 삭제 UX에 확인 모달과
관련 이력까지 삭제옵션(기본 off)을 추가했습니다. - GitHub CI는 현재 테스트를 실행하지 않고, 정적 검사와 preflight 점검만 수행합니다.
naverland-scrapper.spec은 이번 반영 범위에서 추가 수정 없이 현재 설정(Playwright hidden import/runtime hook/Chromium bundle)으로 유지합니다.
- 크롤링/스크래핑 구현 리스크 감사 문서를 추가했습니다:
crawling_scraping_risk_audit_2026-03-07.md. - 핵심 점검 결론:
- Selenium 경로의 0건 negative cache 저장 조건 강화 필요
- 차단 감지 누적 기반 쿨다운(circuit breaker) 필요
- Playwright 응답 매칭/상세 파싱 성공률 관측성 강화 필요
.spec점검 결과:naverland-scrapper.spec는 현재 코드 구조(분할 리팩토링 포함)에서도 추가 수정 없이 유지 가능합니다.
.spec재점검 결과:src/core/*_parts,src/ui/*_parts분할 리팩토링 이후에도naverland-scrapper.spec는 현재 hidden import/runtime hook 구성으로 충분하며 추가 수정이 필요하지 않습니다.- 감사 문서 상태 정합화:
crawling_scraping_risk_audit_2026-03-07.md를 저장소 기준 문서로 유지하고, 문서 내부에 정합성 추적 메모를 추가했습니다. - 문서 정합성 동기화:
README.md,claude.md,gemini.md,update_history.md의 fallback 정책(complex만 Selenium fallback),geo_sweep정책(Playwright 전용), 분할 리팩토링 경로 표기를 동일 기준으로 맞췄습니다. .gitignore재확인 결과: 현재 빌드/로그/백업/Playwright 산출물 무시 규칙으로 충분하며 추가 패턴은 필요하지 않습니다.
- UI 사용성 정합화:
- 검색 조건 좌/우 패널 + 내부 조건 섹션을 스플리터로 조절하고, 분할 비율을 설정에 저장/복원합니다.
- 입력 위젯 전역 wheel-guard를 도입해
QSpinBox/QDoubleSpinBox/QComboBox에서 휠 오입력 변경을 방지합니다(콤보는 팝업 열린 경우만 휠 허용). - 라이트/다크 테마별 드롭다운 팝업 가독성을 보정했습니다(
QComboBox QAbstractItemView상태별 색상 분리).
- 타입 안정화:
npx pyright src기준0 errors를 달성하도록src/전역 타입 오류를 정리했습니다.
- 패키징 점검:
.spec에서 Matplotlib Qt backend hidden import를backend_qtagg기준으로 정리했습니다.NAVERLAND_CONSOLE=1빌드 스위치를 추가해 GUI 실행 실패 시 콘솔 로그 확인 경로를 열었습니다.- Chromium 번들 탐지 실패(
NAVERLAND_BUNDLE_CHROMIUM=1)가 조용히 묻히지 않도록 spec 단계 경고 메시지를 출력합니다.
- F-01: Selenium parse metadata(
response_seen,parse_success,empty_confirmed,blocked_detected)를 표준화하고,confirmed_empty조건에서만 negative cache를 기록합니다. - F-02: Geo 탭도 일반 탭과 동일하게
retry_on_error=False면max_retry_count=0을 강제합니다. - F-03:
mark_disappeared_articles_for_targets를 대량 타깃에서도 안전한 chunk update 방식으로 변경했습니다. - F-04:
add_complex에 write lock + lock retry(database is locked) + rollback 경로를 추가했습니다. - F-05: 차단 대응은 하이브리드 회로차단기(페어 2회/90초 cooldown, 전역 5회 중단)로 고정 적용했습니다.
- F-06: stats용 complex 목록은 충돌 CID에만
asset:cid복합키를 사용하고, UI는 plain/compound 키를 모두 해석합니다. - F-07: Geo 모드에서
complex_trade_types가 비어 있으면 crawl history 저장을 스킵합니다. - F-08:
playwright_parts/complex_mode.py,playwright_parts/geo_mode.py의 깨진 로그/예외 문자열을 정리했습니다. - F-09: stats payload에
response_seen_count,parse_success_count,parse_fail_count,detail_success_count,detail_fail_count,blocked_page_count를 추가했습니다. - Verification:
pytest -q전체 실행 기준112 passed.
- 알림 스코프를
asset_type기준으로 분리했습니다.alert_settings.asset_type,article_alert_log.asset_type를 추가하고 legacy/blank 값은ALL로 backfill 합니다.- 알림 조회는
requested asset_type + ALL규칙만 반환하며, 동일alert_id/article_id/complex_id/notified_on이라도 자산유형이 다르면 별도 dedupe 합니다. - 알림 설정 UI는
단지명 (APT:cid)/단지명 (VL:cid)를 표시하고,공통 적용(APT/VL)체크 시ALL범위 규칙을 저장합니다.
- complex 모드 작업 목록은 당시
cid기준 dedupe였고, 현재는(asset_type, cid)기준으로 동작합니다.- 수동 추가, DB/그룹/최근 검색/URL/예약 실행이 모두 같은 dedupe 경로를 사용합니다.
- 동일
cid가 다시 들어오면 첫 이름을 유지하고 중복은 스킵 로그/상태 메시지로 안내합니다. start_crawling()직전에도 최종target_list를 다시 정규화합니다.
- item dedupe와 집계 수치를 일치시켰습니다.
_push_item()은 실제 push 성공 여부를bool로 반환합니다.- raw item, Selenium cache hit, DOM parse 경로 모두 실제 push 성공 건만
matched_count와 완료 count에 반영합니다.
- crawl history 메타데이터와 stats UX를 보강했습니다.
- 당시
complex모드가 APT 중심 경로였기 때문에 이력도asset_type='APT'를 명시 저장했습니다. 현재는 target 자산유형을 그대로 저장합니다. - 히스토리 탭은
단지명 / 단지ID / 자산 / 엔진 / 모드 / 거래유형 / 수집건수 / 수집시각을 표시합니다. - 통계 차트는 단일
(trade_type, pyeong)시리즈일 때만 렌더하고, 다중 시리즈면차트를 보려면 거래유형과 평형을 하나로 좁혀주세요.메시지를 보여줍니다.
- 당시
.spec재점검 결과:naverland-scrapper.spec는 이번 변경 범위에서도 추가 hidden import/runtime hook 수정이 필요하지 않았습니다.
.gitignore재점검 결과:- 현재 build/log/data/Playwright 산출물 무시 규칙으로 충분하며 이번 범위에서 추가 수정은 필요하지 않았습니다.
article_history,article_favorites는 이제(asset_type, article_id, complex_id)기준으로 분리 관리됩니다.- 앱 시작 시 자산 스코프 마이그레이션이 필요하면 사전 DB 백업을 만든 뒤 스키마를 재구성합니다.
- legacy blank
asset_type값은APT로 정규화합니다.
- disappeared / purge 정합성을 자산 스코프 기준으로 맞췄습니다.
- disappeared 대상 계산은 내부적으로
(asset_type, complex_id, trade_type)triple만 사용합니다. - purge/delete는
article_history,crawl_history,price_snapshots,alert_settings,article_favorites,article_alert_log를 자산 스코프 predicate로 정리합니다.
- disappeared 대상 계산은 내부적으로
- 저장 메뉴를
화면 기준 저장과원본 저장으로 분리했습니다.- 화면 기준 저장은 현재 검색어, 고급 필터, compact duplicate 상태, 현재 정렬 순서를 반영한 가시 결과만 저장합니다.
- 원본 저장은 기존처럼
collected_data전체를 그대로 저장합니다.
- 예약 실행은
complex와geo_sweep를 모두 지원합니다.complex는 기존 그룹 기반 예약을 유지합니다.geo_sweep는 위도/경도 예약 실행을 지원하고, zoom/rings/step/dwell/asset_types는 저장된 geo 기본값을 사용합니다.
- Geo 운영 통계는 실행 중에도 실시간으로 반영됩니다.
geo_discovered_count,geo_dedup_count가 marker 처리 시점마다 상태바/로그에 즉시 반영됩니다.
- 즐겨찾기 동기화는 자산 스코프 기준으로 통일되었습니다.
- 카드뷰, 즐겨찾기 탭, 최근 본 매물, 결과 재렌더가 모두
(asset_type, article_id, complex_id)키를 공유합니다.
- 카드뷰, 즐겨찾기 탭, 최근 본 매물, 결과 재렌더가 모두
.spec/.gitignore재점검:naverland-scrapper.spec는 이번 범위에서도 추가 hidden import/runtime hook 수정이 필요하지 않았습니다..gitignore는 현재 build/log/data/backup/Playwright 산출물 무시 규칙으로 충분해 추가 수정이 없었습니다.
- 검증:
python -m pytest -q=>137 passed
- Typing baseline:
- workspace 기준
pyrightconfig.json을 추가해 검사 범위를app_entry.py + src + tests로 고정했습니다. - 현재 기준 검증 명령은
npx pyright이며 결과는0 errors입니다. - Pylance/Pyright 오탐이 나던 믹스인/동적 속성/테스트 더블 타입을 정리했습니다.
- workspace 기준
- Encoding baseline:
.editorconfig와.vscode/settings.json을 추가해 UTF-8 저장과 workspace type checking 기준을 고정했습니다.- Python/문서 파일에 남아 있던 UTF-8 BOM을 제거했고, 깨진 주석/문자열을 정리했습니다.
.spec/.gitignorereview:naverland-scrapper.spec는 이번 패스에서도 추가 hidden import/runtime hook/data bundle 수정이 필요하지 않았습니다..gitignore는 기존 산출물 무시 규칙은 유지하고, 새로 추가한pyrightconfig.json과.vscode/settings.json만 추적 가능하도록 예외를 보강했습니다.
- Validation:
npx pyright=>0 errorspython -m pytest -q=>137 passed
- Preflight / runtime safety:
preflight는data/settings.json을 직접 읽어effective crawl_engine를 계산합니다.- Playwright Chromium 미설치는
effective crawl_engine=playwright일 때만 시작 차단으로 처리하고,selenium일 때는 warning-only로 처리합니다. geo_incomplete_safety_mode기본값은true이며, geo incomplete 런에서는 자동 단지 등록 / crawl history 저장 / disappeared marking을 보수적으로 skip합니다.
- Geo / history contract:
crawl_history에run_status(success|partial|failed|incomplete) 컬럼이 추가되었고, History 탭은mode다음에status컬럼을 표시합니다.- geo marker 정규화는
complex_id와marker_id를 분리해 저장합니다. - 성공적으로 검증된 pair가 0개인 런에서는 disappeared marking을 수행하지 않습니다.
.spec/.gitignorereview:naverland-scrapper.spec는 hidden import/runtime hook/data bundle 규칙 변경 없이 유지 가능합니다.- slim 배포에서는
playwright사용 시 로컬 Chromium 또는 번들 포함 기본 빌드가 필요합니다. .gitignore는 현재 build/log/data/backup/Playwright 산출물 기준으로 추가 수정이 필요하지 않았습니다.
- Validation:
pytest -q=>149 passed
- URL batch registration now accepts both
complexes/{complex_id}andhouses/{complex_id}?articleId=...style Naver URLs. NaverURLParseris the single parser used for URL batch/manual registration flows.- Monthly price snapshots now persist two metrics for
월세:price_metric=depositprice_metric=rent
- Monthly stats default to
월세 금액and expose a metric selector for보증금. - Legacy deposit-only monthly snapshot rows are preserved but hidden from default queries and stats screens.
- Scheduled
geo_sweepruns now persist and replay a full geo profile:lat,lon,zoom,rings,step_px,dwell_ms,asset_types
- Manual Geo tab runs remember the last user-entered coordinates via
geo_last_lat/geo_last_lon. - Scheduled geo runs do not overwrite the remembered manual coordinates.
- DB restore now stops both active crawl modes before replacement:
- regular crawler
- geo sweep crawler
- Runtime JSON state files now use atomic write + broken-file quarantine:
settings.jsonpresets.jsonsearch_history.jsonrecently_viewed.jsoncrawl_cache.json
- Packaging / ignore review:
naverland-scrapper.specwas rechecked and still needs no extra hidden import/runtime hook/data bundle changes..gitignorenow ignores*.json.tmpand*.json.broken.*runtime artifacts.
- Validation:
pytest -q=>176 passed