0. 현대 R 지리공간 분석 생태계: sp/rgdal에서 sf/terra로의 대전환
과거 R 언어의 공간 데이터 분석은 여러 개별 패키지(sp, rgdal, rgeos)를 조합해야 하는 복잡한 구조였습니다. 하지만 2023년을 기점으로 이러한 레거시 패키지들이 공식 CRAN에서 유지보수 중단 및 아카이빙(Archived)되면서, sf (벡터 데이터)와 terra (래스터 데이터)가 명실상부한 R 공간 분석의 표준 축으로 완전히 자리 잡았습니다.
📊 대표적인 R 공간 데이터 패키지 비교 분석
| 패키지명 | 데이터 타입 | 핵심 특징 및 장점 | 현재 권장 여부 | 생태계 역할 및 비고 |
|---|---|---|---|---|
| sf | 벡터 (Simple Features) | 현대적 문법, tidyverse와 완벽 호환, GDAL/PROJ/GEOS C++ 직접 연동 |
적극 권장 | 현재 R 벡터 공간 데이터의 표준 (sp의 공식 후속) |
| terra | 래스터 / 벡터 | raster 패키지의 C++ 재작성 대체제, 대용량 고속 연산, GDAL 연동 |
적극 권장 | 현재 R 래스터 공간 데이터의 표준 |
| sp | 벡터 / 그리드 | S4 클래스 기반 전통 패키지, 레거시 코드 잔존 | 비권장 | sf로 전면 마이그레이션 필요 |
| raster | 래스터 | 전통적인 래스터 패키지였으나 메모리 및 속도 한계 | 비권장 | terra로 전면 전환 권장 |
| stars | 래스터 + 시계열 | 다차원 시공간 데이터 큐브(Data Cube), sf 확장 라이브러리 |
대안 / 보완 | terra와 상호 보완적으로 활용 |
| rgdal / rgeos | 입출력 / GEOS | sp 기반 파일 입출력 및 기하 연산 엔진 |
사용 불가 | 2023년 CRAN 공식 퇴출 (sf, terra 내장) |
| spdep | 공간 통계 / 회귀 | 공간 가중치 행렬(\(W\)), Moran’s I, 공간 자기상관 회귀모형 | 지속 사용 | sf 객체 지원 완비 |
| gstat | 지구통계학 (Geostatistics) | 공간 보간, 베리오그램(Variogram), 크리깅(Kriging) 모델링 | 지속 사용 | sf, terra 연계 분석 지원 |
💡 현대 R 공간 분석의 핵심 원칙: 새로운 데이터 사이언스 프로젝트는
sf+terra조합을 베이스라인으로 구축하고, 공간 계량 모델링이 필요한 경우spdep과gstat을 결합하는 것이 가장 안전하고 지속 가능한 방식입니다.
1. 분석 환경 구축 및 핵심 라이브러리 로드
R에서 벡터 및 래스터 공간 분석을 수행하기 위해 필요한 핵심 패키지를 설치하고 환경을 초기화합니다.
# 1. 핵심 공간 분석 라이브러리 설치
install.packages(c(
"sf", # 벡터 데이터 처리 표준
"terra", # 래스터 데이터 처리 표준
"tidyverse", # dplyr, ggplot2 등 데이터 파이프라인
"leaflet", # 인터랙티브 웹 지도 시각화
"rnaturalearth" # 실습용 글로벌 지리 경계 데이터
))
# 2. 고급 공간 시각화 및 지형 분석 보조 패키지 (필요 시 설치)
# install.packages(c("tmap", "cartogram", "geogrid", "elevatr", "tidyterra", "rmapshaper", "tidygeocoder"))
# 3. 라이브러리 로드
library(sf)
library(terra)
library(tidyverse)
library(leaflet)
library(rnaturalearth)2. sf 패키지의 기하학 계층 구조: sfg → sfc → sf
sf(Simple Features for R)는 OGC(Open Geospatial Consortium)와 ISO 19125 표준을 엄격히 준수합니다. sf 객체는 아래 3단계의 계층 구조로 체계화되어 있습니다.
flowchart LR
sfg["sfg (Simple Feature Geometry)<br/>단일 기하 객체 (점, 선, 면)"] --> sfc["sfc (Simple Feature Column)<br/>sfg 리스트 + 좌표계(CRS) 정보"]
sfc --> sf["sf (Simple Feature Data Frame)<br/>속성 테이블(tibble) + sfc geometry 열"]
sfg(Simple Feature Geometry): 개별 공간 객체 단 1개(Point 1개, Polygon 1개 등)의 순수 좌표 정보를 담는 기저 객체입니다.sfc(Simple Feature Column): 여러sfg객체들을 모은 리스트-컬럼(List-Column)으로, 좌표계(CRS) 메타데이터가 이 단계에서 부여됩니다.sf(Simple Feature): 일반 데이터 프레임(tibble)의 각 행에 속성 정보(이름, 인구 등)와sfc기하학 열(geometry)이 완벽히 결합된 최종 공간 데이터 프레임입니다.
2-1. sfg 기하학 타입 직접 생성 실습
sf가 지원하는 대표적인 7가지 기하학 타입 생성 문법입니다.
# 1. Point (단일 점)
pt <- st_point(c(126.9780, 37.5665)) # 서울시청 경위도
# 2. LineString (선)
ls <- st_linestring(rbind(c(0, 0), c(1, 2), c(2, 1), c(3, 4)))
# 3. Polygon (다각형: 첫 점과 끝 점의 좌표가 일치해야 함)
poly <- st_polygon(list(rbind(c(0, 0), c(4, 0), c(4, 4), c(0, 4), c(0, 0))))
# 4. Polygon with Hole (도넛 형태의 구멍 뚫린 다각형)
poly_hole <- st_polygon(list(
rbind(c(0, 0), c(5, 0), c(5, 5), c(0, 5), c(0, 0)), # 외곽 경계
rbind(c(1, 1), c(4, 1), c(4, 4), c(1, 4), c(1, 1)) # 내부 구멍
))
# 5. MultiPoint (다중 점)
m_pt <- st_multipoint(rbind(c(1, 2), c(3, 4), c(5, 6)))
# 6. MultiLineString (다중 선)
m_ls <- st_multilinestring(list(
rbind(c(0, 0), c(1, 1)),
rbind(c(2, 2), c(3, 3), c(4, 2))
))
# 7. MultiPolygon (다중 다각형: 여러 섬으로 구성된 국가 경계 등)
m_poly <- st_multipolygon(list(
list(rbind(c(0, 0), c(2, 0), c(2, 2), c(0, 2), c(0, 0))),
list(rbind(c(3, 3), c(5, 3), c(5, 5), c(3, 5), c(3, 3)))
))2-2. sfc와 sf 객체로 조합하기
# sfg들을 모아 sfc 컬럼 생성 및 WGS84(EPSG:4326) 좌표계 부여
seoul_pt <- st_point(c(126.9780, 37.5665))
busan_pt <- st_point(c(129.0756, 35.1796))
cities_sfc <- st_sfc(seoul_pt, busan_pt, crs = 4326)
# 속성 데이터 프레임과 결합하여 완전한 sf 객체 완성
korea_cities_sf <- st_sf(
city_name = c("서울특별시", "부산광역시"),
population = c(9400000, 3300000),
geometry = cities_sfc
)
print(korea_cities_sf)3. 공간 데이터 입출력과 구조 탐색
3-1. 파일 읽기 및 내보내기
st_read() 및 read_sf() 함수를 사용하면 Shapefile(.shp), GeoJSON(.geojson), GeoPackage(.gpkg), KML 등 전 세계 거의 모든 공간 포맷을 자동으로 파싱하여 sf 데이터 프레임으로 불러옵니다.
# 1. 벡터 데이터 읽기
# sido_sf <- read_sf("data/korea_administrative_boundaries.shp")
# world_json <- read_sf("data/world_countries.geojson")
# 2. 벡터 데이터 저장
# st_write(korea_cities_sf, "output/korea_cities.geojson", delete_dsn = TRUE)
# st_write(korea_cities_sf, "output/korea_cities.gpkg", layer = "cities")3-2. sf 객체의 핵심 메타데이터 탐색
sf 객체는 R 콘솔에서 일반 data.frame의 속성과 함께 상단에 지리 공간 헤더를 출력합니다.
# 실습용 세계 지도 데이터셋 로드
world_sf <- ne_countries(scale = "medium", returnclass = "sf")
# 공간 구조 확인
glimpse(world_sf) # 속성 컬럼 확인
st_geometry_type(world_sf) # 기하 타입 (MULTIPOLYGON 등)
st_bbox(world_sf) # 전체 바운딩 박스 (xmin, ymin, xmax, ymax)
st_crs(world_sf) # 좌표참조계(CRS) 사양 확인4. 핵심 이론: 좌표계(CRS)와 지형 데이터 모델
4-1. 지리좌표계(Geographic) vs 투영좌표계(Projected)
좌표계(CRS, Coordinate Reference System)의 선택은 공간 연산(거리, 면적, 버퍼)의 수학적 정확도를 결정짓는 가장 중요한 요소입니다.
graph TD
Earth["3차원 타원체 지구"] --> Geo["지리좌표계 (Geographic CRS)<br/>단위: 도 (Degrees)<br/>예: WGS84 (EPSG:4326)<br/>용도: GPS, 웹 지도, 위치 저장"]
Earth --> Proj["투영좌표계 (Projected CRS)<br/>단위: 미터 (Meters)<br/>예: UTM, GRS80 (EPSG:5179)<br/>용도: 거리·면적 계산, 공간 분석"]
| 구분 | 지리 좌표계 (Geographic CRS) | 투영 좌표계 (Projected CRS) |
|---|---|---|
| 기준 모델 | 3차원 지구 타원체 구면 | 2차원 평면으로 투영된 지도 |
| 측정 단위 | 경위도 도 (Degree: °, ’, “) | 미터 (Meter: m, km) |
| 대표 표준 | EPSG:4326 (WGS 84) | EPSG:5179 (Korea 2000 / 통합기준점), EPSG:3857, EPSG:32652 |
| 주요 목적 | GPS 수집, 전 지구적 위치 표현, Leaflet 웹 지도 | 거리(Distance), 면적(Area), 버퍼(Buffer)의 정밀 계산 |
⚠️ 필수 주의사항:
st_area(),st_distance(),st_buffer()와 같이 길이나 면적을 계산하는 모든 함수는 반드시 사전에st_transform()을 사용하여 미터(m) 단위 투영 좌표계(예: EPSG:5179 또는 EPSG:3395)로 변환한 후 실행해야 합니다.
# CRS 투영 변환 예제: WGS84(4326) -> World Mercator(3395)
world_projected <- st_transform(world_sf, crs = 3395)
st_crs(world_projected)4-2. 지형 데이터 모델의 이해: DEM, DTM, DSM, OSM
래스터 고도 데이터와 지리 지형을 다룰 때 각 모델의 정의를 정확히 구분해야 합니다.
| 지형 모델 | 정식 명칭 | 데이터 형태 | 표현 대상 | 인공물/식생 포함 여부 | 주 활용 분야 |
|---|---|---|---|---|---|
| DEM | Digital Elevation Model | 래스터 | 지표면의 고도 총칭 | 포괄적 개념 | 지형 분석 기초 |
| DTM | Digital Terrain Model | 래스터 | 순수 맨땅(지형) 고도 | 제외 (순수 지형) | 토목, 수문 분석, 침수 예측 |
| DSM | Digital Surface Model | 래스터 | 건물, 수목을 포함한 최상단 고도 | 포함 (건물+나무) | 도시 3D 모델링, 가시권 분석, 드론 비행 |
| OSM | OpenStreetMap | 벡터 | 도로망, 건물 외곽선, POI | 벡터 정보 | 경로 안내, 토지 피복 분류 |
5. 속성 데이터 가공과 지도 경계 단순화
5-1. dplyr 파이프라인과 Sticky Geometry 원리
sf 객체의 가장 뛰어난 장점은 dplyr의 filter(), select(), mutate(), group_by()를 일반 데이터 프레임과 동일하게 사용할 수 있다는 점입니다.
여기서 가장 중요한 특징은 “Sticky Geometry (달라붙는 기하학)” 원리입니다: * select(name, population)으로 속성 컬럼만 선택해도, geometry 열은 별도로 지정하지 않아도 자동으로 유지됩니다. * 만약 기하학 열을 완전히 제거하고 순수 데이터 프레임으로 변환하고 싶다면 st_drop_geometry()를 호출합니다.
# S2 구면 기하학 옵션 설정 후 dplyr 파이프라인 수행
sf_use_s2(FALSE)
asia_summary_sf <- world_sf %>%
filter(continent == "Asia") %>%
select(name, pop_est, gdp_md) %>%
mutate(
pop_millions = pop_est / 1e6,
calc_area_km2 = as.numeric(st_area(.)) / 1e6
) %>%
rename(country = name)
glimpse(asia_summary_sf)5-2. rmapshaper를 활용한 정점 단순화 (Topology Preserving Simplification)
고해상도 지도 데이터는 정점(Vertex)이 지나치게 많아 렌더링과 웹 서빙 속도를 저하시킵니다. rmapshaper::ms_simplify()를 사용하면 인접 폴리곤 간의 위상 관계(Topology)를 깨뜨리지 않으면서 지도를 가볍게 압축할 수 있습니다.
library(rmapshaper)
# 원본 형상을 유지하면서 99%의 불필요한 정점 제거 (1% 보존)
world_simplified_sf <- ms_simplify(world_sf, keep = 0.01, keep_shapes = TRUE)
# 시각화 비교
par(mfrow = c(1, 2))
plot(st_geometry(world_sf), main = "Original (고용량)", col = "antiquewhite")
plot(st_geometry(world_simplified_sf), main = "Simplified (경량화 1%)", col = "lightblue")
6. 필수 공간 기하 연산 (Spatial Operations)
공간 분석의 핵심이 되는 집합 연산, 근접 분석, 공간 조인 기법입니다.
6-1. 공간 집합 연산 (Intersection, Difference, Union)

# 두 개의 겹치는 원형 버퍼 생성
b <- st_sfc(st_point(c(0, 1)), st_point(c(1, 1)))
b <- st_buffer(b, dist = 1)
x <- b[1] # 원 A
y <- b[2] # 원 B
# 교집합 (겹치는 영역) 추출
intersection_xy <- st_intersection(x, y)
plot(b, border = "grey", lwd = 2, main = "st_intersection Result")
plot(intersection_xy, col = "steelblue", border = "darkblue", add = TRUE)
6-2. 공간 조인 (Spatial Join)
st_join()은 두 공간 데이터셋의 공통 키(Key)가 없더라도 지리적 위치 관계(포함, 교차, 인접 등)를 기준으로 속성을 결합합니다.
# 가상의 랜덤 샘플 포인트 10개 생성
set.seed(42)
sample_points <- st_sample(world_sf, size = 10)
# 포인트가 어느 국가 폴리곤 내부에 속하는지 국가 속성 결합
points_joined <- st_join(st_sf(geom = sample_points), world_sf, join = st_intersects)
select(points_joined, name, continent)6-3. 최근접 분석 (Proximity Analysis)
st_nearest_points()와 st_nearest_feature()는 특정 지점에서 가장 가까운 객체를 탐색하고 최단 거리 연결 선분을 생성합니다.
# 점과 선 사이의 최단거리 선분 도출
ls1 <- st_linestring(rbind(c(-2, -3), c(1, 4)))
p1 <- st_sfc(st_point(c(0.1, -0.1)))
shortest_line <- st_nearest_points(p1, ls1)
plot(ls1, lwd = 2, main = "점과 선 사이의 최단거리 (st_nearest_points)")
plot(p1, add = TRUE, col = "red", pch = 19, cex = 1.5)
plot(shortest_line, add = TRUE, col = "blue", lwd = 2, lty = 2)
# 점과 원형 버퍼 사이의 최단거리 도출
r <- sqrt(2) / 10
b1 <- st_buffer(st_point(c(0.1, 0.1)), r)
pts <- st_sfc(st_point(c(0.3, 0.1)))
shortest_circ <- st_nearest_points(pts, b1)
plot(b1, col = NA, border = "blue", lwd = 2, main = "점과 원 사이의 최단거리")
plot(pts, add = TRUE, col = "red", pch = 19, cex = 1.5)
plot(shortest_circ, col = "forestgreen", lwd = 2, lty = 2, add = TRUE)
6-4. 중심점(Centroid vs Point on Surface)과 버퍼(Buffer)
st_centroid: 기하학적 무게 중심을 계산합니다. 도넛 모양이나 초승달 형태, 섬 지형(예: 노르웨이)의 경우 중심점이 폴리곤 외부의 바다나 허공에 위치할 수 있습니다.st_point_on_surface: 어떠한 복잡한 기하 형태라도 반드시 폴리곤 내부 영역 안에 위치하는 대표점을 보장합니다.
# 노르웨이 영역의 중심점 비교
norway_sf <- world_sf[world_sf$name_long == "Norway", ]
centroid_pt <- st_centroid(norway_sf)
surface_pt <- st_point_on_surface(norway_sf)
plot(st_geometry(norway_sf), col = "antiquewhite", border = "grey50", main = "Centroid vs Point on Surface")
plot(centroid_pt, col = "blue", pch = 4, cex = 2, lwd = 2, add = TRUE)
plot(surface_pt, col = "red", pch = 3, cex = 2, lwd = 2, add = TRUE)
legend("topright", legend = c("st_centroid (외부 위치 가능)", "st_point_on_surface (내부 보장)"),
col = c("blue", "red"), pch = c(4, 3))
# 선분 주변 50cm 버퍼 영역 생성
ls_buf <- st_linestring(rbind(c(-2, -3), c(1, 4)))
buff_50cm <- st_buffer(ls_buf, dist = 0.5)
plot(buff_50cm, col = "#FFEEEE", border = "red", lwd = 2, main = "st_buffer 버퍼 영역 생성")
plot(ls_buf, lwd = 3, add = TRUE)
7. 고품질 주제도 시각화 생태계
7-1. ggplot2 + geom_sf를 활용한 현대적 단계구분도
ggplot2의 geom_sf()를 사용하면 좌표계 자동 일치, 범례 관리, 라벨 배치(ggrepel)를 손쉽게 구현할 수 있습니다.
# ggplot2 geom_sf와 ggrepel을 결합한 대한민국 인구 분포 단계구분도 예시
# ggplot(data = korea_sido_sf) +
# geom_sf(aes(fill = population_man), color = "white", size = 0.3) +
# scale_fill_viridis_c(option = "plasma", name = "인구수 (만 명)") +
# geom_label_repel(aes(x = COORDS_X, y = COORDS_Y, label = sido_name), size = 3) +
# theme_minimal() +
# labs(title = "대한민국 시도별 인구 통계 지도", subtitle = "sf 및 ggplot2 geom_sf 기반 렌더링")
과거 sp 객체를 fortify()하여 그리던 번거로운 방식에 비해, geom_sf()는 기하학 메타데이터를 직접 인식하여 코드를 획기적으로 줄여줍니다.

7-2. tmap: 전문적인 주제도(Thematic Maps) 제작
tmap은 지도 제작에 특화된 문법(tm_shape() + tm_polygons())을 제공하며, 출판용 정적 지도와 인터랙티브 Leaflet 지도를 단 한 줄의 모드 전환(tmap_mode())으로 지원합니다.
library(tmap)
# 정적 단계구분도 (Plot Mode)
# tmap_mode("plot")
# tm_shape(korea_sido_sf) +
# tm_polygons("population", palette = "Blues", title = "총인구") +
# tm_layout(title = "대한민국 인구 통계 지도", frame = FALSE)


tmap_mode("view")를 설정하면 브라우저 상에서 줌/팬이 가능한 인터랙티브 맵으로 즉시 전환됩니다.

또한 제주도나 도서 지역을 별도 창으로 배치하는 인셋(Inset) 지도 레이아웃도 손쉽게 구성할 수 있습니다.

7-3. Cartogram & Geogrid: 면적 왜곡도 및 육각형 격자(Hexbin) 지도
cartogram: 지리적 면적이 아닌 특정 변수(예: 인구수, 경제 규모)의 크기에 비례하여 폴리곤의 면적을 유기적으로 왜곡합니다. 인구 밀집 지역(수도권 등)의 가시성을 극대화할 때 매우 효과적입니다.geogrid: 각 행정구역을 동일한 크기의 육각형(Hexagon) 또는 사각형 격자로 재배열하여 면적 왜곡으로 인한 통계 착시를 제거합니다.
# 1. Cartogram 연속 왜곡 지도 생성
# library(cartogram)
# korea_carto <- cartogram_cont(korea_sido_sf, "population", itermax = 10)
# tm_shape(korea_carto) + tm_polygons("population", palette = "Reds")
# 2. Geogrid 육각형 격자 지도 생성
# library(geogrid)
# korea_hex_grid <- calculate_grid(shape = korea_sido_sf, grid_type = "hexagonal", seed = 42)
# korea_hex_map <- assign_polygons(korea_sido_sf, korea_hex_grid)
# tm_shape(korea_hex_map) + tm_polygons("population", palette = "Viridis")
8. 실전 사례 연구 (Case Studies)
8-1. 지오코딩: 주소 문자열을 위경도 좌표로 변환 (tidygeocoder)
데이터베이스에 있는 도로명 주소를 지리 좌표로 자동 변환한 뒤 leaflet에 시각화하는 파이프라인입니다.
library(tidygeocoder)
# 1. 주소 데이터 프레임 준비
address_df <- tibble(
place_name = c("서울특별시청", "부산광역시청", "대전광역시청"),
addr = c(
"서울특별시 중구 세종대로 110",
"부산광역시 연제구 중앙대로 1001",
"대전광역시 서구 둔산로 100"
)
)
# 2. 지오코딩 실행 (ArcGIS 서비스 활용)
geocoded_locations <- address_df %>%
geocode(address = addr, method = "arcgis", lat = latitude, long = longitude)
# 3. Leaflet 인터랙티브 마커 렌더링
leaflet(geocoded_locations) %>%
addProviderTiles(providers$CartoDB.Positron) %>%
addMarkers(
lng = ~longitude, lat = ~latitude,
popup = ~paste0("<b>", place_name, "</b><br>", addr)
)8-2. terra와 elevatr를 활용한 고해상도 지형 경사도(Slope) 분석
elevatr 패키지로 AWS 지형 서버에서 DEM 래스터를 내려받고, terra 패키지로 경사도를 계산하여 지형 특성을 파악하는 워크플로우입니다.
library(elevatr)
library(terra)
library(tidyterra)
# 1. 관심 영역(AOI) 정의 및 DEM 다운로드 (zoom level 11)
# target_dem_raster <- get_elev_raster(locations = target_aoi_sf, z = 11, clip = "bbox")
# target_rast <- rast(target_dem_raster)
# 2. terra 공간 분석: 경사도(Slope) 및 방위(Aspect) 계산
# slope_rast <- terrain(target_rast, v = "slope", unit = "degrees")
# aspect_rast <- terrain(target_rast, v = "aspect", unit = "degrees")
# 3. 고도 및 경사도 시각화
# ggplot() +
# geom_spatraster(data = slope_rast) +
# scale_fill_hypso_c(palette = "dem_poster", name = "경사도 (°)") +
# theme_minimal() +
# labs(title = "지형 경사도 분석 지도", subtitle = "terra::terrain 함수 기반 연산")9. 실무자를 위한 핵심 체크리스트 요약
R 공간 데이터 분석을 실무에 적용할 때 반드시 점검해야 하는 골든 룰 5가지입니다:
📚 참고 문헌 및 공식 출처
| 구분 | 자료명 및 공식 사이트 | 주요 내용 | 바로가기 |
|---|---|---|---|
| R 공식 재단 | sf: Simple Features for R (CRAN) | 벡터 공간 데이터 표준 사양, C++ GEOS/PROJ/GDAL 바인딩 가이드 | 공식 문서 |
| R 공식 재단 | terra: Spatial Data Analysis (CRAN) | 고성능 래스터 및 벡터 데이터 처리, 지형 분석 함수 레퍼런스 | 공식 사이트 |
| 오픈 서적 | Geocomputation with R (Robin Lovelace et al.) | 모던 R 공간 데이터 사이언스, GIS 알고리즘 및 공간 통계 종합 교재 | 원문 도서 |
| 시각화 공식 | tmap: Thematic Maps in R | 정적 및 대화형 단계구분도, Inset 레이아웃, 주제도 디자인 가이드 | 공식 튜토리얼 |
| 지형 패키지 | tidyterra: tidyverse Methods for terra Objects | ggplot2와 terra의 geom_spatraster 연동 및 고도 팔레트 가이드 | 공식 문서 |