지도를 통째로 담은 데이터베이스 — SQLite로 만든 GIS 컨테이너, GeoPackage

확장자를 .db로 바꿔도 열립니다
korea.gpkg라는 지도 파일을 받았습니다. QGIS로 열기 전에, 확장자를.db로 바꿔 평소 쓰던 SQLite 뷰어에 넣어 봤어요. — 열립니다. 테이블 목록이 주르륵 뜹니다. 지도 파일인 줄 알았는데, 정체는 그냥 SQLite 데이터베이스였던 거예요. 속임수가 아닙니다. 이게 GeoPackage 설계의 전부입니다.
지난 GeoJSON 편은 이런 질문으로 끝났습니다. "텍스트의 편함은 버리더라도, 도형·속성·인덱스를 한 파일에 야무지게 담을 순 없을까?" 오늘의 주인공이 바로 그 답으로 나온 포맷, GeoPackage입니다. 그런데 이 포맷, 접근이 아주 영리해요. 새로운 파일 구조를 발명한 게 아니라 — 세상에서 가장 많이 깔린 데이터베이스를 통째로 빌려 왔습니다.
포맷을 발명하지 않았다 — 발상의 전환
새 공간 데이터 포맷을 만든다고 상상해 봅시다. 할 일이 산더미예요. 바이너리 레이아웃을 설계하고, 인덱스 구조를 고안하고, 깨진 파일 복구를 고민하고, 언어별 파서 라이브러리를 만들어 뿌려야 합니다. Shapefile이 30년간 쌓은 지위가 쉽게 안 무너진 이유이기도 하죠. 다들 "새 포맷 또 나왔네" 하고 안 읽어 주면 그만이니까요.
2014년, OGC(Open Geospatial Consortium — 국제 공간정보 표준화 기구)는 정반대의 길을 택합니다. "저장 문제를 이미 완벽하게 푼 소프트웨어가 있는데, 왜 새로 만드는가?" 그렇게 낙점된 게 SQLite입니다. GeoPackage 표준의 실체는 사실 이 한 문장이에요 — "SQLite 파일 안에, 약속된 이름의 테이블 몇 개를 이렇게 만들어 두자."
SQLite가 어떤 물건인지 잠깐 짚고 갑시다. 서버 없이 파일 하나로 굴러가는 데이터베이스인데, 배포량이 상상을 초월합니다. 모든 안드로이드·아이폰, 모든 주요 브라우저, 윈도우와 macOS 안에 내장돼 있고, 활성 상태의 SQLite 데이터베이스가 수조 개로 추정돼요. 인류가 만든 소프트웨어 중 가장 널리 깔린 축에 속합니다.
💡 여러분도 오늘 이미 SQLite를 수십 번 썼습니다. 메신저에서 옛 대화를 검색할 때도, 브라우저가 방문 기록을 뒤질 때도, 그 밑에서 돌아가는 게 대부분 SQLite거든요. 심지어 미국 의회도서관이 디지털 자료의 장기 보존 포맷으로 SQLite를 권고하고, SQLite 개발팀은 2050년까지 파일 호환성을 보장하겠다고 공언했습니다. "이 파일, 20년 뒤에도 열릴까?"라는 걱정이 없는 거의 유일한 포맷이에요.
이 결정으로 GeoPackage 설계자들이 만들지 않아도 됐던 것들을 보세요
- 검증된 파일 포맷 — 수십억 대의 기기에서 매일 두들겨 맞으며 검증된 저장 구조
- 트랜잭션 — 편집 도중 정전이 나도 파일이 반쯤 써진 채 깨지지 않음
- 인덱스 — 공간 검색에 쓸 R-tree까지 SQLite에 이미 내장
- SQL — 조회·수정·집계 언어를 새로 설계할 필요가 없음
- 모든 언어의 드라이버 — SQLite를 못 읽는 프로그래밍 언어는 사실상 없음
포맷 설계자들이 수십 년 걸려 쌓을 것들을, 채택 한 번으로 다 가져온 겁니다. 이게 "SQLite를 GIS로 쓴다"는 발상의 전환이에요.
뚜껑을 열어보자 — .gpkg의 내부
말로만 하면 심심하니, 진짜로 열어 봅시다. 터미널에서 sqlite3에 .gpkg 파일을 그대로 넣으면 됩니다.
$ sqlite3 korea.gpkg ".tables"
gpkg_contents gpkg_spatial_ref_sys sido
gpkg_extensions gpkg_tile_matrix rtree_sido_geom
gpkg_geometry_columns gpkg_tile_matrix_set ...
gpkg_로 시작하는 약속된 테이블 몇 개와, 실제 데이터가 담긴 테이블(sido)이 보이죠. 핵심만 추리면 이렇습니다.
gpkg_contents— 목차. 이 파일에 어떤 레이어가 들었는지, 각각이 벡터(features)인지 타일(tiles)인지 좌표 없는 순수 표(attributes)인지, 데이터가 지도 위 어느 범위에 걸쳐 있는지를 기록합니다. 프로그램은 이 목차부터 읽어요.gpkg_spatial_ref_sys— 좌표계 사전. Shapefile에서.prj파일이 담당하던 좌표계 정의가, 파일 안의 테이블로 들어왔습니다. 따로 살다가 실종되는 사고가 구조적으로 불가능해요.sido— 피처 테이블. 진짜 데이터입니다. 한 행이 피처 하나.geom이라는 BLOB(바이너리) 컬럼에 도형이 들어가고, 이름·인구 같은 속성 컬럼이 같은 행에 나란히 붙습니다. GeoJSON의 Feature(도형+속성 한 몸)와 같은 사상인데, 저장이 텍스트가 아니라 바이너리 테이블인 거죠.rtree_sido_geom— 공간 인덱스. SQLite에 내장된 R-tree 모듈로 만든 인덱스입니다. 덕분에 "화면에 보이는 이 영역의 피처만 줘"라는, GeoJSON이 끝내 못 하던 질문에 파일 전체를 안 읽고 답할 수 있어요.
이제 Shapefile이 파일 다섯 개로 찢어 담던 것들이 어디로 갔는지 한눈에 정리됩니다.
| Shapefile에선 | GeoPackage에선 |
|---|---|
.shp (도형) | 피처 테이블의 geom 컬럼 |
.dbf (속성) | 같은 테이블의 나머지 컬럼들 |
.prj (좌표계) | gpkg_spatial_ref_sys 테이블 |
.shx (인덱스) | R-tree 인덱스 테이블 |
.cpg (인코딩) | 필요 없음 — 항상 유니코드 |
다섯 파일이 한 파일 속 테이블들이 됐고, 테이블은 파일과 달리 서로 헤어질 방법이 없습니다. Shapefile 편의 3대 사고 — 속성 실종, 아프리카행, 한글 깨짐 — 의 뿌리가 전부 "따로 사는 파일"이었다는 걸 떠올리면, 이 구조가 왜 후계자인지 분명해지죠.
그리고 gpkg_contents의 목차 구조가 말해주듯, 이 컨테이너엔 레이어를 몇 개든 담을 수 있습니다. 시·도 경계, 도로망, 건물 — 벡터 레이어 여러 장에, 배경지도용 타일 이미지 피라미드(래스터), 좌표 없는 통계표까지 한 파일에 공존해요. 폴더가 아니라 컨테이너라고 부르는 이유입니다.
화석이 전부 사라졌다
Shapefile 편에서 봤던 1990년대의 화석들, GeoPackage에선 어떻게 됐는지 하나씩 대조해 봅시다.
| Shapefile의 화석 | GeoPackage에선 |
|---|---|
| 컬럼명 10자 제한 | 제한 없음 — apartment_name 마음껏 |
인코딩 지옥 (.cpg, CP949) | 유니코드 고정 — 한글이 안 깨짐 |
| 2GB 천장 | SQLite 이론 한계 약 280TB |
| 한 파일 = 한 레이어 = 한 도형 타입 | 레이어 무제한, 벡터·타일 혼합 |
| 시각(time) 타입 없음 | DATETIME 있음 |
| 텍스트 254자 제한 | 없음 |
GeoJSON과 비교해도 재미있는 차이가 하나 있습니다. RFC 7946이 GeoJSON의 좌표계를 WGS84(4326) 하나로 못박은 것, 기억하시죠? 그래서 EPSG:5179로 배포되는 한국 공공데이터는 GeoJSON에 담기 전에 반드시 좌표 변환을 거쳐야 했습니다. GeoPackage는 반대예요. gpkg_spatial_ref_sys 테이블에 좌표계 정의 자체를 실어 나르기 때문에, 5179든 5186이든 변환 없이 원본 그대로 담을 수 있습니다. 웹 전송용의 단순함을 택한 GeoJSON, 보관·작업용의 유연함을 택한 GeoPackage — 설계 철학의 차이가 또렷하죠.
지도 파일에 SQL을 날린다
이 포맷의 진짜 매력은 써 볼 때 나옵니다. 먼저 변환부터. Shapefile 편에서 만난 GDAL의 ogr2ogr 그대로입니다.
# CP949 Shapefile을 GeoPackage로 — 좌표계(EPSG:5179)는 변환 없이 그대로 담긴다
ogr2ogr -f GPKG korea.gpkg sido.shp --config SHAPE_ENCODING CP949
# 같은 파일에 레이어를 계속 추가 — 시·군·구도, 도로도 한 파일에 차곡차곡
ogr2ogr -update -f GPKG korea.gpkg sigungu.shp --config SHAPE_ENCODING CP949
이제 이 지도 파일에게 SQL로 직접 질문할 수 있습니다.
$ sqlite3 korea.gpkg "SELECT COUNT(*) FROM sigungu"
250
방금 무슨 일이 벌어졌는지 음미해 보세요. GIS 프로그램을 하나도 안 띄우고, 터미널에서 지도 파일에 쿼리를 날려 시·군·구 개수를 세었습니다. 속성 기반 집계·필터링·조인 — SQL이 되는 모든 게 됩니다.
수정도 마찬가지예요. GeoJSON에서 피처 하나의 이름을 고치려면 파일 전체를 다시 써야 했습니다. GeoPackage는 데이터베이스니까 UPDATE 한 줄로 그 행 하나만 고칩니다. 게다가 트랜잭션 덕에, 수정 도중 프로그램이 죽어도 반쯤 써진 채 망가진 파일이 남지 않아요. 편집 중 크래시가 곧 파일 손상이던 Shapefile과의 결정적 차이입니다.
💡 이 "SQLite에 지도를 담자"는 발상, 사실 먼저 증명한 선배가 있습니다. Mapbox가 만든 MBTiles — 지도 타일 수백만 장을 SQLite 한 파일에 담는 포맷이죠. GeoPackage는 그 아이디어를 벡터·래스터·속성까지 아우르는 OGC 국제 표준으로 일반화한 셈입니다. (MBTiles는 나중에 타일 서버 이야기에서 다시 만납니다.)
하나 더 — GeoPackage 표준화를 주도한 곳 중 하나가 미 국방부 산하 NGA(국가지리정보국)입니다. 서버가 없는 야전에서 태블릿에 담아 쓸 오프라인 지도가 절실했거든요. 모든 모바일 기기에 SQLite가 이미 깔려 있으니, 단일
.gpkg파일은 완벽한 오프라인 컨테이너였던 거죠.
도구 생태계도 이미 넘어왔습니다. QGIS는 3.0부터 새 레이어 저장 시 기본 포맷이 GeoPackage예요. Shapefile로 저장하려면 오히려 드롭다운을 열어 일부러 골라야 합니다. 업계의 기본값이 조용히 바뀐 겁니다.
그래도 만능은 아니다
여기까지 들으면 "그럼 다 GeoPackage 쓰면 되겠네"싶지만, 이 포맷에도 명확한 경계선이 있습니다.
① 동시에 쓸 수 있는 사람은 한 명. SQLite는 파일 잠금 기반이라, 여러 사람(프로세스)이 동시에 쓰기 작업을 하는 상황엔 맞지 않습니다. 나 혼자 분석하고 보관하는 덴 완벽하지만, 팀 전체가 실시간으로 함께 편집하는 중앙 저장소가 필요하다면 — 그건 서버형 공간 데이터베이스 PostGIS의 영역이에요. (Phase 6에서 제대로 만납니다.)
② 브라우저가 그대로 못 먹는다. GeoJSON은 fetch 해서 JSON.parse() 하면 끝이었죠. .gpkg는 브라우저 세상의 표준 시민이 아닙니다. 웹 지도에 올리려면 결국 서버에서 GeoJSON이나 타일로 바꿔 내보내야 해요. GeoPackage는 전송 포맷이 아니라 보관·작업 포맷입니다.
③ "필요한 조각만 원격에서" 가 약하다. R-tree 인덱스는 파일이 내 디스크에 있을 때 빛납니다. 웹 어딘가에 올려진 1GB짜리 .gpkg에서 지금 화면에 보이는 동네만 쏙 받아오는 시나리오엔 설계상 맞지 않아요. 일단 파일을 통째로 내려받아야 시작됩니다.
눈치채셨나요? 세 번째 한계가 묘하게 아쉽습니다. 도형·속성·인덱스를 한 파일에 담는 데까진 성공했는데, 그 인덱스를 네트워크 너머에서 활용하는 것은 숙제로 남은 거예요. 이 숙제를 정면으로 겨냥한 포맷이 다음에 등장합니다.
정리 — 한 문장으로
GeoPackage는 새로 발명한 포맷이 아니라 '지도를 담는 SQLite'다. 도형·속성·좌표계·인덱스·타일을 데이터베이스 테이블로 한 파일에 담아 Shapefile의 화석 제약을 걷어냈고, 그래서 확장자를 바꾸면 그냥 데이터베이스로 열린다.
| Shapefile | GeoJSON | GeoPackage | |
|---|---|---|---|
| 파일 구성 | 3~5개 묶음 | 텍스트 1개 | DB 파일 1개 |
| 좌표계 | .prj 별도 파일 | 4326 고정 | 파일 안에, 아무거나 |
| 공간 인덱스 | 부실(.shx) | 없음 | R-tree 내장 |
| 용량 한계 | 2GB | 실질 수십 MB | 약 280TB |
| 레이어 | 1파일 1레이어 | 사실상 1묶음 | 여럿 + 타일 혼합 |
| 수정 | 깨지기 쉬움 | 전체 다시 쓰기 | UPDATE 한 행 |
| 약점 | 화석 제약들 | 크면 붕괴 | 웹 스트리밍 부적합 |
이제 남은 질문은 하나입니다. GeoPackage가 못 다 이룬 것 — 바이너리와 공간 인덱스의 힘을, 파일을 통째로 내려받지 않고 웹에서 그대로 누릴 순 없을까? 다음 편의 주인공이 정확히 그 답입니다. 1GB GeoJSON을 200MB로 줄이고, HTTP Range 요청으로 화면에 보이는 조각만 스트리밍해 오는 포맷 — GeoJSON의 진짜 후계자, FlatGeobuf를 만나 봅니다.