QGIS AI QGIS AI

릴리스 노트

새 소식.

최신 순으로 정리된 릴리스 노트와 개선 사항.

3.8.73 최신
  • 검색/재활용(스니펫·골든) 메커니즘 개선 + 채팅 진행 단계 표시 + 그동안 누적된 3.8.59~3.8.72 미배포분 포함. (1) [검색 재활용 R1~R5] 가장 신뢰도 높은 golden 패턴/correction pair 검색을 code_ref 와 동일한 의미(임베딩)+Jaccard 하이브리드로 승격해 동의어/패러프레이즈 회수율 개선(R1). 슬롯이 못 잡은 숫자 파라미터가 다르면 옛 숫자가 박힌 코드를 확신 반환하던 정확성 사고를 직접반환 직전 numeric 안전장치로 차단(R2). 임베딩 비활성(키 없음/오프라인) 시 Jaccard 폴백으로 조용히 강등되던 것을 1회 경고 + 결과 degraded 플래그로 가시화(R3). 어미를 일일이 나열하던 한국어 토큰 매칭을 교착어 특성(어간=접두) 기반 접두 soft-match 로 교체 — "추출해줘"≈"추출하기" 처럼 임의의 어미를 목록 없이 일반화(R4). 점수식에서 신뢰소스에서 상수(1.0)라 비차별적이던 success_rate 가중치(0.20→0.10)를 의미점수(0.40→0.45)·correction(0.05→0.10)으로 재배분(R5). 신규 retrieval_core.py(순수 결정/매칭 로직, 단위 테스트 가능) + apply_slot_catalog 숫자 경계 치환("8"→"18" 이 "80" 오염 방지). (2) [채팅 진행 스피너] 응답 대기 중 채팅창에 회전 스피너 + 현재 단계(🧭의도 해석 → 🔎기억 탐색 → ♻️재활용/🤖AI 생성 → 🔧실행 → 🔍결과 점검·검증)를 실시간 표시. (3) [AGORA 연결 수정] 분산 quorum 보고가 QGIS_NAS_URL env 미설정 시 localhost 로 빠져 조용히 죽던 것을 실제 NAS URL(QgsSettings→config 기본값) + 공유 telemetry JWT 로 배선. (4) [공유 골든 서빙 영속화] NAS 가 7일 purge 되는 logs 에서만 읽고 payload 중첩이라 클라이언트가 못 받던 것을 flat 평탄화 + 번들 시드(golden_v1.json) 영속 서빙으로 폐곡선화. (5) [프롬프트 복원력] 시스템 프롬프트를 기기 결합 암호화 디스크 캐시로 보존 — 재시작 후 NAS 일시 장애에도 동작하되 파일 이동 시 탈취는 차단. PLUGIN_VERSION 3.8.72 → 3.8.73.
3.8.70
  • PNU prefix 안전망 — 인접 구 필지 섞임 핫픽스. 사용자 로그 진단: "[토지소유] source=vworld_ned 첫 매칭 attrs(1165010900200130248): {'ldCodeNm': '서울특별시 서초구 내곡동'}". 사용자가 강남구(11680) 선택했는데 base_layer(70024→영구화 30529) 첫 매칭 PNU 가 1165010900...=서초구(11650) 내곡동. 원인: V-World adsigg 강남구 폴리곤의 정밀도 부족 또는 native:clip 의 경계 근처 부정확성으로 인접 구 필지 일부가 클리핑 후에도 남음 → V-World NED 가 그 PNU 로 호출되어 잘못된 행정구역 데이터 매칭. PNU 첫 5자리는 행정구역 정의상 정확한 truth source 이므로 안전망으로 prefix 검증 추가. [수정] (1) 신규 헬퍼 _extract_sig_code_from_clip — clip_layer 의 sig_cd/sig_code/adm_cd/code 필드 자동 탐지 후 첫 5자리 추출. (2) 신규 헬퍼 _filter_base_layer_by_sig_code — base_layer 의 PNU 필드 첫 5자리가 sig_code 와 다른 피처를 startEditing/deleteFeatures/commitChanges 로 직접 제거. [clip-pnu] 로그로 제거 개수/잔존 개수 출력. (3) load_landinfo_godata 의 base_layer 받은 직후(jiga 분기 + NSDI 분기 둘 다 영향) PNU prefix 필터 호출. (4) load_landinfo_owner_godata 의 base_layer 받은 직후 동일 필터 호출. clip_layer 가 sig_cd 필드를 가진 행정경계 폴리곤일 때만 동작, 다른 케이스(BBOX 폴리곤/사용자 임의 폴리곤)에선 graceful 생략. PLUGIN_VERSION 3.8.69 → 3.8.70.
3.8.69
  • 토지정보 NULL/0 값 표시 핫픽스 — 강남구 흰 영역의 진짜 원인 해결. 사용자 보고: "개별공시지가... 토지소유도 똑같겠지" — v3.8.67 로그가 raw 70024→영구화 30529 정상 클리핑을 보였음에도 캡처 흰 영역이 다수 남음. 진단: QgsGraduatedSymbolRenderer 가 NULL/범위 밖 값을 표시 자체를 안 함 — 강남구 lp_pa_cbnd_bubun 응답의 일부 필지가 jiga=NULL 또는 0 (도로/국유지/하천변/공원/대형 단일필지). _enrich_pnu_geojson 의 NSDI/NED fallback 도 PNU 매칭 못한 필지의 공시지가_원㎡=None 으로 채움 → graduated 미매칭 → 흰색. 토지소유 categorized 도 NULL 소유구분 피처에 동일 문제. [수정] (1) _apply_landprice_graduated_style 을 graduated→expression-based QgsCategorizedSymbolRenderer 로 재작성. CASE WHEN field IS NULL OR field<=0 THEN '미매칭' WHEN field<=break1 THEN 'q1' ... 패턴. '미매칭' 카테고리는 중간 회색(#BDBDBD) 으로 명시 표시 — 어디가 진짜 빈 영역이고 어디가 API 매칭 실패인지 사용자가 즉시 구분 가능. (2) _apply_landowner_categorized_style 도 column-name 기반에서 expression-based 로 변경. CASE WHEN field IS NULL OR trim(coalesce(field,''))='' THEN '미매칭(NULL)' ELSE field END. '미매칭(NULL)' 회색 카테고리 + '' 기타 카테고리 동시 등록. (3) 등급 수 동적 조정 — 값 부족(<5개)이거나 같은 값 연속 break 가 나오는 경우 dedup 후 n_classes 만큼 색상 ramp 균등 샘플링. (4) 모든 값 NULL/0 인 극단 케이스도 단일 회색 카테고리 적용 — 단색 베이지 fallback 회피. PLUGIN_VERSION 3.8.68 → 3.8.69.
3.8.68
  • 플러그인 "로그" 탭에 QgsMessageLog 메시지가 안 흐르던 버그 픽스. 사용자 보고: "이런 내용들이 왜 우리 플러그인의 로그 탭에는 안 보여?" — QGIS 메인 로그 메시지 패널에는 [WFS] 셀 진단/dedup 라인이 잘 떴지만 채팅 dock 의 자체 로그 탭은 텅 비어 있었음. 원인: log_bus 는 Python logging.Handler(LogBusHandler) 기반이라 root logger 의 logger.info(...) 호출만 캡처. 그러나 skill_manager 의 _vworld_wfs_load, _enrich_pnu_geojson, _clip_layer_by_polygon 등 다수 모듈은 진행 로그를 Python logging 우회해서 QgsMessageLog.logMessage(msg, "QGIS AI", Qgis.Info) 로 직접 출력 — C++ 로그 시스템에 직접 들어가 log_bus 가 못 잡았다. [수정] gemini_chat_dock._build_log_tab 끝에서 QgsApplication.messageLog().messageReceived 시그널 구독 추가. "QGIS AI" 태그 메시지만 필터링해 log_bus.emit(level, tag, message) 으로 forward. level 매핑: Qgis.Info → "info", Warning → "warn", Critical → "error". 다른 플러그인의 messageReceived 는 무시해 탭이 어지러워지지 않게 함. closeEvent 에서 messageReceived.disconnect 도 추가 — dock 사라진 후 죽은 람다 호출 segfault 차단. 이로써 [WFS]/[BLDGINFO]/[filter]/[clip-sig]/[emd] 등 QgsMessageLog 로 흘러가던 모든 진행 로그가 자체 로그 탭에서 실시간으로 보임. PLUGIN_VERSION 3.8.67 → 3.8.68.
3.8.67
  • V-World WFS 셀 transient 실패 retry 보강 + 진단 노출. 사용자 보고: "개별공시지가랑 토지소유현황이랑 똑같은 위치가 비어서 와" — v3.8.66 적용 후에도 강남구 중앙에 큰 사각형 + 가장자리 띠형 BBOX 셀 모양의 흰 영역이 남음. 두 함수가 같은 base_layer(lp_pa_cbnd_bubun) 호출이라 동일 셀에서 실패. 원인: _fetch_cell 의 페이지 호출이 셀당 1회만 시도하고 transient timeout/rate-limit/JSON 파싱 실패 시 그대로 빈 채로 종료 — 4096 셀까지 분할되는 강남구에서 V-World 의 일시적 rate-limit 거부가 트리거. [수정] (1) urlopen 호출을 _fetch_with_retry inner helper 로 감쌈 — 최대 3회, 지수 backoff(0.5→1.0→2.0초). URLError/HTTPError/socket.timeout 외에 비정상 응답(non-JSON, 부분 응답)도 retry. ServiceException XML 은 즉시 반환(retry 무의미). (2) 0건+total>0 변칙 응답 — V-World 가 total 은 보고했지만 features 를 0개 준 케이스 — 셀당 1회 1.0초 대기 후 재시도. (3) 셀 진입 시 time.sleep(0.03) — rate-limit 회피용 30ms 간격, 4096셀 부가 ~120초 한도. line 4710 run_multi_skills 의 0.3s 패턴보다 더 보수적. (4) _cell_diagnostics 로그 노출 — _vworld_wfs_load 반환 직전에 "총 N셀, 빈 셀 K개(P%), cap 초과 M개" 요약 + 빈 셀이 있으면 처음 5개의 BBOX 좌표/면적/total 출력. QGIS 로그 메시지 → "QGIS AI" 탭에서 누락 영역을 즉시 확인 가능. (5) JSON 파싱 실패도 명시적 예외 분기로 분리 — 기존엔 json.loads 가 raise 하면 전체 함수 crash. retry 후에도 실패하면 친절한 에러 메시지로 종료. PLUGIN_VERSION 3.8.66 → 3.8.67.
3.8.66
  • v3.8.65 WFS 청킹 회귀 버그 핫픽스. 사용자 보고: "봐봐 다 못불러오잖아" — 강남구 캡처에서 일부 영역(특히 서쪽)이 흰색으로 빠짐. 진단: (1) 결정적 버그 — v3.8.65 의 axis swap 조건 `depth == 0` 제한. 강남구는 ~40km² 면적 사전 분할로 _fetch_cell 최상위가 곧장 4분할 분기로 진입 → 모든 실제 페이지 호출이 depth>=1 에서 발생 → axis swap 시도 자체가 일어나지 않음 → axis 가 부적합한 typename 에서 일부 셀이 0건 silent 누락. (2) 임계 보수화 필요 — 9km² 셀이 V-World 측 1만건 cap 에 걸려 일부 페이지가 잘리던 정황. [수정] (1) axis swap 조건에서 depth 제한 완전 제거 — `_axis_decided` 클로저 flag 도입, 첫 데이터 호출(start_index=0) 에서 0건+total=0 이면 axis 반대로 토글 후 재시도, 성공 axis 를 전역에 fix. (2) per-cell axis fallback — `_local_axis_tried_both` 로 각 셀에서도 axis 양쪽 시도(전역 axis 미결정 시에만 toggle). (3) 임계값 보수화: AREA_TILE_TH 9→4km², MAX_TILE_DEPTH 6→8(최대 65536 셀), CELL_TOTAL_REDIV 10000→5000(더 일찍 재분할). (4) 진단 정보 누적 — `_cell_diagnostics` 리스트에 셀별 (depth, bbox, area, count, total) 기록. 추후 응답 message 에 노출해 사용자가 어느 셀이 비었는지 즉시 진단 가능(향후 노출 예정). PLUGIN_VERSION 3.8.65 → 3.8.66.
3.8.65
  • V-World WFS 재귀 BBOX 분할(청킹) 이식 — 강남구 같은 큰 영역에서 토지소유/공시지가 누락 해결. 사용자 보고: "강남구 전체로 할 때는 경계부분 일부만 불러와지네 공시지가든 소유현황이든" + "통합건물GIS는 누락 없는데 같은 방식으로 하면 되지 않나". 원인: V-World WFS 는 단일 GetFeature 응답에 약 10km² 가이드라인 + 1만건 cap 이 있어 강남구(~40km²) 같은 큰 BBOX 는 첫 응답에 부분/0건 반환. 행정동(~1-2km²)은 정상 동작. 통합건물GIS(_vworld_data_load) 의 _fetch_box 재귀 4분할 패턴은 이미 검증된 솔루션. [수정] _vworld_wfs_load 의 단일 페이지 루프를 _fetch_cell 재귀 분할 패턴으로 재작성 — (1) 셀 면적 사전 검사: 9km²(안전 마진) 초과면 4분할 재귀, 깊이 6까지(최대 4096 셀). (2) 셀 내부에서 startIndex 페이지네이션(MAXFEATURES=1000) 그대로. (3) cell_total ≥ 10000 이면 WFS 내부 cap 의심으로 한 번 더 분할(깊이 3까지). (4) BBOX axis swap(swn↔wsn) 은 depth=0 셀의 첫 페이지에서만 시도하고 결과를 모든 자식 셀에 전파 — 클로저 list 컨테이너로 공유. (5) 셀 경계 중복은 _vworld_data_load 와 동일한 geometry+속성 hash dedup. (6) max_features 호출자 명시 시 dedup 후 누적 cap 적용. 이로써 _vworld_wfs_load 를 호출하는 모든 함수 — load_landinfo_godata/owner_godata 의 base_layer(lp_pa_cbnd_bubun) 호출, load_landinfo_vbum/owner, load_road/water 등 — 가 임의 크기 BBOX 에서 일관되게 동작. PLUGIN_VERSION 3.8.64 → 3.8.65.
3.8.64
  • 토지소유/개별공시지가 시각화 누락 핫픽스. 사용자 보고: 캡처에서 모든 필지가 단색 베이지로만 표시 — "또 비잖아". 원인: 4개 토지정보 함수(load_landinfo_vbum/owner/godata/owner_godata) 가 _add_layer_to_group 으로 단색만 적용하고 QgsCategorizedSymbolRenderer / QgsGraduatedSymbolRenderer 가 빠져 있었음. 매핑 코드(3.8.63) 만 추가됐고 시각화 단계가 누락된 반쪽 픽스. [수정] (1) 신규 헬퍼 _apply_landowner_categorized_style — 필드 후보(소유구분/posesnSeCdNm/posesnSeNm) 자동 탐지 → 11종 카테고리(개인 파랑, 국유 빨강, 외국 보라, 시도유 주황, 군유 짙은빨강, 법인 초록, 종중 노랑, 종교 핑크, 기타 청록, 일본인 다크그레이, 미상 라이트그레이) 색상 매핑. 색맹 대비 + 의미적 구분 가능한 팔레트. raw "코드 XX"/미분류는 회색 fallback 카테고리로 묶음. (2) 신규 헬퍼 _apply_landprice_graduated_style — 필드 후보(공시지가_원㎡/jiga/pblntfPclnd/land_price/공시지가) 자동 탐지 → 5분위 직접 계산(min~max breaks) → YlOrRd ramp(노랑→주황→빨강) graduated renderer 적용. 0/None 값은 분포 계산에서 제외(미매칭 sentinel 보호). 값 분포가 없거나 단일값이면 styling 생략(단색 유지). (3) 4개 함수 호출부에 _add_layer_to_group 직후 헬퍼 호출 추가 — load_landinfo_vbum/owner(V-World 식별자 후보) + load_landinfo_godata(공시지가, V-World jiga 우선 + NSDI fallback 두 경로 모두) + load_landinfo_owner_godata(토지소유). 매핑된 필지는 카테고리/구간별 색으로, 미매칭은 회색 폴백으로 시각적 구분 즉시 가능. PLUGIN_VERSION 3.8.63 → 3.8.64.
3.8.63
  • 토지소유 코드 매핑 확장 + Windows Plugin Manager 삭제/업데이트 실패 영구 해결. (1) 토지소유 _LANDOWNER_CODE_LABELS 에 누락된 5종 추가 — 3300(일본인/창씨명), 3306(법인), 3307(종중), 3308(종교단체), 3309(기타단체). 사용자 보고: "소유 구분 컬럼에 코드 06 같은 매핑 안 되는 값이 있고 '미상(API 미매칭)' 으로 빠짐". 출처 재확인: data.go.kr #15045905 (NSDI 토지소유정보) 의 posesnSeCode 는 "33XX" 4자리 표준이며 전 10종(3300~3309). 일부 응답 파이프라인이 4자리를 끝 2자리로 truncate 해 "06"=법인 같은 케이스가 raw 코드로 노출되던 현상도 함께 처리 — 2자리 fallback 매핑(00~09) 동시 등록. _set 로직은 (4자리 매핑 → 응답 한글라벨 posesnSeCdNm/posesnSeNm → raw "코드 XX" → "미상") 순으로 graceful degrade. (2) Windows 파일 락 해제 — 사용자 보고 "디렉터리 삭제 실패: ... qgis_AI_assistant 권한을 확인하거나 수동으로 삭제" 반복. 근본 원인: data/python-packages 안에 설치된 chromadb/opencv/sklearn 등의 .pyd binary wheel 이 LoadLibrary 핸들을 잡고 있어 폴더 락 유지. 기존 sys.modules wipe 는 plugin 패키지 prefix 매칭만 해서 binary 모듈을 놓침. [수정] unload() 강화 — (a) chromadb.api.client.SharedSystemClient.clear_system_cache() 명시 호출로 모든 PersistentClient 의 background producer/consumer thread + sqlite/hnswlib 핸들 release, (b) plugin_dir 하위 경로(__file__ 기준)에서 import 된 모든 모듈을 sys.modules 에서 제거 — binary .pyd 락 해제의 핵심, (c) sys.path 에서 plugin_dir 하위 경로(data/python-packages) 제거, (d) __pycache__/*.pyc best-effort 삭제, (e) gc.collect() 3회 반복으로 finalizer 핸들 release. 백그라운드 의존성 설치 스레드를 __init__ module-level _dep_install_thread 핸들로 보관 → unload 가 join(timeout=10) 으로 대기 → chromadb 휠을 푸는 도중 unload 가 끝나 폴더 락이 잡히는 race 차단. gemini_chat_dock 의 워커 스레드 join timeout 2→8초 + threading.Event _stop_flag 추가(현재는 안전망, 향후 워커 루프에서 polling 시 즉시 종료). __init__.py 에 _cleanup_stale_pending_dirs() 부트스트랩 추가 — 이전 시도에서 락 때문에 plugins/ 에 남은 .qgis_AI_assistant_pending_* 잔존 폴더를 QGIS 시작 시 자동 청소. PLUGIN_VERSION 3.8.62 → 3.8.63.
3.8.62
  • "레이어 정리" 버튼 신규 추가. 사용자 요청: "현재 연결된 원본을 전부 프로젝트 폴더에 잘 정리해서(그룹이면 그룹별로 폴더 만들어서) 정리하고 기존거 다 삭제". 툴바 4번째 액션 "레이어 정리" — 셋팅 탭의 프로젝트 폴더(_project_folder) 하위 'layers/<그룹경로>/<레이어이름>/' 트리로 모든 디스크 백킹 레이어를 그룹 구조 그대로 복사·재로드. 레이어 패널에서 변경된 이름(layer.name())을 새 파일명으로 사용해 디스크/패널 이름 일치. 메모리/임시 레이어는 save_and_register_layer 로 .shp/.tif 영속화. 원격(WMS/WFS/XYZ/PostGIS)은 건너뛰고 보고서에 기록. 같은 GPKG/Shapefile 을 여러 레이어가 참조하면 파일은 한 번만 복사하고 layername 으로 분기. 성공한 레이어의 원본 파일·sidecar(.shp/.shx/.dbf/.prj/.cpg/.qix/.aux.xml 등)는 Send2Trash 로 휴지통 이동(미설치 시 안내 후 원본 보존 옵션). 신규 파일: qgis_tools/_organize.py (build_organize_plan + execute_organize_plan), organize_layers_dialog.py (미리보기 테이블 + 결과 보고서 다이얼로그). PLUGIN_VERSION 3.8.61 → 3.8.62.
3.8.61
  • safemap 폴리곤 클리핑 silent-fail 핫픽스. 사용자 보고: "5개 스킬 모두 'safemap raster 로드 실패: ..._clip.tif' 팝업". 원인 진단(curl + ls): _clip.tif 파일이 실제로 생성되지 않음. processing.run('gdal:cliprasterbymasklayer') 는 OUTPUT path 를 반환하지만 gdalwarp 가 PNG worldfile(.pgw) 을 자동 인식 못해 INPUT 의 georef 를 못 읽고 silent fail (예외 없이 빈 결과). [수정] (1) 신규 헬퍼 _png_to_geotiff: osgeo Python 바인딩(gdal/osr)으로 PNG 픽셀 데이터 복사 + GeoTransform/Projection 명시 주입한 GeoTIFF 생성. 결과 GeoTIFF 는 모든 GDAL 도구가 georef 인식. LZW 압축 적용. (2) _add_safemap_wms 클리핑 분기 재구성: clip_layer 폴리곤 있으면 PNG → GeoTIFF 변환 → GeoTIFF 를 cliprasterbymasklayer 의 INPUT 으로. (3) _clip_raster_to_polygon: processing.run 결과 path 가 실제 존재하고 크기 > 0 인지 검증 후 반환. silent fail 시 None 반환해 호출자가 폴백 사용 가능. (4) 클리핑 실패 시 GeoTIFF 폴백(직사각형이지만 정상 로드)으로 graceful degrade — 5단계 폴백 체인: clip TIF → georef TIF → raw PNG. PLUGIN_VERSION 3.8.60 → 3.8.61.
3.8.60
  • 생활안전 카테고리 3종 추가 + 폴리곤 클리핑. 사용자 요청: "여성밤길치안안전 / 노인범죄주의구간 / 어린이대상범죄주의구간 추가" + "왜 드롭다운 선택 영역만큼만 크롭이 안 되고 박스로 보이는지". (1) 신규 스킬 3종: add_safemap_woman_night(IF_0080, A2SM_CRMNLHSPOT_F1_TOT) / add_safemap_elder_crime(IF_0082, A2SM_ODBLRCRMNLHSPOT_ODSN, A2SM_OdblrCrmnlHspot_Odsn 스타일) / add_safemap_kid_crime(IF_0081, A2SM_ODBLRCRMNLHSPOT_KID, A2SM_OdblrCrmnlHspot_Kid 스타일) — HARDCODED_SKILLS 등록 + skills_config.json default_lifesafety 그룹 추가(전체 5개). (2) 박스 표시 원인 진단: WMS GetMap 응답이 BBOX 직사각형 PNG → 직사각형 그대로 georef 되어 표시. 폴리곤 모양 클리핑 추가 — 신규 헬퍼 _clip_raster_to_polygon: gdal:cliprasterbymasklayer 로 raster 를 clip_layer 의 폴리곤 모양으로 자름(CROP_TO_CUTLINE=True, KEEP_RESOLUTION=True, ALPHA_BAND=True). _add_safemap_wms 가 clip_layer 받도록 시그니처 확장 + 5개 스킬 함수 모두 kwargs.get('clip_layer') 전달. 사용자가 사이드바 대상지 콤보에서 폴리곤 레이어를 선택하면 PNG 가 폴리곤 모양으로 잘림(투명 알파). 폴리곤 미선택 시 기존 직사각형 폴백 유지. PLUGIN_VERSION 3.8.59 → 3.8.60.
3.8.59
  • safemap WMS 로드 실패 핫픽스 (3.8.58 회귀). 사용자 보고: "범죄주의구간(전체)/CPTED 둘 다 'WMS 레이어 로드 실패' 팝업". 원인 진단: safemap.go.kr WMS 서버는 GetCapabilities 요청에 HTTP 400 을 반환(서버 미구현). QGIS WMS provider 는 layer 등록 시점에 항상 GetCapabilities 로 검증하므로 isValid()=False → 로드 실패. 단 GetMap 은 lowercase 파라미터 + WMS 1.1.1 + serviceKey 케이스 보존 조건에서 정상 200 PNG 응답함을 curl 로 확인. [수정] _add_safemap_wms 를 QGIS WMS provider 의존에서 직접 HTTP fetch 방식으로 전환. (1) target_bbox 기반으로 GetMap URL 직접 빌드 — service=wms / request=getmap / version=1.1.1 / srs=EPSG:4326 / bbox=west,south,east,north / 이미지 비율 보존(긴 쪽 1024px). (2) 모든 파라미터 키 소문자(safemap 요구사항), serviceKey 만 mixed case 보존. (3) urllib.request.urlopen 으로 PNG 바이너리 받아 사용자 임시폴더(qgis_AI_safemap)에 저장 + WorldFile(.pgw) 동시 생성 — pixel size + upper-left coord 로 EPSG:4326 georeferencing. (4) QgsRasterLayer 로 PNG 읽어 setCrs(EPSG:4326) 후 프로젝트 추가 → 캔버스 줌. 응답 content-type 검증(image/* 아니면 친절한 에러 메시지). PLUGIN_VERSION 3.8.58 → 3.8.59.
3.8.58
  • 생활안전 카테고리 신규 추가 (경찰청 safemap.go.kr WMS). (1) skills_config.json: default_lifesafety 그룹(order=4) + 2개 스킬 — "범죄주의구간(전체)"(IF_0087, A2SM_CRMNLHSPOT_TOT/A2SM_CrmnlHspot_Tot_Tot 스타일) / "범죄예방환경설계(CPTED)"(IF_0023, A2SM_CPTED_G, styles 없음). (2) skill_manager: 신규 _fetch_safemap_key_from_nas — V-World 와 동일한 패턴(NAS shared TelemetryClient → QgsSettings refresh_token 폴백) 으로 GET /api/v1/safemap/issue-key 호출, JWT 인증된 사용자에게 중앙 관리 safemap 키 발급 → Google 로그인만 하면 별도 키 발급 불필요. (3) 신규 _add_safemap_wms 공통 로더 — QGIS WMS provider URI 빌드(crs=EPSG:4326, IgnoreAxisOrientation=1, transparent=TRUE), target_bbox 받아 캔버스를 대상지 영역으로 setExtent → WMS 가 가시영역만 렌더되어 "대상지 지정한 만큼만" 호출 효과. (4) 신규 add_safemap_crime_hotspot / add_safemap_cpted — 두 스킬 함수, HARDCODED_SKILLS 레지스트리에 등록. (5) NAS server.py: _SAFEMAP_KEY env var(QGIS_NAS_SAFEMAP_KEY, 신청키 기본값 포함) + GET /api/v1/safemap/issue-key 엔드포인트 (rate_limit_default 의존성으로 JWT 검증). PLUGIN_VERSION 3.8.57 → 3.8.58.
3.8.57
  • 부분성공 popup false positive 핫픽스. 사용자 보고: V-World 가 jiga 직접 반환해 개별공시지가가 정상 동작했음에도("V-World 직접 응답 1건. 속성 'jiga' 확인. NSDI 활용신청 불필요.") "부분 성공" popup 이 잘못 떴음 — 메시지에 "NSDI"라는 단어가 들어있어 v3.8.56 heuristic 의 키워드 매칭이 false positive. _execute_and_show_skill: 부분 성공 판정을 키워드 매칭("매칭 0건"/"NSDI"/"fallback") 에서 ⚠️ 프리픽스 단일 마커로 좁힘. landinfo 함수의 진짜 0건 메시지("⚠️ 속성 미적용") 만 popup 트리거. V-World 가 실제로 jiga 필드를 제공한다는 v3.8.49 의 주장은 사실로 확인됨 — 개별공시지가는 NAS/NSDI 우회 없이 V-World 만으로 동작. 토지소유는 V-World 미제공이라 NSDI 필요(401 NAS 인증 실패는 사용자가 QGIS 메뉴에서 로그인 시 해결). PLUGIN_VERSION 3.8.56 → 3.8.57.
3.8.56
  • 스킬 결과/오류 popup 강제 노출 + 토지정보 진단 명확화. 사용자 피드백: "반응이 없는데(지형분석 등고선+표고점 버튼). 토지정보는 여전히 빈 레이어." 원인: hardcoded 스킬 결과가 _on_log 로만 흘러가고 사용자가 로그 탭을 안 보면 무반응으로 인식. (1) _execute_and_show_skill: 실행 종료 시점에 결과 분기로 QMessageBox 강제 노출. exception → critical popup, success=False → warning popup, success=True 인데 메시지에 "매칭 0건"/"활용신청"/"NSDI"/"fallback" 신호 포함 시 information popup. 사용자가 로그 탭 안 봐도 즉시 이슈 인지. (2) load_landinfo_godata / load_landinfo_owner_godata 의 NSDI 0건 메시지를 다중 라인으로 재구성: "⚠️ 속성 미적용" 프리픽스 + V-World 응답 필드명 노출(공시지가 jiga 부재 입증) + 필요 조건 3종(QGIS 로그인 / 활용신청 / NAS 환경변수) 명확 안내 + "위 3가지 미충족이면 도형만 표시가 정상 동작" 안내. 사용자가 v3.8.55 까지 "왜 빈 레이어인가" 의문을 가졌던 부분 — popup 으로 즉시 진단 표시. PLUGIN_VERSION 3.8.55 → 3.8.56.
3.8.55
  • 등고선+표고점 SHP 우회 경로로 DEM 생성 + 지형분석 자동 연계. 사용자 결정: "DEM 못 불러올 수도 있으니 표고점+등고선으로 DEM 만들게 해서 우회하자". NGII 5m DEM(data.go.kr #15059920)은 활용신청 필요라 진입 장벽 있는 반면, NGII 연속수치지형도 SHP의 등고선·표고점 레이어는 모두 CC BY 라이선스로 활용신청 없이 즉시 받을 수 있다(NGII #15059721 / V-World 데이터마켓 dsId=30185). [구현] (1) 신규 헬퍼 _find_z_field: 레이어에서 표고값 필드 자동 탐지 — ELEV/CONTOUR/HEIGHT/Z/표고/고도 우선순위 매칭, 실패 시 첫 숫자형 필드 폴백. (2) 신규 헬퍼 _find_layers_by_geom_and_keyword: 프로젝트에서 지오메트리 타입 + 이름 키워드 매칭 벡터 레이어 자동 탐지. (3) 신규 함수 load_terrain_from_contour_spots: 등고선 line 레이어(필수, 자동 탐지: '등고선'/'contour') + 표고점 point 레이어(선택, '표고점'/'spot'/'elev_point') 입력. qgis:tininterpolation Linear 보간으로 5m(기본, kwargs.pixel_size_m로 조절) GeoTIFF DEM 생성 → "지형분석" 그룹에 등록 → 그 DEM 을 target_raster_layer 로 load_terrain_analysis 자동 호출 → 표고·경사도·등고선(재생성) 시각화 일괄 추가. 등고선만으로도 동작(표고점 누락 시 정밀도 약간 하락이지만 기능). (4) skills_config.json: 기존 "지형분석 (표고+경사도+등고선)" → "지형분석 (DEM 래스터)" 명칭 명확화 + 신규 "지형분석 (등고선+표고점에서 생성)" 버튼 추가. 두 버튼 병존 — 사용자가 DEM 받기 부담스러우면 후자, DEM 이미 있으면 전자. PLUGIN_VERSION 3.8.54 → 3.8.55.
3.8.54
  • 지형분석 버튼 라스터 가드 UX. 사용자 피드백: "대상지 레이어가 GeoTIFF가 아니면 경사도/표고/고도 버튼은 회색으로, 마우스 올리면 툴팁으로 조건 알려줘". (1) 신규 _raster_required_btns 레지스트리: skill 정의의 params.requires_raster_target=true 인 버튼들을 추적. _refresh_skills_ui 매 호출마다 초기화. (2) 신규 _update_raster_required_buttons: 대상지 콤보가 QgsRasterLayer 일 때만 등록된 버튼 활성, 아니면 setEnabled(False) + 안내 툴팁(DEM 받는 법: NGII 5m / AWS SRTM 30m 옵션 명시). (3) target_layer_combo.currentIndexChanged 시그널 연결 — 콤보 변경 시마다 가드 자동 재평가. _populate_target_layer_combo 갱신 직후에도 즉시 호출. (4) 버튼 setStyleSheet 에 QPushButton:disabled 회색조 스타일 추가(rgba 12% 배경, 55% 텍스트). 지형분석 버튼이 벡터 대상지에서는 명확히 비활성됨이 시각적으로 드러남. PLUGIN_VERSION 3.8.53 → 3.8.54.
3.8.53
  • 지형분석(표고+경사도+등고선) 통합 + 사용자 DEM 기반 전환. 사용자 결정: V-World 3D Open API 가 2019 폐쇄됐고 SRTM Terrarium 외부 망 의존이 사용자 환경에서 자주 실패하는 것을 확인 → 모든 외부 다운로드 로직 제거하고 사용자가 직접 받은 DEM GeoTIFF(예: NGII 5m DEM, SRTM, ALOS 등)를 입력으로 받도록 전환. (1) 신규 헬퍼 _resolve_target_dem_raster: kwargs 의 target_raster_layer/target_raster_id/clip_layer/target_layer 중 QgsRasterLayer 인스턴스를 우선순위로 추출, 디스크 GeoTIFF 만 허용(memory 레이어 거부). DEM 누락 시 즉시 친절한 에러 메시지("QGIS 에 DEM GeoTIFF 추가 후 대상지 콤보에서 선택"). (2) load_dem_elevation: 이전 _dem_decode_to_float32 + V-World XDWorld fallback 로직 전체 제거. 사용자 DEM 소스 경로를 그대로 새 QgsRasterLayer 로 재로드하고 의사색채 ramp(0m 청록 → 2000m+ 흰) 적용 → "지형분석" 그룹에 추가. (3) load_dem_slope: 이전 V-World/SRTM XYZ 타일 로드 + canvas extent 절단 + gdal:translate 중간 단계 모두 제거. 사용자 DEM 소스 경로를 gdal:slope INPUT 으로 직접 전달 → 결과 GeoTIFF 에 녹색→빨강 ramp 적용. (4) load_dem_contour: _dem_decode_to_float32 의존 제거. 사용자 DEM 을 gdal:contour INPUT 으로 직접 전달, interval 기본 10m(kwargs.interval_m 로 5/10/20m 자유 조절). 결과 SHP 갈색 폴리라인. (5) 신규 load_terrain_analysis: 위 3 함수를 한 번에 호출해 표고·경사도·등고선 3 레이어를 "지형분석" 그룹에 일괄 등록. 외부 망 호출 0, V-World 키 불필요, 라이선스 이슈 0. 사용자가 1회 DEM 받으면 영구 사용. (6) skills_config.json: 기존 dem_elevation/dem_slope/dem_contour 3 버튼 → "지형분석 (표고+경사도+등고선)" 단일 버튼. params 에 requires_raster_target:true 추가. (7) chat_dock_skill_mixin._populate_target_layer_combo: 기존 벡터 전용 → 래스터도 포함, 래스터는 "🏔 이름 (DEM)" 표기로 시각 구분. 기존 벡터 스킬은 raster extent 도 유효한 bbox 라 부작용 없음. (8) load_dem_both alias 는 보존하되 load_terrain_analysis 로 라우팅(AI 채팅 호환). PLUGIN_VERSION 3.8.52 → 3.8.53.
3.8.52
  • 건축물 노후도/층수 6등급 회색 누출 핀포인트 픽스. 사용자 보고: "노후도만 불러왔는데 회색 건물들이 정상 색상 위에 겹치는데 왜이러지". 원인 진단: V-World LT_C_BLDGINFO 가 같은 건물의 historical 버전(증축/리모델링)을 다른 bld_nm 또는 NULL pnu/bld_nm 으로 누적 보유 → 기존 _dedup_bldginfo_features 의 (pnu, bld_nm) → (pnu, centroid_xy) → (centroid_xy) 키 구조가 다른 그룹으로 인식해 둘 다 살려둠 → useapr_day 정상 버전은 색상 카테고리, NULL 버전은 미상(회색)으로 빠져 같은 자리에 겹쳐 그려짐. _load_bldginfo_filtered 가 6등급 모두 캐시된 동일 풀에서 분기하므로 같은 deduped 피처가 여러 카테고리에 들어갈 수는 없음 → 회색 누출은 dedup 사각지대가 유일 원인. [수정] 신규 헬퍼 _spatial_filter_overlap_unknown_bldginfo: QgsSpatialIndex 로 useapr_day NULL 피처마다 후보 검색, 교차면적/NULL피처면적 ≥ 0.5 인 정상 useapr_day 피처가 있으면 NULL 쪽 제거. _load_bldginfo_filtered 의 all_feats 빌드 직후, 캐시 저장 전에 호출 → 6 카테고리 모두 깨끗한 풀에서 분기. 정상 데이터는 절대 건드리지 않음(useapr_day 유효 피처는 보존). PLUGIN_VERSION 3.8.51 → 3.8.52.
3.8.51
  • 강남구 행정동 로드 시 인접 구 누출 잔여 버그 핫픽스. 사용자 재보고: "강남구 행정동만 누르려도 하니까 주변 시군구의 행정동까지 다 나오네". v3.8.50 의 _filter_layer_by_code 강건화는 lt_c_cademd 응답에 대해서는 무력했음 — 통계청 ADM_CD(8자리, 강남구=11230xxx)만 가지고 있어 법정 SIG_CD(5자리=11680) prefix 매칭 0건, ADM_NM("역삼1동" 단일 토큰)에는 시군구명이 없어 이름 매칭도 0건 → setSubsetString("") 폴백 → BBOX 내 인접 구(서초/송파/광진/성동) 행정동 전부 노출. [수정] 신규 헬퍼 _clip_layer_to_sig_polygon: 시군구 폴리곤(_fetch_sig_polygon 캐시 재사용) 가져와 레이어 피처 중심점 PIP 검증, 비포함 피처를 startEditing/deleteFeatures/commitChanges 로 직접 삭제. setSubsetString 우회 — OGR SQL 은 spatial 함수 미지원이라 피처 삭제가 유일한 path. centroid 실패 시 BBOX 중심점 폴백, 빈 geometry 도 삭제 대상. load_base_hjdong/load_base_bjdong/load_base_sigungu 세 곳 모두에서 admin_code 가 5자리(시군구 단위)일 때 _filter_layer_by_code 직후 _clip_layer_to_sig_polygon 호출. 법정동/시군구는 기존 코드 매칭이 동작하지만 엣지 케이스 안전망. PLUGIN_VERSION 3.8.50 → 3.8.51.
3.8.50
  • 시군구 경계·행정동 인접 누출 수정. 사용자 보고: "경계 데이터에서 강남구 불러왔는데 주변 시군구까지 다 불러와지고, 행정동 드롭다운도 주변 시군구의 행정동 모두 불러와졌어." 원인 진단: V-World WFS BBOX 쿼리는 축 정렬 직사각형이라 강남구 BBOX 와 envelope 이 교차하는 인접 구(서초/송파/광진) 피처가 모두 반환됨. 후처리 필터에 두 가지 결함이 있어 인접 구가 새는 중. (1) `_filter_layer_by_code` 의 코드 매칭이 정수형 SIG_CD 필드에서 실패 → `setSubsetString("")` 폴백으로 BBOX 내 전체 노출. CAST 폴백 추가: `CAST("SIG_CD" AS character(20)) LIKE '11680%'` 로 정수/문자열 양쪽 모두 매칭. (2) 시군구·시도 이름 폴백이 `LIKE '%강남구%'` 로 부분문자열 매칭이라 "중구" ⊂ "중랑구" 같은 이름 충돌 위험. `sig_kor_nm`/`ctp_kor_nm`/`sido_kor_nm`/`ctprvn_nm` 단일 토큰 필드는 `=` 정확 일치로만 매칭, `full_nm`/`adm_nm` 풀경로 필드만 LIKE 유지. (3) `_fetch_emd_list` 의 BBOX 중심점 검증 폴백이 부족 — 강남구 BBOX 직사각형 내부에 들어오는 인접 구 일부 행정동이 그대로 통과. 신규 헬퍼 `_fetch_sig_polygon` 추가 — `lt_c_adsigg` 에서 시군구 폴리곤 1건을 가져와 `QgsGeometry` 로 빌드(여러 조각이면 union), SIG_CD 정확 일치 우선·이름 정확 일치 폴백, FIFO 50건 캐시. `_fetch_emd_list` 의 BBOX 중심점 게이트를 실제 폴리곤 `QgsGeometry.contains()` PIP 검증으로 교체. sig_geom 미확보 시 기존 BBOX 폴백 보존. PLUGIN_VERSION 3.8.49 → 3.8.50.
3.8.49
  • 도로 정리 + 토지정보 V-World 직접 + 건물 dedup 완화. (1) 사용자: "도로명주소 도로(중심선) 삭제하고 도로(실폭)을 그 위치로. 유추 기능은 삭제." → skills_config.json 베이스맵에서 road_sprd 항목 제거 + road_inferred 위치를 첫번째로 이동 + 이름 "도로 (실폭)" + description 단순화. load_road_inferred 함수 본문 200줄 → 5줄 _load_single_vworld 호출로 축소(다이얼로그/QSpatialIndex/가변 buffer/native:difference/unaryUnion 모두 제거). LT_C_UPISUQ151 도시계획 결정 도로 폴리곤만. (2) 사용자: "토지세부정보 카테고리 아무것도 안되네 이유좀 찾아봐" → 웹검색 결과 V-World lp_pa_cbnd_bubun 응답에 jiga(공시지가) + gosi_year/gosi_month 필드가 이미 포함됨이 확인됨(PublicDataReader 문서 + 정우일 블로그). NSDI #15058747 NAS 프록시 호출 의존성 제거. load_landinfo_godata: V-World 응답 필드명 검사하여 jiga/land_price/pblntfPclnd 중 하나가 있으면 NSDI 우회하고 V-World 결과를 그대로 토지정보 그룹에 등록. 활용신청 미승인이어도 공시지가 표시 가능. V-World 에 없으면 NSDI fallback 유지. (3) PNU 필드 못 찾을 때 진단 메시지에 V-World 응답 필드명 목록 포함 — 사용자가 무엇이 문제인지 즉시 파악. load_landinfo_owner_godata 도 동일 패턴. (4) 사용자: "건물 겹치는 부분 해결좀" → _dedup_bldginfo_features centroid 반올림 정밀도 6자리(약 11cm) → 4자리(약 11m)로 완화. 같은 건물의 시점별 도형 차이가 11m 이내면 같은 그룹으로 묶여 useapr_day 최신본만 채택. PLUGIN_VERSION 3.8.48 → 3.8.49.
3.8.48
  • 6건 종합 핫픽스 + 새 기능. (B 건축물 사이드바 단일화) v3.8.45 에서 분리한 "건축물 노후도"·"건축물 층수" 두 그룹을 단일 "건축물 세부정보" 그룹 + 일괄 2버튼('노후도별 6등급'/'층수별 6등급')으로 통합. LayerTree 자동 분기는 v3.8.45 코드 그대로 유지(layer_name prefix 로 노후도/층수 두 그룹 자동 분리). (C 토지정보 cap 상향 + 진단 강화) load_landinfo_godata + load_landinfo_owner_godata 의 pnu_cap 200 → 1000. _format_landinfo_diagnostic 메시지에 "총 N건 호출 / 매칭 M건" 헤더 추가 + 200 OK 응답이 매칭의 2배 초과 시 "활용신청 데이터 범위 외" 안내. (D 표고/경사도 진단 + fallback) 신규 헬퍼 _dem_decode_to_float32 추출 — load_dem_elevation 과 load_dem_contour 가 공유. SRTM Terrarium 도달 / gdal:translate / gdal:rastercalculator 각 stage 별 결과 검증 + 빈 파일·예외 시 명확 메시지("zoom 5+ 권장" 등). rastercalculator 실패 시 V-World XDWorld PNG fallback URI 반환 — 호출자가 raw RGB 라도 표시 가능. (E "표고+경사도" 사이드바 제거) skills_config.json 에서 dem_both 항목 삭제. load_dem_both 함수 자체는 HARDCODED_SKILLS 보존(AI 채팅 호출용). (A 실폭도로 유추 — load_road_inferred 신규) 도시계획시설 도로(LT_C_UPISUQ151) 폴리곤 + 도로명주소 중심선(lt_l_sprd) 가변 buffer 합성. 사용자 클릭 시 QMessageBox.question 다이얼로그로 "확장 여부" 묻기 — 아니오 = 도시계획만, 예 = 중심선 buffer 합성. 가변 buffer 알고리즘: native:difference 로 누락 segment 추출 → QgsSpatialIndex 로 인접 도시계획 폴리곤 검색 → 폴리곤 면적/장변 비율로 폭 추정(cap 30m) → segment 시작·끝점 평균폭의 절반 buffer → QgsGeometry.unaryUnion 합성. 인접 폴리곤 못 찾으면 default 8m. 결과 layer 는 "source" 속성으로 도시계획/buffer추정 분리 색조 표시(#888888/#BBBBBB). (F 등고선 — load_dem_contour 신규) V-World 등고선 API 미제공 확인 → SRTM Terrarium DEM → gdal:contour 10m 간격(kwargs.interval_m 로 5/10/20m 조절 가능). 갈색(#8B4513) 라인. 평지에선 라인 적게 나오는 게 정상. PLUGIN_VERSION 3.8.47 → 3.8.48.
3.8.47
  • GIS건물통합정보 같은 건물 중복 폴리곤 제거. 사용자 보고: "베이스맵에서 GIS건물통합정보 불러오니까 겹친 게 많은데 또 모양이 조금씩 다름. 이런 게 엄청 많네." 원인 진단 결과 V-World/국토부 LT_C_BLDGINFO DB 가 같은 건물의 증축/재건축/리모델링 이전 도형까지 누적 보유 → V-World 가 그대로 반환 → 같은 자리에 폴리곤 2~5장 겹침. BBOX 분할 dedup(JSON hash) 로는 잡히지 않음 (properties 가 미세하게 달라 hash 불일치). [구현] 신규 _dedup_bldginfo_features 헬퍼 — 묶음 키 우선순위 (1) (pnu, bld_nm), (2) (pnu, centroid_xy), (3) (centroid_xy) — 같은 키 안에서 useapr_day(사용승인일자) 최신본 채택. centroid 는 GeoJSON coordinates 평탄화 후 좌표 평균(소수 6자리 반올림). _vworld_data_api_load 의 BBOX hash dedup 직후 data_code == 'LT_C_BLDGINFO' 면 자동 호출 — 한 곳 수정으로 모든 호출 경로(load_building_spbd / 노후도 6종 / 층수 6종) 커버. 진단 로그(QGIS AI 로그 채널) 에 "BLDGINFO 의미적 dedup: N → M (X건 제거)" 출력. PLUGIN_VERSION 3.8.46 → 3.8.47.
3.8.46
  • 실폭도로 폴리곤 후보 5종 자동 시도 + 정직한 진단 메시지. 사용자 재요청 "왜 자꾸 도로중심선만 불러오냐, 실폭도로 못하냐". V-World 명명규칙(L=Line, C=Polygon)에 따라 폴리곤 typename 후보 5종(lt_c_sprd / lt_c_aisroad / lt_c_jusoroad / lt_c_road / lt_c_road_link)을 차례로 호출 — 0건 아닌 첫 typename 발견 시 즉시 폴리곤 사용. 5종 모두 0건이면 lt_l_sprd 라인 fallback + 진단 메시지: "실폭도로(폴리곤)는 V-World API 미제공 (시도 5종 모두 0건). juso.go.kr 도로명주소 전자지도 11종 SHP 또는 data.go.kr #15050413 신청 후 SHP 다운로드하여 QGIS 에 직접 추가하세요." 정직성 확보 — 사용자가 V-World 한계를 정확히 알고 우회 경로(SHP 신청)를 따를 수 있게 안내. 향후 NAS 측에 시도 단위 SHP 캐싱 + plugin 에서 stream 으로 받는 통합 경로는 별도 트랙으로 검토. PLUGIN_VERSION 3.8.45 → 3.8.46.
3.8.45
  • 건축물 노후도/층수 그룹 분리 + 토지정보 진단 강화. (1) [건축물 그룹 분리] 사용자 피드백 "건축물 세부정보도 따로따로 해라, 층수랑 노후도 말이야". skills_config.json: 단일 그룹 "건축물 세부정보"(2버튼) → "건축물 노후도"(7버튼: 6등급 일괄 + 30년+/20-29/10-19/5-9/0-4/미상 6개 개별) + "건축물 층수"(7버튼: 6등급 일괄 + 저/중/고/준초고/초고/미상 6개 개별) 두 그룹으로 분리. _load_bldginfo_filtered 의 하드코딩된 그룹명 "건축물 세부정보" 를 layer_name prefix 분기로 변경 — "노후"/"신축" 포함은 "건축물 노후도", "저층"/"중층"/"고층"/"준초고"/"초고"/"층수" 포함은 "건축물 층수" 그룹으로 자동 분리 등록. 결과 LayerTree 에서도 그룹 노드 둘로 보임. (2) [토지정보 진단 강화] 사용자 피드백 "토지세부정보는 전부 다 안되네". 원인은 보통 NAS 미연결/data.go.kr 활용신청 미승인이지만 기존 메시지가 "0건" 만 출력해 사용자가 어디 막혔는지 모름. _fetch_nsdi_attrs_for_pnus 가 (attrs, diag) 튜플 반환 — diag 에 status_counts 포함. _format_landinfo_diagnostic 헬퍼 추가: 401/403/404/429/500/504/200(empty)/none 별 사용자 친화 설명. load_landinfo_godata + load_landinfo_owner_godata: NSDI 매칭 0건이어도 base 필지 도형은 그대로 등록(사용자가 위치는 보고 작업 가능) + 진단 메시지로 어디서 막혔는지 정확히 알림 — 점검 항목 3종(QGIS 로그인/활용신청 승인/NAS 환경변수) 안내. PLUGIN_VERSION 3.8.44 → 3.8.45.
3.8.44
  • 자동로드 비활성 + 도로 라벨 정정 + 폴리곤 폴백. (1) [자동로드 OFF] 사용자 피드백: "대상지 선택될 때마다 베이스맵 자동 호출되면 중복이거나 행정동 단위면 난리. 베이스맵은 버튼 누를 때만 호출하는 게 낫다." chat_dock_skill_mixin._AUTOLOAD_BASEMAP_IDS = set() 으로 비움 → 자동로드 함수가 호출되어도 4종 모두 skip, 무동작. 사이드바 버튼 클릭 시에만 호출. (2) [도로 라벨 정정] V-World 명명규칙 L=Line, C=Polygon 확정(lt_c_landinfobasemap 등 실재 코드로 검증). lt_l_sprd 는 라인(도로중심선). 기존 라벨 "도로명주소 도로(실폭도로)" 가 잘못된 표기였음 — 사용자가 폴리곤(실폭) 기대했는데 라인이 들어와 혼동. 라벨을 "도로명주소 도로 (중심선)" 으로 정정. skill_manager.py 카탈로그 + skills_config.json 동시 수정. (3) [폴리곤 폴백 시도] load_road_sprd 에 lt_c_sprd(가설 폴리곤 typename) 1차 시도 → 0건이면 lt_l_sprd 라인으로 자동 fallback. V-World 카탈로그가 lt_c_sprd 를 실제 노출하면 폴리곤 자동 표시, 미노출이면 라인 안전 fallback. 행안부 도로명주소 11종(juso.go.kr) 에 "실폭도로(폴리곤)" 가 별도 데이터셋으로 존재함이 확정 — V-World API 가 미제공이면 juso.go.kr 신청 다운로드가 정답. PLUGIN_VERSION 3.8.43 → 3.8.44.
3.8.43
  • 베이스맵·DEM·자동로드 4종 핫픽스. (1) [도로/하천 미작동 근본해결] V-World 공식문서 "WMS/WFS typename 은 소문자만 허용" 확인. `_vworld_wfs_load` 가 호출자에게서 받은 typename(예: "LT_L_SPRD")을 그대로 URL 에 박아 V-World 가 silent 0건으로 거부 → 도로(LT_L_SPRD)·하천(LT_C_WKMSTRM)·지적도(LP_PA_CBND_BUBUN) 모두 안 떠지던 원인. 진입 즉시 `typename = typename.lower()` 강제 변환 1줄 추가 — 즉시 살아남. (2) [강남급 건물 듬성듬성 해결] `_vworld_data_api_load` 의 `max_pages = 10` 고정으로 1만 건 cap → 강남구(약 5~7만 동) 80~90% 누락. BBOX 4×4 재귀 분할(최대 깊이 3 → 64×64=4096 셀) + 셀당 max_pages=1000 + JSON hash dedup 으로 교체. 셀 totalFeatures>=10000 + bbox span>0.001 일 때 추가 분할. 결과: 고밀도 지역도 완전 로드. (3) [자동로드 가드 강화] chat_dock_skill_mixin._on_target_changed_autoload_basemap: boundary_keywords 확장(BND_/CTPRVN/SIG_/EMD_/LI_/boundary 추가) + 정규식 boundary_suffix_re 추가하여 "강남구"·"양천구"·"서울시"·"OO동" 같은 행정구역 단독 이름도 boundary 로 인식 → 자동로드 skip. existing_names 단순 비교를 feature count 기반으로 교체 — 같은 이름 layer 가 있어도 비어 있으면(이전 호출 실패) 제거 후 재시도, 한 번 실패한 도로/하천이 영구 skip 되던 cascade 버그 해결. (4) [DEM 표고 보라 단일색 해결] load_dem_elevation 이 SRTM Terrarium PNG 를 raw RGB 로 그대로 캔버스에 그려 무지개 노이즈 + 봉우리 saturate 보라색이 되던 문제. 신규 흐름: gdal:translate 로 캔버스 영역 RGB GeoTIFF 추출 → gdal:rastercalculator 로 Terrarium 디코딩(R*256 + G + B/256 - 32768 = m, Float32) → QgsSingleBandPseudoColorRenderer 7단계 컬러램프(0m 청록 → 50m 연녹 → 200m 살구 → 500m 갈 → 1000m 진갈 → 1500m 회 → 2000m+ 흰) 적용. load_dem_slope 와 동일한 출력 디렉터리(<project>/생성된 RASTER/표고_<날짜>/)에 .tif 저장. PLUGIN_VERSION 3.8.41 → 3.8.43.
3.8.28
  • 토지정보 그룹 실작동 복구(공시지가/토지소유). v3.8.26~3.8.27 에서 토지정보 두 항목은 라벨에 "(V-World 미제공)" 이라고 자기 고백한 상태로 V-World typename 5종 폴백을 시도했으나, 카탈로그 자체에 두 데이터가 없어 5/5 모두 빈 결과 → 사용자가 클릭해도 항상 "0건". 원인 진단 결과 V-World API 의 lp_pa_cbnd_* 는 도형만 제공하고 소유속성·공시지가는 부재. 실제 데이터 출처는 (1) 개별공시지가: data.go.kr NSDI #15058747 IndvdLandPriceService, (2) 토지소유현황: data.go.kr NSDI #15045905 LandOwnerInfoService — 둘 다 PNU 단위 XML API. [구현] skill_manager.py 신규 함수 `load_landinfo_godata` / `load_landinfo_owner_godata` + 헬퍼 5종(`_default_landinfo_year`, `_find_pnu_field`, `_nas_proxy_get`, `_parse_nsdi_item`, `_fetch_nsdi_attrs_for_pnus`, `_enrich_pnu_geojson`, `_collect_pnus_from_layer`). 동작: viewport bbox → V-World lp_pa_cbnd_bubun 에서 필지+PNU 200건 cap 수집 → NAS 프록시 `/api/v1/proxy/data_go_kr/1611000/nsdi/...` 로 4-worker 병렬 호출 → 응답 XML 의 첫 item 에서 attr 추출 → 베이스 GeoJSON properties 에 부착 후 새 레이어로 등록. NAS 가 serviceKey 자동 주입(QGIS_NAS_DATAGOKR_KEY 환경변수). 토지소유 응답의 posesnSeCode 5코드를 한국어 라벨로 매핑(3301=개인 / 3302=국유지 / 3303=외국인·외국공공기관 / 3304=시·도유지 / 3305=군유지) — 성명·주민번호 등 개인정보는 미제공이지만 도시계획용 인격구분 통계로는 충분. [카탈로그] skills_config.json 토지정보 두 항목의 라벨에서 "(V-World 미제공)" 제거, function 포인터를 새 godata 함수로 교체, description 갱신. 기존 V-World 폴백 함수(`load_landinfo_vbum`/`load_landinfo_owner`)는 HARDCODED_SKILLS 에 호환용으로 보존(AI 채팅 폴백 호환성). [기준연도] 기본값은 작년(당해년도 데이터는 통상 미공개), kwargs.year 로 override 가능. [선결조건] NAS 호스트에 `QGIS_NAS_DATAGOKR_KEY` 설정 + data.go.kr 에서 #15058747 / #15045905 활용신청 승인 필요. 둘 중 하나 누락 시 친절한 에러 메시지로 안내.
3.8.27
  • V-World 공식 카탈로그(189개) 기반 typename 정정 + 일괄 호출 전면 폐지 + 종류별 단일 호출 51버튼 + 건축물 분석 5버튼. (1) typename 정정: 연속지적도 lp_pa_cbnd_bonbun→LP_PA_CBND_BUBUN, 도시자연공원구역 lt_c_uq141→LT_C_UQ162, 사업지구경계도 lt_c_uq162→LT_C_LHZONE, 토지이용계획도 추정→LT_C_LHBLPN, 시장정비구역 lt_c_uq170→LT_C_UB901, 교육환경보호 lt_c_uo311→LT_C_UO101, 대기환경규제 lt_c_uo321→LT_C_UM301, 지구단위계획 lt_c_uq131→LT_C_UPISUQ161. (2) 용도지역 4종을 LT_C_UQ111 단일 typename에서 속성 분리하던 것을 종류별 별도 typename(UQ111~UQ114) 호출로 변경. 카탈로그 확인 결과 도시지역 UQ111, 관리 UQ112, 농림 UQ113, 자연환경보전 UQ114는 각자 별도 typename. (3) 용도지구 9종(미관 폐지) 각자 typename(UQ121, 123-130) 별도 버튼. (4) 도시계획시설 9종(UPISUQ151~159) 각자 typename 별도 버튼 — 기존 lt_c_upisuq151 단일 분리는 잘못. (5) 일괄 호출 함수 전면 폐지(load_urban_basics_all/load_zone_usedistrict_split/load_facility_split/load_protect_all/load_project_district_all/load_districtplan_split): 한 번 클릭에 9~10번 V-World API 호출이 발생해 일일 한도 빠르게 소모 + rate-limit 위험. 한 클릭 = 한 호출 원칙. (6) 신규 _load_single_vworld 헬퍼: 단일 typename → 단일 레이어를 도시기본계획 그룹에 즉시 배치. (7) 신규 건축물 분석(LT_C_BLDGINFO 1번 호출 + 속성 필터): 노후건축물(useapr_day < 1985, 40년+), 저층/중층/고층/초고층 4종 분리. 데이터 출처 국토교통부, 갱신 2026-03-12, 전국 지원, 속성 grnd_flr/ugrnd_flr/height/totalarea/useapr_day/usability/strct_cd/bld_nm/pnu. (8) 사이드바 구조: 도시기본계획(51버튼) + 토지정보(개별공시지가/토지소유 — V-World 미제공이라 다중 typename fallback). 보호·규제구역 10종(시장정비/교육환경/문화재/대기환경/상수원/습지/산림/백두대간/야생동식물/재해위험), 자연공원 3종(국립/군립/도립) 모두 카탈로그 정식 typename으로. (9) 환경·녹지/교통 그룹은 V-World 카탈로그에 토지피복도(LCSV)/생태자연도(KOSC)/도로명도로(lt_l_aisroad)가 없어 동작 불가가 확인되어 사이드바 노출 안 함(함수는 코드 보존). (10) 색상은 LURIS 표준 그대로 + 건축물 분석 색상(노후 #8E0000/저층 #FFE082/중층 #FFB74D/고층 #E57373/초고층 #7B1FA2) 추가.
3.8.26
  • 스킬 사이드바 카테고리 정리 (사용자 요청): 분류 그룹 다수를 단일 '도시기본계획' 그룹으로 통합, '토지정보' 그룹은 별도 유지. (1) 신규 그룹 구조: '도시기본계획'(12버튼: 도시계획기본 일괄/용도지역 4종/용도구역 2종/용도지구 일괄/도시계획시설 일괄/지구단위계획/보호구역 일괄/사업지구 일괄), '토지정보'(2버튼: 개별공시지가/토지소유정보). (2) 기존 default_urban_planning(도시계획 9버튼) + default_urban_basics + default_zone_*/facility/districtplan/protect/project_district 등 분리 그룹은 모두 단일 '도시기본계획'으로 통합 (중복 제거). (3) 환경·녹지(LT_C_LCSV/LT_C_KOSC) 및 교통(lt_l_aisroad/lt_c_a5parkinglot) 그룹은 V-World typename 미확정으로 작동 불가가 확인되어 사이드바에서 제거 (함수는 코드에 보존하여 정확한 식별자 확인 시 즉시 재활성). (4) 신규 토지소유정보 함수(load_landinfo_owner) + _try_typename_candidates 헬퍼 추가 — 개별공시지가/토지소유정보는 다중 V-World typename 후보를 차례대로 시도해 처음 성공한 식별자 사용. (5) 색상 매핑은 v3.8.25의 LURIS 표준 그대로.
3.8.25
  • 스킬 사이드바에 V-World 분류별 그룹 + 종류별 버튼 + 신규 환경/교통/토지정보 그룹 추가. (1) 신규 그룹: 도시계획기본(실폭도로/실폭하천/건물/연속지적도/토지소유현황 일괄), 용도지역(도시지역/관리지역/농림지역/자연환경보전지역 4종 종류별), 용도지구(10종 일괄), 용도구역(도시자연공원구역/개발제한구역 종류별), 도시계획시설(8종 일괄), 지구단위계획, 보호구역, 사업지구, 환경·녹지(토지피복도 LT_C_LCSV/생태자연도 LT_C_KOSC 환경부 전국), 교통(도로명도로/주차장 전국), 토지정보(개별공시지가 LT_C_LANDINFO_VBUM 전국). (2) 색상은 국토부 LURIS 표준(URBAN_COLOR_MAP) — 도시지역 #FFAEB9 분홍, 관리 #FFEABF 살구, 농림 #B0DFA1 연녹, 자연환경보전 #87CDA0 청록 등. (3) _split_layer_by_attribute 헬퍼로 단일 typename 응답을 속성 필터로 N개 메모리 레이어로 분리해 QGIS LayerTree 그룹 노드에 배치. 부분일치 색상 매칭은 키 길이 내림차순으로 정렬해 "시설보호"가 "보호" 키보다 먼저 매치. (4) _vworld_wfs_load/_vworld_data_api_load 에 skip_add_to_project 파라미터 추가. (5) chat_dock_skill_mixin._on_skill_clicked 에 type=='hardcoded' 분기 추가 — 행정동/법정동 버튼처럼 모든 hardcoded 스킬이 AI 채팅을 거치지 않고 _execute_and_show_skill 통해 V-World API 즉시 호출. 대상지 미선택 시 _resolve_default_bbox 폴백.
3.8.24
  • AI 생성 레이어 자동 SHP 디스크 영속화 + 레이어 패널 즉시 동기화. 사용자 보고: "왜 자꾸 생성한 걸 레이어 패널에 바로 안보여주냐, 꼭 한번씩 레이어 패널로 보여달라고 해야만 하네. 임시 레이어가 아닌 실제 파일로 디스크에 안전하게 저장하고 레이어 패널에 보이게 해야지." 근본 원인 3종: (1) `_exec_run_processing` 이 OUTPUT 미지정 시 `"memory:"` 를 기본값으로 사용해 processing 결과가 메모리 레이어로만 생성됨 — 프로젝트 닫으면 사라짐. (2) `_exec_pyqgis` 후처리는 메모리 레이어를 경고만 하고 자동 영속화 안 함 — LLM 이 `QgsVectorFileWriter` 직접 호출하길 기대하지만 종종 빠뜨림. (3) `addMapLayer()` 후 `_sync_layer_tree()` 호출 누락 site 다수 → mapLayers 와 layerTreeRoot 비동기로 패널 갱신 누락. [수정] config.py 에 `get_default_layer_output_dir()` 추가 — 프로젝트 저장 시 `<project>/생성된 SHP/` 또는 `/생성된 RASTER/`, 미저장 시 `<USER_DATA_DIR>/...` fallback. 사용자 결정에 따라 SHP 포맷 + 레이어별 서브폴더 구조 (`<output_dir>/<레이어이름>/<레이어이름>.shp`) 로 한글 파일명 보존. qgis_tools/_core.py 에 중앙 헬퍼 `save_and_register_layer(layer, name, output_dir, group_name, overwrite, iface)` 추가 — `threading.Lock` 으로 동시 호출 직렬화, `QgsVectorFileWriter.writeAsVectorFormatV3` 로 .shp 저장(실패 시 .gpkg fallback), 래스터는 `QgsRasterFileWriter` 로 .tif, 충돌 시 `_2`/`_3` suffix 자동 부여(99 시도 후 timestamp), 메모리 원본 제거 + 디스크 backed 새 레이어 등록 + `_sync_layer_tree(immediate=True)` 호출. Windows reserved char 만 sanitize, 한글/영숫자/_-. 보존, 200자 cap. Shapefile 한계(필드명 10자/DateTime/2GB) 자동 경고. _processing.py: `_exec_run_processing` 의 `OUTPUT="memory:"` 기본값을 자동 .shp 경로로 교체 + 결과 dispatch 확장(QgsVectorLayer / QgsRasterLayer / 디스크 경로 string), 끝에 `_sync_layer_tree(immediate=True)` 명시. `_exec_pyqgis` 후처리의 메모리 경고 블록을 자동 영속화로 업그레이드 — opt-out: `customProperty("ai_intermediate")=True`, 빈 결과(`featureCount()==0`), NoGeometry. 결과 dict 에 `_memory_persisted=[{name, old_id, new_id, path, format, warnings}, ...]` 첨부해 LLM 이 다음 턴에 새 ID로 재참조 가능. _analysis.py: `_exec_cluster_analysis` 의 scatter_layer / suitability current 를 헬퍼로 영속화. skill_manager.py: `load_dem_slope` 의 `tempfile.gettempdir()` 경로를 `<프로젝트>/생성된 RASTER/경사도_<날짜>/` 로 리다이렉트 + 끝에 `_sync_layer_tree(immediate=True)` 호출. PLUGIN_VERSION 3.8.23 → 3.8.24.
3.8.23
  • 브라우저 자동 로그인 false negative 핫픽스 (브이월드 등 다수 사이트). 사용자 보고: ID/PW 정확히 입력했는데 매번 "스냅샷에 로그인 버튼이 여전히 노출됨: ['로그인/회원가입', '로그인'] → 세션 미설정. ID/PW 오류, 봇 감지, CAPTCHA 등 가능" 메시지로 자동 로그인 실패. 결정적 단서: 사용자가 "건너뛰기" 클릭하면 다음 단계가 정상 진행됨 → 실은 로그인 성공한 상태인데 우리 검증이 false negative. 원인: qgis_tools/_browser.py 의 _evaluate_login_success 가 snapshot 신호 단독으로 negative 판정 시 즉시 실패 처리 (line 143-149). 일부 사이트(브이월드 등)는 로그인 후에도 헤더/푸터에 "로그인/회원가입" 텍스트가 남아 logged_out 으로 오판정. URL 전환·쿠키 증가 신호를 무시하던 게 문제. 수정: [1] snapshot=negative 단독 실패 제거. URL 전환 + 쿠키 증가가 모두 positive 면 snapshot 잔재 무시하고 성공 처리. [2] _SNAPSHOT_LOGGED_IN_KEYWORDS 보강 - 환영합니다/안녕하세요/정보수정/비밀번호 변경/회원탈퇴/마이 페이지/내 메뉴/장바구니/주문조회 추가. 키워드 누락이 false negative 의 한 축이었음.
3.8.22
  • 검색 편의를 위한 표시명 단축. name 'QGIS AI Assistant' → 'QAI' 변경 (사용자 요청). QGIS 플러그인 매니저 검색에서 'qai' 키워드로 즉시 노출되도록 tags 에 'qai' 추가. 폴더명(qgis_AI_assistant)/모듈명/import 경로/사용자 데이터(code_references.db, feedback_learning.db, chroma_db/) 는 모두 그대로 유지 — 위험 0. 기존 사용자가 v3.8.22 로 업데이트해도 학습 자산 보존됨. tags 에 기존 키워드(qgis ai/qgis assistant/assistant 등) 도 유지하여 옛 검색 친화성 함께 보장.
3.8.21
  • 캐시 false positive + "실행할 계획이 없습니다" 긴급 핫픽스 (v3.8.19/3.8.20 의 두 결함). [버그 1] 사용자 보고: "도로 데이터 다운로드" 요청에 "용도지역" 스니펫이 캐시 hit 으로 반환, "강남구 실폭도로 호출" 요청에 "DEM 도움말" 코드 반환, "강남구 모든 건물" 요청에 같은 DEM 코드 반환. 유사도 0.57 (Phase B 의 DIRECT_RETURN_THRESHOLD=0.55 임계값 직상). 즉 임베딩/Jaccard 점수가 임계값을 살짝 넘은 경우 의미적으로 완전 다른 작업 코드를 자신 있게 반환하던 false positive. 수정: (a) DIRECT_RETURN_THRESHOLD 0.55 → 0.75 상향 — Jaccard 폴백 환경에서 hit율은 떨어지나 잘못된 코드 반환보다 LLM 재생성이 안전. (b) try_direct_return 에 의미적 mismatch 검증(_semantic_overlap_ok) 추가 — 사용자 메시지와 캐시 task 의 핵심 명사 토큰 자카드 유사도 < 0.10 이면 LLM 폴백. 한국어 명사 + Qgs* 클래스 + 영문 식별자 추출. [버그 2] 사용자 "✅ 승인됨. 실행을 시작합니다..." 클릭 직후 "실행할 계획이 없습니다." 메시지로 실행 차단. 원인: ai_client._try_snippet_shortcut 가 PendingPlan 객체를 만들기만 하고 self.pending_plan = plan 설정을 누락. provider.plan() 우회 흐름이라 base.py 의 자동 설정도 안 탐. 수정: snippet shortcut 흐름에서 명시적으로 self.pending_plan = plan 추가 (Phase C export_router 와 동일 패턴). 두 버그 모두 v3.8.19/v3.8.20 사용자에게 즉시 핫픽스 적용 필요. v3.8.20 의 자동 업데이트 알림 시스템이 v3.8.21 을 자동 안내함.
3.8.20
  • 자동 업데이트 흐름 가시성 개선 (.pyd 락 우회 강제). 사용자 보고: v3.8.18 → v3.8.19 자동 업데이트 시 QGIS 기본 '플러그인 관리 및 설치' 매니저로 시도하다 "플러그인 패키지의 압축해제에 실패했습니다... 파일이 손상되었거나 저장소에서 없어진 듯 합니다" 메시지 반복 발생. 근본 원인은 Windows 에서 Cython 컴파일된 .pyd 파일이 QGIS 프로세스에 메모리 매핑돼 OS 레벨 락 → QGIS 기본 매니저가 덮어쓰기 실패. 우리 plugin_updater.py 모듈은 v3.8.8 부터 외부 helper 프로세스로 .pyd 락을 우회하는 흐름을 제공해왔으나, 사용자가 메뉴 진입점("플러그인 → Gemini QGIS Assistant → 업데이트 확인 및 설치") 을 못 찾고 QGIS 기본 알림을 클릭하는 패턴이 반복됐음. v3.8.20 핫픽스로 자동 노출 강화: [1] gemini_qgis_plugin.py 의 initGui 끝에 `_schedule_startup_update_check()` 추가 → QGIS 시작 후 10초 지연 백그라운드 plugins.xml 폴링 → 새 버전 발견 시 모달 다이얼로그로 [자동 설치/나중에/이 버전 건너뛰기] 선택 노출. [2] plugin_updater.py 에 `check_for_update_async`(데몬 스레드) + `is_version_skipped`/`mark_version_skipped` (TEMP 파일 기반 건너뛴 버전 기록) 추가. [3] metadata.txt 의 about 에 ".pyd 락 우회를 위해 우리 메뉴 사용" 안내 한 줄 추가 → QGIS 기본 plugin manager 의 plugin 상세 다이얼로그에서도 사용자가 보게 됨. [Windows 전용] Linux/Mac 은 .so 락이 Windows 만큼 빡빡하지 않아 QGIS 기본 매니저로도 보통 성공 → 시작 알림은 win32 만. [.pyd 유지 결정 확인] zip-only hiding 보호 모델 (메모리 기록) 준수 위해 Cython 컴파일은 그대로. .py 평문 배포 옵션은 보호 모델 깨지므로 채택 안 함.
3.8.19
  • 대시보드 데이터 기반 캐시·라우팅·텔레메트리 종합 개선 (Phase A~E1). [Phase A — 가이드 연결 복구] `ai_client._get_system_instruction()` 의 두 빌드 경로(SmartRetriever 조기 반환 vs `_build_dynamic_instruction`) 를 통합. 기존엔 base.py plan() 만 풍부한 프롬프트를 받고 프로바이더 follow_up `call_api(system_instruction=None)` 경로(gemini.py:107, claude_provider.py:117, openai_provider.py:116) 는 PyQGIS API DB 주입이 누락됐었음. 이제 모든 경로에서 동일한 프롬프트 품질. `pyqgis_api_db.py` 에 반복 에러 함정 5종 추가: `pitfall_labelplacement_enum` (Qgis.LabelPlacement.AroundPoint enum vs int 0 TypeError 차단), `pitfall_raster_resampling` (setZoomedInResampler 정확한 시그니처, setZoomedInResamplingMethod 같은 허상 메서드 명시), `pitfall_gradient_color_ramp` (QgsGradientStop, ColorRampStop 아님), `pitfall_layer_visibility` (QgsLayerTreeLayer.setItemVisibilityChecked, setVisible 아님), `pitfall_spatial_index` (processing.run('native:createspatialindex'), createSpatialIndex 함수 아님). CURRENT_VERSION 2→3 으로 자동 재색인. SmartRetriever 에 `_search_corrections` + `feedback_learning_db.fetch_recent_corrections_with_messages` 메서드로 correction_pair 를 retry 루프 외 초기 프롬프트 시점에서도 검색하도록 확장. `_detect_correction_pair` 시그니처도 (pair_id, failed_fingerprint, failed_error_type, failed_error_subcategory) 튜플로 확장하여 correction_pair_created 이벤트 페이로드에 실패 쪽 지문을 실어 서버 측 재사용 가능하게 함. [Phase B — 캐시 가중치 강화 + AST 검증] `smart_retriever._recency()` 를 단계 함수로 변경 (7d=1.0, 30d=0.7, 90d=0.4, >90d=0.2) — QGIS 버전 업데이트로 깨진 과거 스니펫 자동 강등. 점수 가중치 조정 (임베딩/Jaccard 0.50→0.40, recency 0.10→0.20). 실패율 페널티 단계화 (success_rate < 0.6 → ×0.3, < 0.8 → ×0.7, feedback 소스 면제). `try_direct_return()` 에 AST 검증 추가 — 캐시 코드를 ast.parse() 로 1차 검증 후 SyntaxError 시 LLM 폴백, "확신 있게 틀린 코드 반환" false positive 차단. [Phase C — export 3-tier 라우팅] 신규 `export_router.py`: 사용자 메시지를 휴리스틱 키워드 매칭으로 분류해 export_map / create_print_layout 결정론 도구를 LLM 코드 생성 없이 직접 호출. 부정 키워드(분석/필터 + 저장)는 폴백, 분류 신뢰도 < 0.70 도 폴백 → false negative 0 보장. ai_client.plan() 흐름에 `_try_export_router` 추가 (snippet_shortcut → export_router → hybrid → LLM). 인자 자동 추출 (1920x1080, 300dpi, A3, "파일명: foo.png" 등). 대시보드의 export 작업 성공률 43% / 120s 타임아웃 / 토큰 52K~256K 폭증 문제 해소 목표. [Phase D — 벡터 검색 폴백 버그 수정] `code_reference_db._init_vector_store()` 의 if/else 구조 버그 수정 — pg_config 설정 후 PG 연결 실패 시 ChromaDB 폴백 시도 없이 곧장 jaccard_fallback 으로 떨어지던 문제 (PyQGISApiDB._init_store 와 비대칭). 단계 폴백 (PG → chroma → jaccard) 으로 통일. 운영 DB 분석에서 캐시 hit 0% / snippet_cache_hit 0건 의 근본 원인. `gemini_chat_dock.py` 에 폴백 가시화 print 추가 (jaccard_fallback 모드 시 콘솔에 "벡터 검색 미가동, hit율 떨어짐" 명시 — 사용자가 즉시 인지 가능). [Phase E 1/2 — 텔레메트리 hit 추적] `experience_retry_loop._find_correction()` 가 출처를 self._last_correction_source 에 기록 ("feedback_db" | "graph"). retry loop 가 correction_used / golden_used 를 추적해 telemetry learning dict 에 `correction_pair_detected` / `golden_pattern_used` boolean 포함. ai_client 일반 보고에도 vector_backend 진단 정보 포함. 이로써 NAS reports 테이블의 해당 boolean 카운터가 비로소 채워지기 시작. [NAS 측 별도 작업] Phase E 2/2 (daily_stats 일별 집계 + aggregate_daily.py cron) + Phase F (promote_golden 분석 + /api/v1/analytics/daily_stats|snippet_efficiency|golden_candidates 통계 API + /api/v1/learning/pull priority_score 가중치) 는 NAS 서버 (proprietary) 에 별도 배포 — 클라이언트 plugin 변경 없음. 라이선스 영향 0 (plugin GPL 유지, NAS proprietary 유지, 사용자 코드 NAS 미수신 보호 모델 유지).
3.8.18
  • exec 후처리 결과물 무결성 가드 3종 — LLM이 생성한 PyQGIS 코드의 결과가 "캔버스엔 보이는데 레이어 패널엔 안 뜨는" 현상을 플러그인 단에서 결정적으로 차단. 프롬프트 룰(addMapLayer 필수)을 모델이 간헐 무시해도 복구됨
  • (1) 레이어 패널 자동 동기화 — `_exec_pyqgis` 종료 시 `_sync_layer_tree()` 를 무조건 호출. mapLayers 에는 있는데 레이어 트리에 없는 레이어 자동 등록 + 유령 트리 노드 제거. 기존엔 새 레이어 감지 시에만 호출했는데 이전 턴 누락분도 함께 구제
  • (2) orphan 캔버스 그래픽 자동 제거 — exec 시작 전 `iface.mapCanvas().scene()` item id 스냅샷 기록 → exec 후 이 턴에 새로 추가된 `QgsRubberBand` / `QgsVertexMarker` (레이어 없는 "그림만") 를 자동 `scene.removeItem()`. pre-snapshot id 기준 diff 라 사용자 기존 측정/하이라이트 도구 그래픽은 보존
  • (3) 메모리 레이어 영속화 경고 — 새 레이어 중 `providerType()=='memory'` 이고 `customProperty('ai_intermediate')` 없으면 `_memory_warning` 반환. 프로젝트 닫힐 때 사라질 최종 산출물을 LLM이 다음 턴에 `QgsVectorFileWriter.writeAsVectorFormatV3` 로 디스크 영속화하도록 유도
  • QMetaType import 누락 보강 핫픽스 — v3.8.17 의 `QVariant → QMetaType.Type` 일괄 치환 중 일부 모듈에서 `QMetaType` import 가 누락돼 qt6 빌드 Cython 컴파일이 실패하던 문제 수정
3.8.17
  • QGIS 공식 `pyqt5_to_pyqt6.py` 변환 스크립트 적용 — 수동 grep 으로 반복 놓쳤던 PyQt6 호환성 잔재를 일괄 탐지/수정해 최종 마무리. 공식 스크립트 dry-run 재검증 0 warnings
  • 자동 수정 — `QVariant.String/Int/Double → QMetaType.Type.QString/Int/Double` (qgis_tools/_analysis 7건, _processing 4건: QGIS 4.0 필드 타입 지정 방식 변경), `QImage.Format_ARGB32 → QImage.Format.Format_ARGB32` (_export.py 2건)
  • 수동 수정 — `QTabBar.RightSide → QTabBar.ButtonPosition.RightSide` (gemini_chat_dock.py 2건: 채팅 탭 "+"버튼 숨김 로직, 이번 릴리스 발단 크래시 원인), `QIODevice.WriteOnly → QIODevice.OpenModeFlag.WriteOnly` (이미지 버퍼 저장)
  • v3.8.11~v3.8.16 의 6연속 수동 핫픽스를 공식 변환 스크립트로 마감. 향후 CI pre-commit 훅에 이 스크립트 통합해 PyQt6 호환성 회귀 자동 방지 예정
3.8.16
  • PyQt6 unscoped `Qt.*` enum 최종 스윕 (9건) — 광역 grep 으로 v3.8.15 까지 놓친 패턴 일괄 확인. AI Assistant 도크 오픈이 `Qt.RightDockWidgetArea` 때문에 도크 생성 단계에서 크래시 가능성 높았던 것 포함
  • 수정: `Qt.RightDockWidgetArea → Qt.DockWidgetArea.RightDockWidgetArea` (도크 추가), `Qt.WindowModal → Qt.WindowModality.WindowModal` (업데이트 진행률), `Qt.QueuedConnection → Qt.ConnectionType.QueuedConnection` (3건, _Qt alias 경유 포함), `Qt.ScrollBarAsNeeded/AlwaysOff → Qt.ScrollBarPolicy.*` (3건), `Qt.ShiftModifier → Qt.KeyboardModifier.ShiftModifier`, `Qt.Popup | Qt.FramelessWindowHint → Qt.WindowType.*`
  • 영향 파일 — gemini_qgis_plugin, gemini_chat_dock, chat_dock_skill_mixin. 다음 릴리스부터 QGIS 공식 `scripts/pyqt5_to_pyqt6/pyqt5_to_pyqt6.py` 변환 스크립트를 CI 에 통합해 수동 grep 반복 종결 예정
3.8.15
  • v3.8.14 후속 PyQt6 잔여 호환성 수정 — QGIS 4.0 에서 AI Assistant 도크 오픈 시 `AttributeError: type object Qt has no attribute SmoothTransformation` 크래시 + 사이트 계정 관리 다이얼로그 `AttributeError: SiteCredentialsDialog has no attribute exec_` 크래시 발생 문제 수정
  • 원인 1 — `Qt.SmoothTransformation` / `Qt.KeepAspectRatio` 는 PyQt6 에서 scoped enum 으로만 접근 가능(`Qt.TransformationMode.*` / `Qt.AspectRatioMode.*`). v3.8.14 감사 grep 이 이 두 상수를 누락. 수정: `Qt.KeepAspectRatio → Qt.AspectRatioMode.KeepAspectRatio`, `Qt.SmoothTransformation → Qt.TransformationMode.SmoothTransformation` (4건)
  • 원인 2 — PyQt6 는 `.exec_()` 메서드를 **완전 제거** 하고 `.exec()` 만 유지. PyQt5 의 `exec_` 는 Python 2 예약어 회피용이었으나 PyQt6 가 Python 3 전용이라 불필요해 드랍. 수정: `dlg.exec_() → dlg.exec()`, `menu.exec_() → menu.exec()` 등 18건 전수 치환
  • 영향 파일 — chat_dock_session_mixin, chat_dock_conv_mixin, chat_dock_skill_mixin, eula_dialog, gemini_qgis_plugin, gemini_chat_dock, sites_credentials_ui, skill_manager. 다음 릴리스에서 QGIS 공식 `scripts/pyqt5_to_pyqt6/pyqt5_to_pyqt6.py` 변환 스크립트를 적용해 남은 파괴적 변경사항 자동 탐지 예정
3.8.14
  • PyQt6 widget-class enum 전수 스코핑 — v3.8.13 에서 `Qt.*` 모듈 수준 enum 은 치환했으나 위젯 클래스 레벨 enum(`QLineEdit.Password`, `QHeaderView.ResizeToContents` 등)을 놓쳐 QGIS 4.0 에서 사이트 계정 관리·AI Assistant 도크 오픈 시 `AttributeError: type object QLineEdit has no attribute Password` 발생하던 문제 수정
  • 50+건 일괄 스코핑: QLineEdit.(Password/Normal) → .EchoMode.*, QHeaderView.(ResizeToContents/Stretch) → .ResizeMode.*, QFrame.HLine → .Shape.HLine, QTableWidget.(SelectRows/SingleSelection/NoEditTriggers) → .SelectionBehavior/.SelectionMode/.EditTrigger.*, QDialog.Accepted → .DialogCode.Accepted, QEvent.(MouseButton*/Drag*/Drop/KeyPress) → .Type.*, QPainter.SmoothPixmapTransform → .RenderHint.*, QImage.Format_ARGB32_Premultiplied → .Format.*, QFont.Bold → .Weight.Bold, QTextEdit.WidgetWidth → .LineWrapMode.*, QTextCursor.End → .MoveOperation.End
  • PyQt5 5.15+ 는 unscoped 접근을 관대하게 허용했으나 PyQt6 는 엄격히 scoped 형태만 받음. QGIS 3.x (PyQt5) 에서는 문제 없으나 QGIS 4.0 (PyQt6) 에서만 노출. 8개 파일(gemini_chat_dock, sites_credentials_ui, chat_dock_skill_mixin, chat_dock_session_mixin, chat_dock_conv_mixin, eula_dialog, skill_manager) 수정
  • V-World 행정동 필터 과다매칭 수정 — "강남구 하나 선택했는데 서초/송파/광진/강동/과천/성남/서대문/성동 구의 행정동까지 드롭다운에 나오는" 문제 해결. 원인: 센서스 행정동(`lt_c_cademd`) 응답이 속성 부족으로 3.8.3 의 "WFS BBOX 공간 필터 신뢰" 경로를 타는데, 축 정렬 BBOX 특성상 인접 시군구 영역까지 포함됐음
  • 수정: `_feat_belongs_to_sig` 반환값을 bool → 상태문자열("exact"/"bbox"/False) 로 변경. 센서스 경로는 "bbox" 로 표시되어, 호출자 `_fetch_emd_list` 가 피처 BBOX 중심점이 시군구 BBOX 에 엄격 포함되는지 추가 검증 후 인접 구 피처를 스킵. 법정 코드로 확정된 "exact" 피처는 추가 검증 없이 통과(회귀 방지)
3.8.13
  • v3.8.12 qt6 빌드 Python 3.13 ABI 불일치 핫픽스 — QGIS 4.0 에 설치 후 플러그인 활성화 시 `ModuleNotFoundError: No module named qgis_AI_assistant.code_reference_db` 발생하던 문제 수정
  • 원인: v3.8.12 CI 의 qt6 매트릭스가 Python 3.13 러너에서 Cython 컴파일해 `.cpython-313-win_amd64.pyd` 산출물을 만들었으나, QGIS 4.0.1-Norrköping 이 실제 번들하는 Python 은 **3.12.13**(사용자 실측 보고). Python 3.12 는 cp313 ABI 태그의 .pyd 를 로드하지 못해 모든 compiled 모듈 import 실패. QGIS 4.0 이 Python 3.13 으로 업그레이드한다는 공개 자료 기반 가정이 실제와 달랐음
  • 수정: GitHub Actions workflow 의 qt_target=6 python_version 을 3.13 → 3.12 (qt5 와 동일)로 교체. Cython 은 런타임에 `qgis.PyQt` 를 통해 Qt 바인딩을 동적 라우팅하므로 컴파일 시 PyQt5/PyQt6 선택은 Python ABI 와 무관. Dual-build 아키텍처는 향후 API 분기 대비로 유지
3.8.12
  • v3.8.11 qt6 zip 설치 실패 핫픽스 — QGIS 4.0 플러그인 매니저에서 qt6 zip 을 설치할 때 "Could not store plugin to the plugin directory" 에러 발생하던 문제를 수정. 원인은 qt6 zip 파일명 `qgis_AI_assistant-qt6.3.8.11.zip` 의 첫 "." 앞 토큰(`qgis_AI_assistant-qt6`)이 zip 내부 루트 폴더명(`qgis_AI_assistant/`)과 불일치해 plugin_installer 의 "폴더명은 file_name 을 첫 "." 에서 split" 관례를 깨트린 것
  • 수정: qt6 suffix 위치를 버전 뒤로 이동 — `qgis_AI_assistant.{version}-qt6.zip` 형식으로 재구성. 첫 "." 앞은 qt5/qt6 모두 `qgis_AI_assistant` 로 동일 → plugin_installer 폴더명 파싱 일치. build_plugin_zip.py / scripts/update_plugins_xml.py / download.astro 3개 파일에서 zip_name 조립 규칙 동기화. qt5 zip 이름은 불변이라 QGIS 3.x 사용자 회귀 없음
  • 소스 코드 변경 없음(빌드 메타데이터만). v3.8.11 에서 설치 못 한 사용자는 저장소 새로고침 후 v3.8.12 로 자동 설치 가능
3.8.11
  • QGIS 4.0 (Norrköping, PyQt6/Python3.13) 지원을 위한 Qt5/Qt6 이중 빌드 인프라 — 같은 버전에서 두 개의 호환 zip(`qgis_AI_assistant.X.Y.Z.zip` + `qgis_AI_assistant-qt6.X.Y.Z.zip`)을 생성하고 `plugins.xml` 에 두 개의 `<pyqgis_plugin>` 엔트리로 노출. QGIS 3.x 사용자는 기존과 동일하게, QGIS 4.x 사용자는 자동으로 qt6 zip 을 설치
  • `public/plugins.xml` 의 `<qgis_maximum_version>` 이 3.99 로 고정돼 QGIS 4.0 플러그인 매니저 목록에 아예 표시되지 않던 구조적 문제 해결. qt5 엔트리(min=3.28, max=3.99) + qt6 엔트리(min=4.0, max=4.99) 로 분리해 QGIS 클라이언트가 자신의 런타임에 맞는 엔트리를 자동 선택
  • GitHub Actions 매트릭스에 qt_target 축 추가 — OS 3종 × Qt 2타겟 = 6개 Cython 빌드 잡(qt5=Python3.12+PyQt5, qt6=Python3.13+PyQt6). assemble 잡도 qt_target 별로 분기해 두 zip 독립 생성. plugins.xml 자동 갱신은 qt5/qt6 assemble 양쪽 성공 후에만 실행되는 `update-web-manifest` 분리 잡으로 이동(한쪽 실패 시 404 URL 가리키는 것 방지)
  • `build_plugin_zip.py` 에 `--qt-target {5,6}` 플래그 추가 — staging 단계에서 metadata.txt 의 qgisMinimumVersion/qgisMaximumVersion 을 타겟별로 자동 치환(원본 metadata 는 보존하므로 빌드 재현성 유지). qt6 zip 은 `-qt6` 접미사로 구분
  • `scripts/update_plugins_xml.py` 신규 — 이중 `<pyqgis_plugin>` 엔트리 plugins.xml 렌더러. 기존 sed 치환은 단일 엔트리만 지원했으나, ElementTree 기반 헬퍼로 대체해 두 엔트리 동시 생성 + 기존 표시용 필드(description / about / tags 등) 재사용
  • PyQt5 5.15+ / PyQt6 양쪽 호환을 위해 8개 파일의 unscoped Qt enum 42건을 scoped form 으로 치환 — `Qt.AlignCenter → Qt.AlignmentFlag.AlignCenter`, `Qt.Key_* → Qt.Key.Key_*`, `Qt.UserRole → Qt.ItemDataRole.UserRole`, `Qt.LeftButton → Qt.MouseButton.LeftButton`, `Qt.WA_ShowWithoutActivating → Qt.WidgetAttribute.*`, `Qt.NoFocus/StrongFocus → Qt.FocusPolicy.*`, `Qt.ControlModifier → Qt.KeyboardModifier.*`, `Qt.Horizontal → Qt.Orientation.*`, `Qt.Cross/PointingHandCursor → Qt.CursorShape.*`, `QDialogButtonBox.Ok/Cancel → StandardButton.*`, `QMessageBox.Yes/No → StandardButton.*`
  • `graph_visualization.py` 에 PyQt6 QtWebEngineCore 폴백 추가 — PyQt6 는 QWebEnginePage 가 QtWebEngineWidgets 에서 QtWebEngineCore 로 이동했으므로 QGIS 4.0 에서 그래프 시각화 탭이 ImportError 로 초기화 실패하던 문제 방어
3.8.10
  • pyqgis_retry 사전 구문 검증 — `_exec_pyqgis` 진입부에 `ast.parse()` 게이트 추가. LLM이 생성한 코드의 `unterminated string literal`(멀티라인 문자열 closing quote 누락) 등 SyntaxError를 exec() 직전에 차단하고 "줄 N: msg" 형태로 정제된 에러 메시지를 재시도 루프에 되돌린다 — 리포트 기준 하루 11건 발생하던 패턴 사실상 제거
  • 허상 메서드 스캐너 — `_KNOWN_HALLUCINATED_METHODS` 맵으로 LLM이 자주 만들어내는 존재하지 않는 메서드 호출(`QgsVectorLayer.setVisible()`, `QgsRasterResampleFilter.setZoomInResampler()` 등)을 정규식 `\.method\(` 로 사전 탐지 + 올바른 대체 API 힌트(`QgsLayerTreeLayer.setItemVisibilityChecked(bool)`, `setZoomedInResampler` 등) 반환. 리포트 상위 AttributeError 24건의 주요 패턴 커버
  • 시스템 프롬프트 PyQGIS 4대 규칙 주입 — `_build_dynamic_instruction` 에 (1) 멀티라인 따옴표 쌍 엄수, (2) 허상 메서드 금지(위 함정 명시), (3) None 체크 강제, (4) API 시그니처 추측 금지를 매 요청마다 주입해 첫 시도 성공률 자체를 개선
  • 스니펫 캐시 집계 버그 수정 — 스니펫 shortcut 경로에서 플러그인이 `learning={"snippet_hit": 1}` 를 전송하지만 NAS DB는 `snippet_cache_hit` 키를 읽어 모든 캐시 히트가 0으로 저장되던 필드명 불일치 수정. 대시보드 캐시 히트율·토큰 절감 효과 측정이 정상화됨
3.8.9
  • V-World 행정동/법정동 필터링 버그 수정 — 기존 `_filter_layer_by_code` 는 필드 이름 우선순위만 보고 `adm_cd LIKE '{SIG_CD}%'` 를 실행해, 시군구+행정동 전체 선택 시 센서스 통계청 코드(lt_c_cademd.adm_cd 8자리, 예: 11230…)와 법정 SIG_CD(5자리, 예: 강남구=11680) 코드 체계 불일치로 0건 매칭되어 "행정동만 안 나오거나 시군구와 무관하게 전체가 로드되는" 증상이 있었음
  • admin_code 자릿수별(2/5/8/10)로 적합한 코드 필드만 우선 매칭하도록 분기 — 길이 2 → ctprvn_cd, 길이 5 → sig_cd+emd_cd(법정 10자리 접두 호환), 길이 8 → adm_cd 전용, 길이 10 → emd_cd/bjd_cd/li_cd/ri_cd
  • admin_name 텍스트 폴백 추가 — 코드 매칭 0건 시 `ADM_NM`/`full_nm`/`SIG_KOR_NM` LIKE '%{name}%' 로 재시도. `load_base_sido/sigungu/hjdong/bjdong` 4종이 admin_name kwargs 를 수신·전달
  • UI 호출부(`_on_load_dataset`) 에서 맥락별 이름(시도명/시군구명/읍면동명) 자동 주입 — 읍면동 ★전체 선택 시에는 시군구명을 admin_name 으로 전달해 통계청 코드 레이어에서도 정확한 필터
  • 둘 다 실패 시 subsetString 을 해제하고 경고 로그 기록 — 빈 레이어가 아니라 BBOX 내 전체 노출로 폴백(사용자가 필터 실패를 식별 가능). `QgsMessageLog "QGIS AI"` 채널에 `[filter] field=xxx code=xxx matched=N` / `name fallback` 로그 추가
3.8.6
  • 조건부 완료 감지 — `BaseProvider._looks_like_more_work` 가 "원하시면/필요하시면/드리겠습니다/말씀해주세요" 같은 제안형 표현을 우선 매치해 완료 상태로 구분. 이전엔 "하겠습니다" 하나로 추가 nudge 를 트리거해 무한 tool_use 루프 위험이 있었음
  • 요구사항 검증 루프 — `_reverse_scan_intents` 가 히스토리를 역순으로 스캔해 취소/변경 신호("취소/아니야/대신/무시하고") 를 검증 전에 반영, 세션당 `MAX_REQUIREMENT_VERIFY=2` 회로 상한
  • Claude / Gemini / OpenAI 메시지 포맷 통합 추출(`_extract_msg_text`) — tool_use / tool_result / functionCall 블록 제외하고 텍스트만 뽑음
  • 텔레메트리 센더 스레드 `_STOP` 센티넬 멱등 종료 — QGIS 정상 종료 시 데몬 스레드가 `sys.modules` 와이프 뒤에 깨어나 `NameError/ImportError` 를 내던 문제 해결
  • `gemini_qgis_plugin.unload()` + `QgsApplication.aboutToQuit` 이중 stop() 방어선 (stop 은 멱등)
  • `_sender_loop` 배치 드레인 교체 — 첫 아이템 블로킹 + nowait 드레인으로 보고 지연 최악 50초 → 1초 미만으로 단축
  • `server_url` race 봉쇄(`_auth_lock` 스냅샷), 큐 포화 관측(`dropped_count`, 첫 드롭만 warning), `_sender_loop` 예외를 `logger.exception` 으로 승격
  • `report_snippet_usage` 스니펫 ID SHA-256 자동 생성 + 캐시미스도 `was_hit=False` 로 보고
3.8.5
  • 도구 실행 결과 자동 검증 체계 — 레이어를 변경하는 도구 12종(load_vector_layer / load_raster_layer / load_wms_layer / add_xyz_tiles / set_layer_style / set_layer_labels / add_field / select_features / join_csv_to_layer / cluster_features / run_processing / evaluate_suitability) 실행 직후 `verify_layer_state`를 자동 호출해 결과에 `_auto_verified` 블록을 주입
  • LLM이 검증 없이 "완료" 를 선언하지 못하도록 방어 — QgsSettings `qgisai/auto_verify_enabled` 로 토글(기본 ON)
  • 카테고리 추론 임베딩 폴백 (opt-in, 환경변수 `QGIS_AI_CATEGORY_EMBEDDING_FALLBACK`, 기본 OFF) — 키워드 confidence 가 낮을 때 Gemini 임베딩 + `category_seeds.py`(L1 6개 카테고리 시드) 코사인 유사도로 재분류. 내부 테스트 기준 정확도 93% → 100%
  • 피드백 학습 DB `_find_success_examples()` — 유사 쿼리의 성공 스니펫을 컨텍스트에 주입해 실패 반복 방지
  • 텔레메트리 강화 — `snippet_id` SHA-256 자동 생성 + 큐 만료 경고 로그 + 스니펫 캐시미스 보고
  • 카테고리 분류기 단위 테스트(`tests/test_classifier.py`, 7 케이스) 추가
3.8.4
  • 3.8.1 핫픽스 — `_on_qgis_project_changed` 슬롯이 `QgsProject::fileNameChanged` 시그널이 인자 없이 발생하는 QGIS 빌드(3.44.7 확인)에서 `TypeError: takes exactly 2 positional arguments (1 given)` 로 실패하던 문제 수정
  • 인자를 optional(None + *args) 로 받고 누락 시 `project.fileName()` 을 조회하도록 방어
3.8.3
  • 센서스 행정동(lt_c_cademd) 로딩 버그 수정 — V-World 센서스 행정동 코드(통계청 8자리, 예: 11230xxx)가 법정 시군구 코드(예: 강남구=11680)와 체계가 달라 `_feat_belongs_to_sig` 필터가 모든 피처를 걸러내 행정동 드롭다운이 항상 비거나 숨겨지던 문제 해결
  • 속성이 `{base_date, adm_cd, adm_nm}` 만 있는 센서스 응답은 WFS BBOX 공간 필터를 신뢰해 통과시킴
  • 3.8.1 의 드롭다운 표시 수정과 함께 "AI 로 행정동 못 불러옴" 현상 해결
3.8.2
  • 렌더링/레이어 패널 자동 동기화 — AI 코드 실행이나 `addMapLayer(layer, False)` 호출로 맵 캔버스엔 보이지만 좌측 레이어 패널엔 누락되던 문제 해결
  • `QgsProject.layersAdded/Removed` 시그널 훅으로 프로젝트 레지스트리와 레이어 트리 불일치를 50ms 뒤 자동 복구
  • 플러그인 로드 시점 기존 불일치 레이어도 초기 1회 복구
3.8.1
  • 저장 시 튕김 방어 — QGIS 프로젝트 경로 변경 시그널을 C++ 디스패치 컨텍스트가 아닌 Python 메인 이벤트 루프로 defer
  • 행정동 드롭다운이 강제 숨겨지던 문제 수정 — 폴백 허용 조회 결과로 판정하던 로직을 `lt_c_cademd` 단독 조회로 바꿔 실제 데이터가 있으면 표시
  • AI follow_up 루프의 조기종료 방지 — tool_use 누락 시 재시도 한계를 1→3 으로, 빈 응답(raw=None) 수신 시에도 2회까지 "계속 진행" nudge 후에만 종료
  • V-World 관리자 키 발급 API(`/api/v1/vworld/issue-key`) 서버 구현 — Google 로그인 사용자가 별도 키 발급 없이 즉시 사용 가능
3.8.0
  • 스니펫 숏컷 텔레메트리 — _try_snippet_shortcut() 히트 시 /api/v1/snippet_usage 로 report_snippet_usage 이벤트 전송 (대시보드 스니펫 탭 데이터 수집 재개)
  • metadata.txt 와 config.py 의 PLUGIN_VERSION 동기화
3.7.1
  • Windows 에서 3.7.0 업그레이드가 "플러그인 패키지의 압축해제 실패" 로 막히던 문제 수정 — unload 시 sys.modules 정리 + gc.collect() 로 Cython .pyd DLL 핸들 해제 유도
  • 프로젝트 변경 자동 리셋 — QgsProject.fileNameChanged 시그널에 맞춰 세션 탭·UI 를 비우고 "채팅 1" 하나만 남김
  • tool_use 누락 백스탑 — "이제 …하겠습니다" 같은 계속 의도 표현이 있는데 tool_use 가 없을 경우 한 번만 재호출 유도 (MAX_NUDGES=1 로 무한 루프 차단)
3.7.0
  • 프롬프트 본문을 배포 zip 에서 완전히 제거 — 18개 ai_prompt_template 필드 삭제, prompt_id 기반 서버 fetch 로 전환
  • system_prompt.py FALLBACK_INSTRUCTION 377줄을 SERVER_REQUIRED_MESSAGE 단일 상수로 축소
  • prompt_fetcher: 디스크 캐시 제거, 메모리 캐시(6시간 TTL)만 유지 + PromptUnavailableError 도입 — 서버 필수 스킬은 실패 시 경고 표시
  • feature flag skill_prompt_from_server 기본 ON + 레거시 prompt_cache.bin 일회성 삭제
  • _on_load_dataset(checked=False) — QPushButton.clicked bool 인자 수신 버그 수정
3.6.1
  • 라이선스 및 상표 컴플라이언스 정리. 플러그인 코드가 GPL-2.0-or-later로 명시됨 (metadata.txt license 필드)
  • EULA 전면 개정 — 플러그인 코드 권리(GPL)와 서버 SaaS 약관을 분리. 기존 "리버스 엔지니어링·재배포 금지" 조항은 서버 API 남용·인증 토큰 공유·유료 콘텐츠 크롤링 금지로 축소
  • /source 페이지 신설 — GPL 소스 링크 및 Cython 대응 소스 정책
  • /trademark 페이지 신설 — QGIS® 표기 및 nominative fair use 고지
  • 기능 변경 없음 — 런타임 동작 및 API 호출은 3.6.0 과 동일
3.6.0
  • 코드 보호 및 이상 관찰 강화 — Cython 컴파일 대상이 11개에서 39개 모듈로 확장 (ai_client, skill_manager, system_prompt, ai_providers, qgis_tools)
  • 시스템 프롬프트를 서버(/api/v1/prompt/system)로 이관, AES-XOR 암호화 디스크 캐시(6시간 TTL)와 3단 폴백 — 서버 장애 시에도 플러그인 동작 유지
  • 기능 플래그(/api/v1/feature_flags) 도입 — 30분 단위 긴급 킬 스위치 가능
  • 서버 측 anomaly_log (UA 패턴, 디바이스 지문, IP — 관찰 전용)
  • 최초 실행 시 EULA 동의 다이얼로그 표시
3.5.1
  • 사이트 계정 "녹화하기" 버튼이 Playwright 브라우저가 연결된 상태에서도 비활성으로 남던 버그 수정
  • 버튼 활성 판단을 __init__ 1회 평가에서 클릭 시점 lazy 체크로 변경
  • _resolve_browser_controller — 부모 체인 + QGIS 메인윈도우 자식 위젯 스캔으로 browser_controller 탐색 (플러그인 툴바 경로에서도 녹화 가능)
  • config.PLUGIN_VERSION 을 metadata.txt 와 동기화
3.5.0
  • 사이트 계정 매니저: 신규 "녹화(Record)" 기능 — 평소처럼 브라우저에서 로그인하고 API 키를 발급하면 계정이 자동 등록됨
  • 녹화기는 보안 원칙상 비밀번호·API 키 값을 절대 읽지 않음 — 사용자가 검토 화면에서 수동 입력
  • 녹화 가이드 "다음 업데이트까지 숨기기" 옵션 — 새 공지는 버전 업마다 자동 재등장
  • 사이트 계정 내보내기/가져오기(JSON) — 보안상 민감 값은 제외, 구조만 백업
  • 녹화된 auth_recipe_json 컬럼 — 업그레이드 시에도 유지됨
3.4.0
  • 커스텀 플러그인 저장소 지원 (https://qgis.ai/plugins.xml) — 한 번 등록 후 업데이트는 버튼 한 번으로 설치
  • 사용자 데이터를 QGIS 프로필 디렉토리 하위 qgis_ai_assistant/ 로 이전 — 사이트 계정, 피드백 DB, API 키 캐시가 업그레이드 시 자동 유지
  • 3.3.x 이하 기존 DB는 최초 로드 시 신규 외부 경로로 자동 마이그레이션
  • 릴리스 파이프라인이 qgis-web/public/plugins.xml 을 자동 갱신
3.3.9
  • 채팅 진행 상태 가시화 — 도구 호출·완료·오류가 실시간 라인 단위로 스트리밍
  • 자격 증명 실패 시 [자격 증명 수정 / 건너뛰기 / 중단] 액션 카드와 사이트 다이얼로그 자동 선택
  • follow_up 에이전트 루프 최대 50회로 제한하여 무한 재시도 방지
  • auth_error 카테고리 + NON_RETRYABLE 확장으로 불필요한 자동 재시도 차단
  • 수정: Google 로그인 사용자에게 VWorld API 키 다이얼로그가 반복 표시되던 문제
3.3.8
  • 멀티 모델 라우팅, 카테고리 추론, 태스크 분해
  • ai_client 를 ai_providers 구조로 리팩터링
  • code_reference_db 및 smart_retriever 확장
  • qgis_tools 에 분석·처리 기능 추가
3.3.7
  • 다운로드 버전을 3.3.7 로 갱신
  • 최신 플러그인 빌드에 맞춰 install.mdx 수정
3.3.6
  • QGIS 재시작 시 VWorld 키 로딩 안정화
3.3.5
  • 간헐적 인증 실패에 대한 VWorld issue-key 우회 처리
3.3.4
  • VWorld 프록시를 공용 NAS 엔드포인트를 통해 경유
3.3.3
  • Google OAuth client 정보를 NAS 서버 /api/v1/oauth/client-config 에서 자동 수신 — 환경변수 QGIS_NAS_GOOGLE_CLIENT_ID/SECRET 미설정 사용자도 Google 로그인 가능
  • OAuth 실패 시 QMessageBox 로 원인 표시 (기존: 작은 상태 라벨에만 표시되어 놓치기 쉬웠음)
3.3.2
  • 이미지→벡터 변환 코드를 배포 zip 에서 물리적으로 제거
3.3.1
  • 핫픽스: 배포 zip 에서 내부 코드 제거
3.3.0
  • 공개용 .pyd 빌드를 Keilkim/qgis-releases 에서 제공
  • 설정 UI: 서버 URL 필드 제거 (api.qgis.ai 자동 사용)
  • NAS 설정: 세션 상태에 따라 로그인 또는 로그아웃만 표시
  • 플러그인 로드 시 오래된 localhost NAS URL 자동 초기화
3.2.0
  • google-auth-oauthlib 대신 순수 stdlib OAuth 로 교체 (macOS SSL 크래시 해결)
  • DEFAULT_NAS_SERVER_URL 이 api.qgis.ai (Fly.io) 로 변경
  • 플러그인 zip 에 실제 Google OAuth 클라이언트 자격 증명 포함
3.1.0
  • 사이트에 플러그인 zip 다운로드 추가
  • ZIP 기반 설치로 설치 가이드 전면 개편
  • 빠른 시작: 단계 번호가 제목 위에 오도록 위치 조정 (겹침 해결)