QAI

릴리스 노트

새 소식.

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

3.11.32 최신
  • [시군구 경계] 지도와 항상 펼친 지역 목록을 양방향으로 동기화합니다. 추가 버튼이나 Shift+클릭으로 불러올 경계 리스트에 담고 항목별로 삭제할 수 있습니다.
  • [공공데이터] 자료 목록을 36px 높이의 2열·3열로 촘촘하게 배치했습니다. 검색·분류를 바꿔도 담은 자료와 선택 순서를 유지하며 하단에서 함께 불러옵니다. 실행 시 자료와 범위를 고정하고 실패한 항목은 재시도를 위해 남깁니다.
  • [내보내기] 문서 양식과 공통 체크 표시를 적용했습니다. 북마크 이름 선택은 QGIS 주 지도를 해당 범위로 이동시키며 체크는 출력 대상만 바꿉니다.
3.11.31
  • [설정 탭 로고] AGORA·QAI 로고를 작게 줄여 이름 왼쪽에 한 줄로 배치하고, 이름과 소개 문구를 읽기 쉽게 키웠습니다. 기능 변경은 없습니다.
3.11.30
  • [설정 탭 새 디자인] 흰 바탕에 ① 약관 동의·구글 로그인 ② AI 채팅 활성화(선택) ③ 프로젝트 폴더 지정을 번호 순서로 배치했습니다. 공공 API 10개의 연결 상태를 링 표시로 한눈에 보여 주고, 실패한 서비스는 아래 한 줄로 알려 줍니다.
  • [QAI 소개] 2단계 제목 아래에 AGORA와 같은 형식으로 QAI 로고와 소개를 넣었습니다. Gemini API 키는 입력 후 [적용]으로 연결합니다.
  • [Playwright 설치] API 키 없이 [Playwright 설치 시작] 버튼으로 따로 설치하며, 설치 진행률을 버튼에 표시합니다.
  • [사용 가능 여부] 화면 맨 아래에 지금 사용할 수 있는지와 남은 단계를 알려 줍니다. 약관에 동의하면 약관 본문이 자동으로 접히고, 사용법 영상과 추가 설정은 하단 링크에서 열 수 있습니다.
3.11.29
  • [QAI 로고] 적갈색 단색과 세 개의 굵은 면으로 심벌을 단순화하고, QGIS 툴바·플러그인 관리자·웹사이트에 같은 로고를 적용했습니다.
  • [사이트 계정] 같은 QAI 심벌의 우측 하단에 작은 i를 더해 메인 버튼과 구분합니다. 투명 배경과 작은 크기에서의 가독성을 정리했으며 기능 변경은 없습니다.
3.11.28
  • [AI 작업 진행] 채팅으로 입력하고 번호가 있는 단계 카드와 스피너로 진행 상황을 확인합니다. 완료·오류·취소를 구분하며 결과는 보고서로 남습니다.
  • [자동 실행과 중간 질문] 요청한 작업은 기본적으로 바로 실행하고, 필요한 정보만 선택지와 직접 입력 카드로 묻습니다. 답변하면 기존 작업을 이어서 진행합니다.
  • [데이터와 지도] 원본 날짜의 기준·갱신·생성·등록을 구분하고 출처를 보존합니다. 국내 지도 투영과 로그인 후 공공 API 자동 확인, QAI·AGORA 색상 정리도 포함합니다.
  • [관리자 학습 화면] NAS 대시보드에 재사용 판단 학습과 PyQGIS LLM 작업·자료·워커·평가 관리 화면을 추가했습니다. 실제 LLM 학습은 별도 워커가 연결되고 자료 조건이 충족된 뒤 시작합니다.
3.11.19
  • [🧭 스킬 탭 사용법] 스킬 탭 맨 위에 짧은 사용법을 띄우고, 그룹마다 무엇을 입력으로 쓰는지(📍 지역 / 🎯 대상지)를 한 줄로 보여 줍니다. 51개 버튼 설명을 내부 코드 대신 ‘무엇을 불러오는지 · 출처 · 필요한 것’으로 다시 썼고, 사용 설명서에 ‘스킬 탭 사용법’ 장을 새로 넣었습니다.
  • [🔘 회색 버튼] 조건이 안 맞는 버튼도 누를 수 있게 했습니다. 누르면 무엇이 부족한지와 고칠 곳으로 바로 가는 버튼을 보여 주고, 조건이 방금 풀렸다면 그대로 실행합니다.
  • [✅ 실행 결과] 스킬 탭 아래에 결과 한 줄(추가된 레이어 · 걸린 시간 · 불러온 범위)을 보여 주고, 실패 안내를 쉬운 말로 바꿨습니다. ‘로그 탭에서 확인하세요’라던 자세한 기록이 이제 실제로 로그 탭에 남습니다.
  • [🏙️ 용도지구 · 도시계획시설] ‘용도지구’ 버튼이 경관지구만, ‘도시계획시설’ 버튼이 도로만 불러오던 문제를 고쳐 이름대로 9종을 모두 불러옵니다.
  • [▶ 일괄 실행] 체크한 버튼 이름을 AI 채팅으로 보내던 방식을 버리고, 화면 순서대로 직접 실행한 뒤 성공 · 실패 · 건너뜀을 이유와 함께 한 번에 보여 줍니다.
  • [🛠️ 오류 수정] AGORA 로그인이 만료된 상태에서 채팅 [승인] · [수정 제출]을 누르면 오류가 나던 문제, AI를 쓰지 않는 생활안전 버튼이 AI 키가 없으면 회색이던 문제를 고쳤습니다. PLUGIN_VERSION 3.11.18 → 3.11.19.
3.11.12
  • [♻️ 코드 스니펫 재사용 개선] 정확히 같은 작업은 저장 시점이 오래됐다는 이유만으로 탈락하지 않도록 검색 기준을 바로잡았습니다. 벡터 인덱스의 오래되거나 삭제된 항목을 원본 DB와 맞추고, 빈 후보가 섞여도 유효한 코드가 채워질 때까지 제한적으로 보충 검색합니다.
  • [🛡️ 안전한 실행 분리] 레이어 삭제·프로젝트 저장·파일 쓰기·네트워크·대기 루프처럼 영향이 큰 코드는 정확히 일치해도 자동 실행하지 않고 AI 검토와 사용자 승인 경로로 보냅니다. 검색 코드를 참고한 계획은 그 출처를 후속 단계까지 유지해 승인 없는 도구 실행도 차단하며, 릴리스 ZIP에는 Git 작업트리 정보가 들어가지 않게 보강했습니다.
  • [📊 재사용 대시보드 개선] 직접 재사용·AI 참고·저장 코드 관여율·미사용을 서로 배타적인 지표로 분리하고 기간·분모·측정 의미를 명시했습니다. KPI 배치, 실시간 피드, 표 확장 버튼, 모바일 반응형과 키보드·스크린리더 지원도 함께 다듬었습니다.
  • [🎨 QAI 아이콘 정리] QAI 나침도·AGORA 설정·사이트 계정 아이콘을 최신 아트워크로 교체해 제품 화면과 플러그인 관리자 표시를 맞췄습니다.
  • [🧪 실제 QGIS 검증] QGIS 3.44.7에서 저장 작업 178건을 재생해 94건은 안전한 직접 재사용, 84건은 AI 참고 경로로 분류됐고 저장 코드 미관여는 0건이었습니다. 깨끗한 릴리스 작업트리에서 플러그인 회귀 1,047건과 서브테스트 87건(1건 제외), 대시보드·서버 회귀 483건을 통과했습니다. PLUGIN_VERSION 3.11.11 → 3.11.12.
3.11.8
  • [🧹 설정 화면 정리] 설정 탭 하단의 AICON 로고와 ‘Powered by AI Leader at AICON’ 문구, www.ai-con.kr 링크를 제거했습니다. 설정 항목만 남아 화면이 간결해지며 기능 변경은 없습니다. PLUGIN_VERSION 3.11.6 → 3.11.8.
3.11.6
  • [☑️ 최초 진단 설정] 새 프로필에서 처음 AGORA에 참여하면 ‘사용 데이터 전송’이 체크된 상태로 시작합니다. 이후에는 사용자가 저장한 ON/OFF를 그대로 복원하고, 참여 철회 뒤 재참여나 저장소 오류에서는 자동으로 다시 켜지지 않습니다. 진단 전송을 꺼도 AGORA의 다른 기능은 계속 이용할 수 있습니다.
  • [⌨️ 메시지 입력 포커스] 채팅 입력창을 선택할 때 나타나던 파란 테두리를 없앴습니다. 키보드 포커스는 중립적인 미세 배경 변화와 커서로 구분해 화면의 재질과 흐름을 유지합니다.
  • [⚖️ 고지 동기화] 진단 초기 설정 변경을 이용약관·개인정보처리방침·코드 기여 라이선스 안내에 정확히 반영하고 정본 버전과 해시를 2026.08.13a로 갱신했습니다. PLUGIN_VERSION 3.11.5 → 3.11.6.
3.11.5
  • [✨ QGIS UI 개편] 채팅·설정·사이트 계정·브라우저 녹화 화면을 카드형 그룹과 정돈된 간격, 새 메시지 작성 영역으로 개편했습니다.
  • [🧭 텍스트 편집 절차] 브라우저 녹화가 UTF-16 커서·선택·부분 편집과 contenteditable 구조 앵커를 처리합니다. 실제 문자열과 자격증명 위치 정보는 공유하지 않으며 구조가 달라지거나 위치가 모호하면 재생을 중단합니다.
  • [🛠️ Gemini 도구 호환성] 동적 map 스키마를 Gemini parametersJsonSchema 형식으로 변환해 사이트 절차 도구 호출의 HTTP 400 오류를 막았습니다.
3.11.4
  • [✅ 동의는 실제로 한 번] 최초 ‘AGORA에 참여하고 시작’ 클릭에 기록한 시각·약관 버전·정본 해시·코드 기여 라이선스를 Google 로그인 뒤 현재 계정·기기에 그대로 연결합니다. 로그인 뒤 같은 내용에 다시 동의하라는 창이나 버튼은 없으며, 설정에서는 재확인이 아니라 철회만 제공합니다. 선택형 오류·사용 패턴 진단은 계속 별도 기본 OFF입니다.
  • [📦 성공 코드 유실 방지] 성공한 정확 일치 PyQGIS 코드는 서버 전송보다 먼저 내구성 있는 로컬 대기열에 원자적으로 저장합니다. QGIS가 종료되거나 네트워크가 일시적으로 끊겨도 다음 실행에서 재시도하며, 서버가 수신을 확인하기 전에는 로컬 항목을 지우지 않습니다.
  • [🧭 AGORA 브라우저 절차] 보이는 Playwright 창에서 클릭·입력·탭·프레임·URL 전환과 텍스트 커서·선택·교체 위치를 순서대로 녹화합니다. 검색어·지역 같은 값은 템플릿 변수로 분리하고, 선택·주변 텍스트나 실제 입력값 없이 UTF-16 숫자 오프셋만 절차에 남깁니다. 계정값은 로컬에만 남으며, 경로별 별도 동의를 받은 값 없는 템플릿만 검토 후 공동 풀에 제공됩니다. 일반 채팅은 로컬 경로를 우선하고 없을 때만 같은 도메인·역할의 승인 경로를 헤드리스로 재생합니다.
  • [📊 대시보드 수치 정합성] 최근 500건만 세던 학습 집계를 전체 조회 기간 기준으로 고쳤고, AGORA 참여율의 분자와 분모를 같은 활성 Google 사용자 모집단으로 맞췄습니다. 대화·오류·기여 페이지네이션, 스니펫 태그 필터, 번들 시드와 실제 사용자 기여 분리, 진단 이벤트 중복 제거를 함께 적용했습니다.
  • [🛡️ 대시보드 표시 안정성] 외부 CDN 없이 표·막대·네트워크를 표시하고 차트가 실패해도 표는 독립적으로 렌더링됩니다. 관리자 로그와 동적 값은 안전한 텍스트로 표시하며, 중앙 AGORA 운영 현황과 선택형 연합 실험을 분리해 비활성 API 호출과 화면 누락을 막았습니다.
  • [⚖️ 처리 고지 갱신] 브라우저 절차 공동 풀의 도메인·역할·값 없는 템플릿·UTF-16 숫자 위치·검색·실행 영수증 처리와 다른 이용자 제공, 보유·철회 방식을 개인정보처리방침과 이용약관에 명시했습니다. 정본 버전과 해시를 함께 갱신하며 기존 참여자는 변경 문서를 다시 확인합니다. 브라우저 절차 공유 라이선스도 2026.08.11a로 갱신했습니다. PLUGIN_VERSION 3.11.3 → 3.11.4.
3.11.3
  • [✅ 한 번의 AGORA 참여] 체크박스 여러 개와 두 번째 코드 라이선스 확인창을 없앴습니다. ‘AGORA에 참여하고 시작’ 한 번으로 현재 서비스 약관과 코드 기여 라이선스를 수락하며, 선택형 오류·사용 패턴 진단은 별도 기본 OFF로 유지합니다.
  • [🔒 미참여 QAI 완전 차단] 참여 전에는 채팅·설정·사이트 계정·레이어 정리 등 QAI 화면을 만들지 않습니다. 거부하면 화면 뒤의 기능을 사용할 수 없고, 철회하면 진행 중 요청과 대기 계획을 취소·폐기하고 기존 독 인스턴스도 재사용하지 않습니다.
  • [🤝 실제 상호기여 강제] 참여 후 Google 계정 선택은 추가 동의가 아니라 기여 귀속을 위한 본인 확인입니다. 최신 참여 증빙과 필수 코드 기여 권한이 현재 계정·기기에 연결되기 전에는 채팅·스킬을 열거나 메시지를 실행할 수 없습니다. 로그아웃·재로그인과 계정 전환에서도 필수 기여 권한을 자동 복구·재검증합니다.
  • [🛡️ 서버 멤버 게이트] NAS는 약관·개인정보처리방침·3문서 정본 해시·코드 기여 라이선스를 append-only 원장에 함께 기록합니다. 기존 동의는 자동 승격하지 않으며, 최신 3.11.3 참여 증빙이 없는 계정의 중앙 코드 검색·제출·프록시·관리형 기능·AGORA 접근을 차단합니다.
  • [⚖️ 권리와 진단 분리] 개인정보처리방침은 동의가 아닌 처리 고지로 분리했고, 미참여·철회 상태의 기존 기여 및 개인정보 권리 요청은 공개 문의 경로로 처리합니다. 플러그인 소스의 GPL-2.0-or-later 권리는 그대로 유지합니다. PLUGIN_VERSION 3.11.2 → 3.11.3.
3.11.2
  • [⚡ QGIS 멈춤 방지] NAS 토큰이 만료됐거나 서버가 느린 상황에서 약관 동의·진단 설정·코드 기여 관리 등을 누르면 QGIS 전체가 최대 20초 멈추던 문제를 해결했습니다. 인증 갱신은 백그라운드에서 한 번만 수행하며, 실패하면 화면에 원인과 재시도 방법을 표시합니다.
  • [🔐 계정 전환 격리] 로그아웃·계정 전환·약관 철회 중 늦게 도착한 인증·진단·기여 응답을 폐기하고, 이전 계정의 토큰·대기 작업·관리형 API 키 캐시를 즉시 지웁니다. 시작 시 늦은 로그인 응답이 로그아웃한 계정을 되살리는 경합도 차단했습니다.
  • [♻️ 중앙 코드 재사용 보호] 코드 검색부터 계획 표시·사용자 승인·QGIS 메인 스레드 실행·결과 영수증까지 요청을 시작한 계정에 고정했습니다. 중간에 계정이 바뀌면 이전 계정 코드는 화면에 표시하거나 실행하지 않으며, 대기 계획과 원격 컨텍스트를 제거합니다.
  • [🧪 검증] 플러그인 전체 회귀 748건과 NAS 회귀 319건, GitHub PyQGIS 헤드리스 CI 및 독립 보안 리뷰를 통과했습니다. PLUGIN_VERSION 3.11.1 → 3.11.2.
3.11.1
  • [✅ 최초 동의 UX] 필수인 공식 NAS 온라인 서비스 약관·개인정보처리방침과 선택인 사용 데이터 전송·성공 코드 기여를 한 화면에서 명확히 분리했습니다. 사용자가 직접 "모두 동의"를 누르면 세 항목이 함께 체크되지만, 선택 항목은 각각 다시 끌 수 있고 필수 항목만 동의할 수도 있습니다.
  • [🔐 계정별 코드 기여] 최초 화면의 코드 기여 선택은 로그인 후 상세 동의 절차를 요청하는 선택입니다. 실제 제출은 현재 로그인 계정에 재배포 범위와 철회 방법을 다시 보여주고 별도 확인을 마친 뒤에만 시작하며, 계정이 바뀌면 새 계정으로 다시 동의해야 합니다. 진단 전송 동의와 코드 기여 동의는 서로 독립적입니다.
  • [⚖️ 이용 범위 명확화] 필수 동의를 거부하거나 철회하면 정식 Google 서비스 세션·관리형 프롬프트/데이터 프록시·중앙 코드 재사용 API·대시보드 등 공식 NAS 제품 기능만 사용할 수 없습니다. 미동의 상태에서도 약관 동의·철회 재시도와 본인 기여 권리 행사를 위한 10분 제한 계정 확인은 허용하지만 제품 로그인은 아닙니다. GPL 플러그인의 로컬 사용·연구·수정·재배포 권리와 이용자 기기에서 직접 이루어지는 BYOK 호출은 제한하지 않습니다. 선택 동의를 거부했다는 이유로 다른 기능을 제한하지 않습니다. PLUGIN_VERSION 3.11.0 → 3.11.1.
3.11.0
  • [🔐 중앙 코드 재사용 베타] 성공 코드는 로그인 계정별 명시 동의를 마친 경우에만 제출되며, 서버 보안 검사와 관리자 검토 전까지 격리됩니다. 검색은 자연어 원문이나 전체 코드 목록을 보내지 않고 정확 일치 지문으로 승인 코드 최대 한 건만 받습니다. 받은 코드는 기존과 같이 실행 전 화면에서 확인하고 승인해야 합니다.
  • [📊 가치 측정] 기여→검색→제공→적용→클라이언트 성공 보고 흐름을 중앙 원장과 대시보드에 연결했습니다. 절감 토큰은 코드 제공 시점의 서버 보수 추정치이며 독립 검증이나 실제 청구액으로 과장하지 않습니다. 기여자·소비자 계정과 일일 사용량 한도는 서버에서 관리합니다.
  • [🛡️ 개인정보·자산 보호] 진단 이벤트에서 자연어·코드·오류 트레이스를 제거하고, 구버전이 내려받았던 shared 골든·교정 코드도 업그레이드 시 로컬 검색 DB에서 격리·제거합니다. 기존 전체 코드 pull은 중단하고 비코드 오류 규칙만 유지합니다. AGORA 연합 동기화는 별도 운영 옵트인 전까지 기본 동결했습니다.
  • [🧯 안정성] QGIS 종료 시 AGORA SSE 소켓과 백그라운드 스레드를 안전하게 깨우고 정리해 종료 크래시 조건을 막았습니다. PLUGIN_VERSION 3.10.19 → 3.11.0.
3.10.17
  • [🐛 대화 브릭] "keys must be str, int, float, bool or None, not QVariant" 오류가 한 번 뜬 뒤로는 새 질문을 해도 계속 같은 오류만 반복되던 문제를 고쳤습니다. 속성값이 비어 있는(NULL) 레이어를 분석할 때, AI가 그 값을 집계표의 항목 이름으로 쓰면 대화 기록에 저장할 수 없는 값이 섞여 들어갔고 그 이후 모든 요청이 같은 자리에서 실패했습니다. 대화를 새로 시작하는 것 말고는 빠져나올 방법이 없었고, 그 대화는 저장조차 되지 않아 복구도 불가능했습니다. 이제 그런 값은 "NULL"로 바꿔 담고, 이미 망가진 상태로 남아 있던 대화도 다음 요청에서 자동으로 복구됩니다. Gemini뿐 아니라 Claude·GPT 경로에도 같은 결함이 있어 함께 고쳤습니다.
  • [🐛 함께 고친 것] 날짜·도형처럼 "비어 있는지"를 스스로 판단할 수 있는 값이 실제로는 비어 있지 않은데도 전부 빈 값으로 지워지던 문제, 대화에 저장할 수 없는 값이 섞이면 세션이 디스크에 저장되지 않아 통째로 사라지던 문제를 고쳤습니다. 신규 회귀 테스트 15건. 전체 396건 통과. PLUGIN_VERSION 3.10.16 → 3.10.17.
3.10.7
  • [🚨 생활안전] 열분포도를 추가했습니다. 국립재난안전연구원이 Landsat 위성영상으로 추정한 여름철(6~9월) 평균기온을 30m 격자로 보여줍니다. 범례는 파랑 20℃에서 빨강 28℃ 이상까지 0.5℃ 간격입니다. 기존 생활안전 버튼과 마찬가지로 대상지 영역만 요청하고, 대상지가 폴리곤이면 그 모양대로 잘라 올립니다. 인증키는 로그인만 하면 자동 발급되므로 따로 신청하실 필요가 없습니다. ⚠️ 위성영상으로 기온을 추정한 값이라 일부 오차를 포함하며, 등급이 높은 지역이 현재 위험하다는 뜻은 아닙니다(자료 제공처 고지).
  • [🏔 지형분석] 경사도·표고 범례를 실무 도면 관행에 맞췄습니다. 경사도는 5°이하 / 5~10 / 10~15 / 15~20 / 20~30 / 30°이상 6등급으로 바꾸고, 저경사=밝은 연녹에서 급경사=진한 적갈로 명도가 한 방향으로만 떨어지도록 색을 다시 잡았습니다. 보내주신 도면 색을 그대로 쓰면 명도가 중간에서 꺾여 흑백 인쇄·복사 시 '5°이하'와 '30°이상'이 같은 회색으로 찍히고, '10~15°'와 '20~30°'가 적록색각에서 사실상 같은 색이 됩니다(5~10°와 10~15°는 색상각이 동일해 분리 자체가 불가능했습니다). 등급 구간은 도면 그대로 두고 색만 조정했습니다. ⚠️ 20~30°를 한 등급으로 묶으면서 25° 경계가 사라집니다 — 25°는 산지관리법 시행령 별표4의 산지전용 평균경사도 상한이라, 산지전용 검토가 필요하면 레이어 속성으로 따로 조회해 주세요.
  • [🏔 지형분석] 표고는 고정 등급표 대신 DEM의 실제 표고범위에 맞춘 연속 그라데이션(살구→주황→적→자주)으로 바꿨습니다. 고정 등급표는 표고차가 30m 남짓인 시가지 대상지에서 화면 전체를 한 색으로 뭉개고 있었습니다.
  • [📊 통계·인구 (KOSIS)] 통계 버튼의 입력이 시도/시군구 콤보로 바뀌었습니다. KOSIS 통계는 애초에 행정구역 단위로 집계된 값이라 지도에 대상지 폴리곤을 그리는 것보다 행정구역을 고르는 편이 맞고, 대상지 방식은 사각형 범위로 조회하게 되어 모서리만 스친 옆 시군구가 '인접지역 포함'을 켜지 않았는데도 함께 올라왔습니다. 이제 선택한 행정코드로 정확히 거릅니다 — 서울을 고르면 25개 구, 강남구를 고르면 강남구만 나옵니다. 수원시처럼 하위 구가 있는 시는 장안·권선·팔달·영통 4개 구로 펼치고 겹치는 상위 시 폴리곤은 자동으로 뺍니다. '인접지역 포함'을 체크하면 예전처럼 주변까지 함께 나오고, 콤보를 고르지 않으면 기존처럼 대상지/화면 범위로 동작합니다.
  • [🐛 함께 고친 것] 영동군(43740)과 증평군(43745)처럼 행정코드 앞 4자리를 공유하지만 상하위 관계가 아닌 지역이 지도에서 삭제되던 문제, 인천 중구·서구가 출장소 때문에 사라지던 문제, 시도 콤보가 2023 개편 전 코드(강원 42·전북 45)를 써서 강원·전북에서만 지역 필터가 통째로 무효화되던 문제, 표고에서 결측(-9999) 화소가 색 범위에 빨려들어 전 화면이 단색이 되던 문제, 표고 범례가 '0~255'로 표시되던 문제를 고쳤습니다. pytest 271건 통과. PLUGIN_VERSION 3.10.6 → 3.10.7.
3.10.6
  • [🏔 지형분석] "DEM을 눌러도 경사도/표고 버튼이 계속 회색"이던 문제를 세 갈래로 고쳤습니다 — (1) 두 버튼의 사용 조건이 [AI 키, V-World]로 잘못 걸려 있어 DEM을 올려도 게이트가 버튼을 먼저 꺼버렸습니다(지형분석은 로컬 처리라 두 조건 모두 불필요합니다). (2) 활성/비활성을 판정하는 내부 목록이 한 번도 채워지지 않아 판정이 통째로 무효였습니다. (3) 경위도(EPSG:4326) DEM의 경사도 값이 수만 배 부풀려지던 것을 미터 좌표계로 재투영한 뒤 계산하도록 바꿨습니다. DEM 파일을 바로 고를 수 있는 [DEM] 버튼을 추가했고, 등록하면 대상지로 자동 선택돼 두 버튼이 즉시 켜집니다. 프로젝트에 등고선/표고점만 있어도 활성화됩니다.
  • [📊 통계·인구 (KOSIS) 신규] 국가통계포털 통계를 행정구역 경계에 조인해 단계구분도로 올리는 스킬 그룹을 추가했습니다 — 총인구·인구밀도·세대수·고령인구비율(65세+)·순이동(전입−전출)·지가변동률(월간)·총인구(시도), 그리고 임의의 통계표를 키워드로 찾아 올리는 [KOSIS 통계 검색]입니다. 값이 없는 지역은 회색으로 명시해 '0'과 구분되고, 순이동처럼 부호가 의미인 지표는 0을 색 전환점으로 잡는 발산 색상을 씁니다. 등급 경계는 화면에 걸린 지역이 아니라 전국 분포로 계산해 확대·축소해도 색이 바뀌지 않습니다. 인증키는 로그인 시 자동 발급되며, 직접 발급받은 키는 '사이트 계정'에 KOSIS로 등록하면 우선 사용됩니다.
  • [⚠️ KOSIS 조인 정확성] KOSIS는 같은 '종로구'를 가리키는 5자리 코드가 세 체계(법정 현행 11110 / 법정 구코드 / 통계청 11010)로 공존하고 일부가 서로 겹칩니다 — 통계청 11110은 노원구인데 법정 11110은 종로구입니다. 코드만 믿으면 20개 시군구에 다른 지역 값이 조용히 칠해지므로, 응답이 들고 있는 시도명으로 코드 체계를 판정해 통계청 계열은 코드 조인을 금지하고, 남는 코드 조인도 지역명으로 검증해 어긋나면 기각합니다. 강원 42→51·전북 45→52 구코드와 세종(시도이자 시군구)도 함께 보정합니다.
  • [🐛 대화 브릭] 캐시된 코드를 바로 실행하는 세 경로(하이브리드·스니펫 직접실행·내보내기)에서 실행은 성공하고 직후 '결과 점검·복구' 턴이 매번 오류로 죽던 문제를 수정했습니다 — Gemini 3는 도구호출마다 서명을 요구하는데, AI를 거치지 않고 플러그인이 직접 만든 도구호출에는 그 서명이 없었습니다. 3.10.1에서 고친 브릭과 증상은 같지만 원인이 다른 별개 건입니다. 이미 서명이 빠진 채 쌓인 대화도 다음 요청에서 자동 복구됩니다. 신규 회귀 테스트 37건(KOSIS) + 52건(지형) + 6건(Gemini 서명). PLUGIN_VERSION 3.10.5 → 3.10.6.
3.10.5
  • [📄 고지] 약관 4항의 "개인 식별 정보를 전송하지 않습니다"가 Google 로그인을 쓰는 경우 사실과 달랐습니다. 로그인을 실행하면 Google ID 토큰이 서버로 전송되고 서버가 계정 이메일을 인증 목적으로 저장합니다(홈페이지 개인정보처리방침에는 이미 필수 수집으로 고지돼 있었습니다). 사용 데이터 전송과 로그인 인증을 분리해 각각 정확히 적었습니다. 로그인하지 않으면 이메일은 전송되지 않고, "사용 데이터 전송"을 꺼도 로그인 기능 자체는 동작합니다. 약관 재동의를 1회 요청합니다. PLUGIN_VERSION 3.10.4 → 3.10.5.
3.10.4
  • [🔒 중요] 3.10.3에서 추가한 "사용 데이터 전송" 끄기가 연합 학습(AGORA) 보고에는 적용되지 않았습니다. 약관·가이드·FAQ는 "끄면 서버로 아무것도 보내지 않습니다"라고 안내했지만 실제로는 서명 해시와 잡음 처리된 합의 수치가 계속 전송됐습니다. 이제 토글이 이 경로까지 함께 끕니다(재시작 불필요). 3.10.3을 설치하고 전송을 끄신 분은 이 버전으로 업데이트해 주세요.
  • [📄 홈페이지] 기능 소개의 "텔레메트리는 옵트인" 문구가 실제 동작(기본 켜짐 + 설정 탭 옵트아웃)과 반대여서 정정했습니다. pytest 131건 통과. PLUGIN_VERSION 3.10.3 → 3.10.4.
3.10.3
  • [🔒 선택권] 설정 탭에 "사용 데이터 전송" 체크박스를 추가했습니다. 그동안 약관·가이드·FAQ가 모두 "설정에서 끌 수 있습니다"라고 안내했지만 실제로는 끄는 수단이 없었습니다. 이제 체크를 해제하면 서버로 아무것도 보내지 않고 즉시 반영됩니다(재시작 불필요). 끈 상태에서도 로컬 학습과 코드 재사용은 그대로 동작합니다. 기본값은 기존과 동일하게 켜짐이라 동작 자체가 바뀌지는 않습니다.
  • [📄 고지 정정] 약관 4항에 "AI가 생성한 PyQGIS 코드 스니펫 전송"을 명시했습니다. 실제로 전송되고 있었으나 목록에 빠져 있었습니다. 전송하지 않는 것(파일 내용·MAC 원본·개인식별정보), 자연어 요청이 원문 대신 슬롯 템플릿으로 치환된다는 점, 오류 메시지·트레이스백은 기본적으로 전송되지 않는다는 점도 함께 적었습니다. 데이터 수집 고지의 실질 변경이라 약관 재동의를 1회 요청합니다.
  • [📄 사용 가이드] 서로 모순되던 3항(원문 미전송·옵트인)과 4항(옵트아웃)을 실제 동작 기준으로 정리하고, 전송 끄기를 세 층위로 다시 썼습니다.
  • [⚖️ 라이선스] README의 "MIT License" 표기를 GPL-2.0-or-later로 정정했습니다. 배포 파일 안에 GPL 전문과 MIT 표기가 동시에 들어 있어 자기모순 상태였습니다. pytest 129건 통과. PLUGIN_VERSION 3.10.2 → 3.10.3.
3.10.2
  • [💬 답변 품질] 트리거 조건이 하나도 없는 에러 규칙이 모든 질문에 무조건 딸려가고 있었습니다. 활성 규칙 17건 중 13건이 여기 해당했고, "로그인 실패"·"사이트 정보를 찾을 수 없습니다" 같은 극히 특정한 상황의 규칙이 무관한 질문마다 프롬프트를 2,317자씩 차지했습니다. 이제 상황에 맞는 규칙만 주입됩니다.
  • [💬 답변 품질] AI가 방금 만든 레이어의 이름을 돌려받지 못해, 다음 질문에서 이름을 되추측하다 "레이어를 찾을 수 없습니다"로 이어지던 문제를 수정했습니다. 새 레이어도 출력도 결과도 없는 실행을 "완료했습니다"로 답하지 않도록 표시도 추가했습니다.
  • [📊 학습 정확도] 한 번의 실패 뒤에 같은 대화에서 성공이 여러 번 일어나면 같은 실패가 그 횟수만큼 중복 학습됐습니다(평균 4.7배, 최다 32배). 그 위에 규칙 신뢰도와 우수 사례 승격 판정이 서 있어 학습 신호가 전반적으로 왜곡돼 있었습니다. 중복을 차단하고 누적 수치를 실제 값으로 재계산했습니다.
  • [⚡ 성능] 코드 재사용 데이터베이스가 저장 중에는 검색을 막는 모드로 동작하고 있었습니다(다른 데이터베이스는 이미 개선된 모드). 대화 중 검색과 학습 저장이 겹치는 구간에서 체감되던 지연과 실패를 줄였습니다.
  • [🧹 정리] 스니펫을 지워도 분류 매핑이 남아 1,952건 중 497건(25%)이 고아 상태였고 카테고리 사용 횟수도 그만큼 부풀어 있었습니다. 정리하고 삭제 경로 세 곳에 재발 방지를 넣었습니다. pytest 112건 통과. PLUGIN_VERSION 3.10.1 → 3.10.2.
3.10.1
  • [🐛 대화 브릭] 저장된 코드를 바로 실행하는 경로(스니펫 직접실행·내보내기·하이브리드)에서, 실행은 성공했는데 바로 다음 질문이 매번 오류로 죽던 문제를 수정했습니다. 대화 기록에 형식이 맞지 않는 데이터가 섞여 들어가 이후 대화가 연쇄적으로 실패했고, 저장된 코드가 잘 재사용될수록 오히려 더 자주 끊기는 상태였습니다.
  • [🗺️ 경계 데이터] 리(里) 경계를 불러와도 상위 읍/면 경계와 인접 읍/면이 함께 나오던 문제를 수정했습니다. 읍면동 레이어를 항상 먼저 조회하는 구조라 리 경계는 조회조차 되지 않았고, 행정코드 필터가 실패해도 성공으로 보고했습니다. 이제 선택한 단계에 맞는 레이어를 불러오고, 필터가 실패하면 잘못된 경계를 성공으로 보여주지 않습니다.
  • [🧪 품질] 저장소 폴더 이름이 QGIS 내장 모듈을 가려 자동 테스트가 생긴 이래 한 번도 실행되지 않던 문제를 수정했습니다. pytest 92건 통과. PLUGIN_VERSION 3.10.0 → 3.10.1.
3.10.0
  • [🔒 보안] 그동안 인증 없이 열려 있던 공유 서버(NAS) 데이터 수집 API 7개(계정 저장·다운로드·파일 삭제 등)에 관리자/로그인 인증을 적용하고, 잘못된 경로를 이용한 서버 파일 접근(경로 우회)을 차단했습니다.
  • [🔒 보안] 설정·자격증명 저장을 원자적으로 처리해 저장 도중 중단돼도 기존 키(V-World 등)가 사라지지 않도록 하고, 원격 진단 데이터에서 개인정보(실제 파일 경로 등)를 걸러내는 스크러버를 적용했습니다.
  • [🐛 크래시] 이미지→벡터 변환 중 백그라운드 작업이 지도에 접근하다 QGIS가 종료되던 문제, 로그인/조치 대기 후 다음 대화가 멈추던 문제, OpenAI 응답에 빈 인자가 올 때의 크래시를 수정했습니다.
  • [🐛 안정성] 구버전 데이터베이스 사용자의 재사용 패턴 조회 크래시(자동 마이그레이션), 일시적 네트워크 오류를 로그인 오류로 오판해 자동 재시도를 건너뛰던 문제, 일부 환경의 기기 식별 불안정을 수정했습니다.
  • [🔁 재사용] 저장한 코드를 다시 찾을 때 특정 벡터 DB(pgvector)에서만 점수가 낮게 매겨져 잘 안 걸리던 문제와, 코드 식별자 파싱 오류로 재사용이 아예 동작하지 않던 문제를 해결해 저장된 코드를 더 잘 재사용합니다.
  • [🔁 검색] 임베딩 캐시가 앞부분이 같은 문서끼리 뒤바뀌던 충돌과, 실행마다 검색 점수가 미세하게 달라져 결과가 흔들리던 비결정성을 제거해 검색 정확도를 안정화했습니다.
  • [🔁 학습] 한 번에 여러 작업을 처리할 때 결과 조각이 엉뚱한 작업에 잘못 학습되던 문제를 작업 마커 기준 정확 매칭으로 해결하고, 정답/수정 사례가 과하게 걸러지던 문제와 중복 저장을 개선했습니다.
  • [🧩 도구] 지도/레이아웃 이미지 저장이 실패했는데도 성공으로 잘못 보고하던 문제, 범주별 스타일에서 목록에 없는 값의 피처가 안 보이던 문제, 재사용 코드의 출처 표시가 항상 기본값으로만 나오던 문제를 수정했습니다.
  • [🖥️ 서버] AGORA 관련 환경설정에 잘못된 값이 있어도 공유 기능 전체가 멈추지(503) 않도록 안전 처리했습니다.
  • 이번 릴리즈는 심층 후속 감사와 신규 버그 헌트로 약 40건을 수정했으며, 신규 단위 테스트 20건 추가·pytest 74건 통과로 검증했습니다. (일부 QGIS 런타임 동작은 실기기 확인 권장.) PLUGIN_VERSION 3.9.0 → 3.10.0.
3.9.0
  • 전체 코드베이스 멀티에이전트 감사 반영 — 2026-07-02 감사에서 검증 통과한 80건을 69개 실행 티켓으로 정리해 68건 적용 (qgis/ 53개 파일 +3,733/−2,215, +NAS 서버 6개 파일). 근본 원인 3가지: 메인스레드 블로킹 만연 · 배포판 죽은 기능 다수 · 프로바이더 라우팅 계약 파손.
  • [🔴 치명적 크래시·데이터 유실] 도크를 X 버튼으로 닫으면 채팅이 영구 브릭되던 문제(#01), 스타일 복제 exportNamedStyle 시그니처 오류로 심볼로지 유실(#14), 탭 닫기 removeTab 재진입 세션 인덱스 오염(#07), 탭 전환이 중지된 작업 취소를 되돌리던 문제(#28) 수정.
  • [🟠 프로바이더/라우팅] 자동 모델 라우터가 프로바이더 전환 시 Strategy 미교체(#02), 하이브리드 라우팅 pending_plan 유실(#03), OpenAI GPT-5 계열이 거부하는 파라미터로 전 요청 HTTP 400(#04), _trim_history 프로바이더 인식화(#05), 백그라운드 슬롯추출 히스토리 교체 레이스(#06) — 멀티키·긴 세션 사용자 확정 실패 해소.
  • [🟠 메인스레드 블로킹 해소 = 최대 체감 개선] LLM 도구 실행·http_download 타임아웃(#15), 도구 실행기 120초 가짜 실패·이중 실행(#29), 독 최초 오픈 동기 pip·서버 HTTP(#08), PG 풀 connect_timeout 부재(#09), 시도/시군구/읍면동 콤보 동기 WFS(#12), evaluate_suitability 공간 인덱스로 O(N×M) 제거(#16), 필지별 최대 1~2만 HTTP 스톰(#35) 등 UI 프리즈 대거 제거.
  • [🟠 배포판 죽은 기능 복구] 절대 임포트로 배포판에서 전멸하던 AGORA(#18)·자동 레이어 검증(#23), 자동 업데이터 python executable 오지정(#17), update_helper 트랜잭셔널 백업 스왑(#20), Dockerfile agora_quorum 미복사(#21), 종료 시 학습데이터 정리 VACUUM·경로(#22), PyQGIS API 버전 게이트·재색인(#31/#48), 카테고리 랭킹 키 불일치(#32) 등.
  • [🟡 RAG/토큰 낭비] Jaccard 검색 전체 테이블 스캔(#10), 프로바이더 프롬프트 캐싱·반복 업로드(#24), 매 턴 중복 검색 이중 주입(#25), 골든/수정 패턴 임베딩 배치화(#30).
  • [🟡 NAS 서버] 텔레메트리 SQLite 이벤트루프 블로킹(#43), 데이터 수집기 Playwright 배포 불가(#44), AGORA 보존 정리 미호출 DB 무한 증식(#45), /agora/report 비숫자 payload 500→400(#63), /spatial GeoJSON 전체 메모리 로드 제거(#64).
  • ⚠️ [안전 폴백 1건] 스킬 버튼 클릭 메인스레드 오프로드(#11)는 vworld_marketplace_downloader 스레드 비안전성·30초 타임아웃과 충돌해 크래시 대신 안전한 프리즈로 status quo 유지 — 향후 로더별 마샬링 완료 시 재활성화. 편집 후 전수 py_compile/compileall 통과 + 파일 간 인터페이스 grep 대조 검증. QGIS/PyQGIS 런타임 동작은 실기기에서 각 티켓 검증 방법대로 재현 확인 필요. PLUGIN_VERSION 3.8.73 → 3.9.0.
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.8
  • 자동 업데이트 도우미 (Windows Cython 잠금 우회). 플러그인 → "업데이트 확인 및 설치" 메뉴 → plugins.xml 체크 → zip 다운로드 → `%TEMP%/qgis_ai_update/staging/` 압축해제 → `update_helper.py` 를 DETACHED 프로세스로 spawn → QGIS 메인윈도우 close() 요청 → 헬퍼가 QGIS 종료 확인 후 기존 폴더 삭제(재시도 5회) + staging rename + QGIS 자동 재실행. Cython `.pyd` 파일이 QGIS 프로세스에 메모리 매핑돼 Windows 파일 잠금이 걸려 기본 Plugin Manager 업그레이드가 "압축해제 실패" 로 끝나던 현상을 근본 해결. [신규 파일] `update_helper.py` (독립 실행, stdlib 만), `plugin_updater.py` (체크/다운로드/스테이징/헬퍼 spawn). [__init__.py] 플러그인 로드 초기에 `cleanup_orphan_staging()` 으로 이전 세션 실패 잔여물 정리. [gemini_qgis_plugin.py] 메뉴 3번째 액션 "업데이트 확인 및 설치" + QProgressDialog 진행률 표시. Linux/Mac 은 Plugin Manager 기본 경로로 안내(플랫폼 가드). 재시작할 qgis-bin 경로는 `QgsApplication.applicationFilePath()` 로 탐지. 로그: `%TEMP%/qgis_ai_update/update.log`.
3.8.7
  • 학습 루프 전면 복구 (스니펫 히트·Correction·Golden·실시간 피드 라벨링). [스니펫 캐시 히트] `DIRECT_RETURN_THRESHOLD` 0.82 → 0.55, `_rank_and_filter` 가중치 재조정(c.score 0.30 → 0.50) — 임베딩 미가동 환경(Jaccard 폴백)에서도 히트 가능. 기존 임계값은 임베딩+카테고리+recency가 모두 강할 때만 도달 가능해 대시보드 캐시 히트율이 0% 로 고착. `gemini_chat_dock._setup_embedding_client` 가 실패 시 원인(키·Ollama 부재) 을 명시 로그로 노출. [이벤트 분리] `feedback_learning_db` 가 correction_pair 감지·golden_pattern 재사용 시 `execution_recorded` 에 참조를 끼워 보내는 대신 `correction_pair_created` / `golden_pattern_reused` 신규 이벤트를 분리 발행 — 서버 집계 쿼리가 `logs.level='feedback'` 테이블에서 event_type 별로 COUNT 가능해짐. `get_correction_code()` 가 골든 히트 시 `reuse_count++` 를 트랜잭션 안에서 수행 후 lock-free 영역에서 이벤트 emit. [카테고리 라벨] `command_classifier.build_display_label()` 신규 — L1·L2 한국어(`_L1_KO/_L2_KO`) 조립 후 페이로드 `display_label` 필드로 전송. 키워드 매치 0개 시 기본값이 `pyqgis_code` → `free_query` 로 변경(UI 표시 "자유 질의"). `snippet_usage.tags` 에 `category:*` 접두 태그 포함해 대시보드 카테고리 분포 집계 가능. [토큰 전송 정정] `ai_client._last_reported_usage` 도입해 `report_execution()` 마다 단건 delta 만 전송 — 이전에는 세션 누적값 (예: 4,347,271 토큰) 이 매 행마다 반복 표시돼 과금 추적을 왜곡. Claude provider 가 `cache_read_input_tokens` / `cache_creation_input_tokens` 를 `_token_usage` 에 별도 누적하고 텔레메트리에 `tokens_cache_read` / `tokens_cache_creation` 필드로 실어 Anthropic 캐시 할인 가시화. [스킬 클릭 신뢰성] `chat_dock_skill_mixin` 이 `_telemetry` 미초기화 시 `_ensure_feedback_telemetry()` 를 지연 호출하도록 안전장치 추가 — 스킬 위젯이 텔레메트리 싱글톤보다 먼저 그려질 때 발생하던 `/api/v1/skill_click` 유실 복구.
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 키를 발급하면 계정이 자동 등록됨
  • 녹화기는 로그인 ID·비밀번호·API 키 입력을 로컬 메모리에서 감지해 검토 화면에 채우되, 실행 절차에는 값 대신 {login_id}/{password}/{api_key} 토큰만 저장하고 공유본에는 계정값을 포함하지 않음
  • 녹화 가이드 "다음 업데이트까지 숨기기" 옵션 — 새 공지는 버전 업마다 자동 재등장
  • 사이트 계정 내보내기/가져오기(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 기반 설치로 설치 가이드 전면 개편
  • 빠른 시작: 단계 번호가 제목 위에 오도록 위치 조정 (겹침 해결)
3.0.0
  • QWebEngine 내장 브라우저를 OpenClaw 브라우저 자동화로 교체, 스냅샷-참조번호 방식 채택
2.0.0
  • 내장 브라우저 자동화, 사이트 계정 관리, 코드 레퍼런스 시스템, 강화된 PyQGIS 지원 추가
1.0.0
  • Initial release