Next.js에서 ssg 페이지를 구성하고 반응형 지도 그리기
지난 글에서 csr과 ssr에서 d3가 어떻게 그려지는지 간단하게 보았다. ssr을 강력하게 지원하는 next.js와 같은 프레임워크에서는 렌더링 방식을 최대한 이용하는것이 좋다. 특히 지도와 같은 큰 용량을 다루는 그래프는 next.js가 제공해주는 기능을 잘 쓰기만 하면 성능을 크게 향상시킬 수 있다. 이번 글에서는 큰 데이터를 다루는 그래프를 그리는 방법을 공유해 본다.
지도를 그리는데 필요한 데이터는 폴리곤 데이터와 지도에 표시할 데이터가 있다. 데이터는 전처리를 잘 하게 되면 크게 문제될만한 사이즈는 못되지만, 폴리곤 같은 경우는 경우에 따라 수메가 에서 수십, 수백메가까지 이를 수 있으므로 적절한 렌더링 전략을 세우는 것이 중요하다.
카카오맵과 같이 우리가 흔히 쓰는 지도들은 관심지역부터 시작하여 사용자의 상호작용에 따라 중간중간 로딩을 감수하며 폴리곤을 더하여 필요지역만 순차적으로 보여주는 방법을 많이 쓴다. 하지만 이번에 글에서 소개할 경우는 그래프로써의 맵으로써 전국 모든 범위에서 폴리곤을 한번에 렌더링하는 상황을 가정한다.
폴리곤을 그릴 수십메가 크기의 geojson파일.
하나의 화면에서 나타내야할 수십메가의 인구 이동 데이터.
raw data에는 지역별, 연령별, 성별, 일별, 시간별 인구 이동 데이터가 들어있고, 1달치만 고려해도 수십메가 이상의 크기이다. 설정하기 나름이지만, 이용자가 1년치 이상의 유동인구를 알고 싶을수도 있기 때문에 확실히 이 데이터를 서버에서의 전처리 없이 클라이언트에서 필터링 하기엔 문제가 있다.
Next.js 는 정적 페이지 생성(SSG)와 서버측 렌더링(SSR)을 매우 적극적으로 지원하는 프레임워크로 무거운 데이터를 다루는 그래프를 생성할 때 매우 큰 도움이 된다. 그래프를 그림으로 생각한다면, 아무리 무거운 데이터라도 결국 svg 그림 파일에 지나지 않기 때문에 어떻게 보면 클라이언트에서 그려야 한다는 발상이 어색하다.
일단 무겁고 복잡한 일들을 서버에게 위임하겠노라 선언한다면 앞에서 언급했던 문제들이 무의미하게 느껴질 것이다. raw data던지, geojson이던지 웹서버에 공간만 충분하다면 얼마든지 저장하여 단순히 읽어 들어 필요한 정보들을 필터링 하여 그림(svg)를 그리면 되는 일이다.
정적인 그래프는 여기서 말그대로 끝이다. 하지만 아까도 말했듯이, 이 지도는 유저의 수요에 맞춰 지도위에 인구 이동을 표현해야 하는 동적인 지도이다.
가장 위에서 topoJson으로 기본 아웃라인을 그리고 필요한 범위(시도,시군구 등)의 기본 바탕이 될 기본 svg를 그리게 된다. 이 후 직접적으로 인구 이동을 표현하게 될 폴리곤과 선들에 대한 정보를 서버에 요청해서 조각으로 가지고 오게 된다. 따라서 바탕은 SSG로 미리 서버에 svg로 그려 놓고 받아오며, 이후 서버에 요청하여 나머지 요소들을 추가로 받아오는 것이다.
geoJson과 topoJson등을 이용하여 `filterMapPolygons`함수에서 지도 바탕을 그릴 폴리곤들을 준비한다. (여기서 outline과 inner boundary를 나눈 이유는 위 작업을 담당하는 `D3MapGenerator`가 재사용 가능한 컴포넌트로 만들었기 때문.) 그리고 각 데이터는 지도 표시 지역을 나타낼 outline, outline내의 나타낼 기본 지역을 나타낼 inner boundary를 그린다. (그리고 추가로 지도가 표시되고 줌 인 될 목표 지역을 나타내는 target outline도 그려준다.)
초기 로딩 시에도 클라이언트에서 데이터 표시를 위한 작업을 하게 된다. 초기 로딩에 기본 표시 데이터를 포함시키면 두가지 문제가 있는데, 첫번째는 기획 의도에 따라 동적으로 바뀌게 되면 페이지를 정적으로 구성(SSG)할 수 없어 불필요하게 SSR페이지가 될 수 있고 두번째는 페이지를 정적으로 구성한다고 해도 데이터 가 담긴 svg파일이 기대보다 커지게 되면 정적 페이지라 하더라도 유저가 빈페이지를 보는 시간이 유의미하게 길어지기 때문이다.
따라서 적절한 유저 경험을 위해 지도 바탕을 먼저 보여주고, 이후에 데이터가 렌더링 되는 효과와 줌 인 효과를 주면서 이를 만회하려는 시도로 초기 로딩 시에도 클라이언트에서 데이터 표시 작업을 처리하게 된다.
하지만 클라이언트의 작업이 raw data를 받거나 어떤 계산을 동반하지는 않는다. ssg로 지도가 표시되게 되면, 이후에 hydration이 일어날 때 클라이언트에서 `getMapInfoWithServer`에서 서버에 특정 조건에 대한 폴리곤 데이터등을 요청하게 되고 요청을 받은 서버는 조건들을 이용해 raw data를 filtering 하고 적절한 폴리곤들(FeatureCollections)을 클라이언트에 반환해준다.
렌더링 흐름을 이해했다면 이후 d3로 하는일은 hydration에서 일어났던 작업의 반복이다. 유저가 기간, 성별, 연력, 지역등을 설정하면 이 조건들을 api를 통해 서버에 요청하면 서버는 데이터로 표시할 폴리곤등을 반환 하게되고 클라이언트에서 `appendResToSVG`에서 반환된 값들을 svg에 붙여준다.