임상도 레이어를 웹지도나 모바일 앱에 올리려고 하면 거의 항상 걸리는 문제가 있다. 원본 그대로 쓰면 파일이 무겁고 렌더링이 느린데, 폴리곤 정점을 줄이겠다고 단순화(Simplify) 도구를 돌리는 순간 인접한 임상 구획 사이에 틈이 벌어지거나 선이 서로 겹쳐버리는 현상이다. 처음 겪으면 소프트웨어 버그처럼 보이지만, 사실은 단순화 알고리즘이 애초에 폴리곤 하나하나를 독립적으로 취급하기 때문에 생기는 구조적인 문제다. 경계를 공유하는 두 임상 구획을 각각 따로 깎아내면, 깎이는 위치가 미묘하게 달라지면서 공유 경계선 자체가 더 이상 일치하지 않게 된다.
단순화가 경계를 깨는 이유
PostGIS에서 가장 많이 쓰는 두 함수를 보면 이 문제가 바로 드러난다. ST_Simplify는 더글라스-포이커(Douglas-Peucker) 알고리즘을 그대로 적용하는데, 톨러런스를 조금만 크게 잡아도 폴리곤 선이 스스로 꼬이는 자기교차(self-intersection)가 발생할 수 있다. 그래서 실무에서는 대부분 ST_SimplifyPreserveTopology를 쓰도록 배운다. 이 함수는 입력이 유효한 지오메트리였다면 결과물도 유효하게 유지해주고, 링 개수나 꼬임 여부까지 신경 써준다.
그런데 여기서 오해가 자주 생긴다. ST_SimplifyPreserveTopology가 보장하는 건 “그 폴리곤 자체의” 위상이다. 옆 폴리곤과 공유하는 경계선까지 똑같이 맞춰주는 기능은 아니다. 공식 문서에도 이 함수가 폴리곤 간에 공유된 경계는 보존하지 않는다고 명시돼 있다. 결국 임상도처럼 전체 구역이 빈틈없이 맞물려 있는 커버리지(coverage) 성격의 데이터에서는, 각 폴리곤을 따로 돌리는 순간부터 갭·오버랩이 생길 여지가 생기는 셈이다.
작은 톨러런스도 안심할 수 없다
톨러런스 값을 보수적으로, 아주 작게 잡으면 괜찮지 않겠냐고 묻는 경우가 많다. 어느 정도는 맞는 말이지만, 값이 작아도 두 폴리곤이 각각 다른 지점에서 정점을 탈락시키면 공유선이 어긋나는 구조 자체는 사라지지 않는다. 특히 임상도는 경계가 길고 굴곡이 잦은 구획이 많아서, 겉보기엔 깨끗해 보이는 축소본도 확대해서 보면 경계부에 머리카락처럼 가느다란 틈이나 겹침이 줄줄이 생겨 있는 경우가 흔하다. 납품 직전에 QA를 돌리다가 이런 틈을 수백 개 단위로 발견하고 밤새 손으로 스냅(snap)을 잡았던 기억이 있는 담당자라면 무슨 말인지 바로 알 것이다.
커버리지 단위로 단순화하는 방법
이 문제를 근본적으로 피하려면 폴리곤을 하나씩 다루지 말고, 전체 레이어를 하나의 커버리지로 보고 동시에 단순화해야 한다. PostGIS 3.4부터(내부적으로 GEOS 3.12 이상 필요) 제공되는 ST_CoverageSimplify가 바로 이 방식이다. 윈도우 함수로 동작하기 때문에 OVER() 구문으로 레이어 전체 또는 특정 그룹 전체를 한 번에 넘겨줘야 하며, 그 결과 공유 경계가 양쪽 폴리곤에서 정확히 같은 좌표로 유지된다.
다만 전제 조건이 있다. 입력이 처음부터 “유효한 커버리지”여야 한다는 점이다. 원본 임상도에 이미 미세한 갭이나 중첩이 있다면 ST_CoverageSimplify도 그 오류를 그대로 물고 들어가거나 더 이상한 결과를 낼 수 있다. 그래서 돌리기 전에 ST_CoverageInvalidEdges 같은 함수로 먼저 검증하는 단계를 건너뛰면 안 된다. 또 simplifyBoundary 옵션을 false로 주면 다른 레이어(예: 행정구역이나 지적 경계)와 맞닿는 외곽선은 그대로 두고, 임상 구획끼리 공유하는 내부 경계만 단순화할 수 있어서 실무에서는 이 옵션을 꽤 유용하게 쓴다.
데스크톱 GIS에서 작업할 때
서버에 PostGIS 3.4 이상을 올리기 어려운 환경이라면 ArcGIS Pro나 QGIS로 작업하게 되는데, 접근 방식이 조금 다르다. ArcGIS의 Simplify Polygon 도구는 공유 경계를 유지하기 위해 경계가 겹치는 피처를 조각으로 나눠서 순서대로 처리하고, 그래도 안 맞으면 이웃 피처를 먼저 처리한 뒤 다시 돌아와 처리하는 방식을 쓴다. 알고리즘도 하나가 아니라 POINT_REMOVE(더글라스-포이커), BEND_SIMPLIFY(굴곡 기반), WEIGHTED_AREA, EFFECTIVE_AREA(비스발링엄-와이어트) 네 가지 중에서 고를 수 있어서, 산림 구획처럼 능선을 따라 완만하게 굽는 경계에는 단순 점 제거 방식보다 굴곡 보존 방식이 더 자연스러운 결과를 주는 편이다.
QGIS는 기본 Simplify 도구가 폴리곤 단위로만 동작해서 커버리지 전체를 한 번에 다뤄주는 기능은 아직 약하다. 그래서 현장에서는 단순화 후에 반드시 토폴로지 체커나 v.clean 류의 정리 과정을 한 번 더 돌리거나, 아예 mapshaper처럼 공유 경계를 인식하는 외부 도구로 전처리한 뒤 다시 QGIS로 불러오는 식으로 우회하는 경우가 많다.
작업 전 점검 체크리스트
톨러런스를 잡기 전에 아래 순서를 한 번씩 짚어보면 사고를 많이 줄일 수 있다. 원본 레이어는 반드시 복사본을 만들어 작업하고, 단순화 전에 전체 레이어의 위상 오류(갭, 오버랩, 자기교차)를 먼저 검증한다. 톨러런스는 최종 출력 축척에서 선 하나가 눈에 보이는 폭 정도를 기준으로 작게 시작해서 점차 올려보고, 가능하면 개별 폴리곤이 아니라 커버리지 단위 함수를 쓴다. 내부 경계와 외곽 경계를 다르게 다뤄야 하는지 미리 정하고, 단순화 후에는 전체 면적 합계와 경계 일치 여부를 다시 검증하는 것까지 포함해야 작업이 끝난다.
결과물 파일 크기를 줄이는 일은 생각보다 간단해 보이지만, 임상도처럼 전체 구획이 서로 맞물려 있는 데이터에서는 폴리곤 하나를 건드리는 게 아니라 전체 네트워크를 건드리는 작업이라는 점을 놓치면 꼭 한 번은 경계선 사고를 겪게 된다. 도구를 바꾸기 전에 지금 쓰는 함수가 공유 경계를 보장하는지부터 확인하는 습관이, 나중에 QA 단계에서 틈을 하나씩 찾아 메우는 수고보다 훨씬 적게 든다.