etc

[react] openlayers - react - vworld

caporatang 2024. 10. 22. 00:45
반응형

openLayers vworld

이직 준비 중 지도 관련으로 서비스를 개발하던 중 나중에라도 사용할 일이 있을거같고, 구글링해도 정보가 좀 부족해서 기록으로 남겨둔다.

vworld

vworld를 알아본 이유는 실제로 서비스를 운영하는게 목표인 이번 프로젝트에서 구글이나 카카오를 선택하기엔 비용이 걱정되서 다른 openApi 를 알아보던 중, 나라에서 운영하는 지도 서비스가 있어서 사용하게 됐다.
(vworld는 사이트 들어가서 인증 key를 발급 받으면 사용할 수 있다. -> 트래픽 과금 없음! 무료!)

React에서 vworld를 사용하려면 openLayers 라이브러리를 사용해야 한다.

openLayers

openLayers는 지도를 표시하게 해주는 라이브러리다.

useEffect(() => {
    const vworldApiKey = process.env.REACT_APP_VWORLD_API_KEY;

    // Initialize the map
    const map = new Map({
        controls: defaults({ zoom: false, rotate: false }).extend([]),
        layers: [
            new TileLayer({
                properties: { name: 'base-vworld-gray' },
                minZoom: 5,
                maxZoom: 19,
                zIndex: 2,
                preload: Infinity,
                source: new XYZ({
                    url: `https://api.vworld.kr/req/wmts/1.0.0/${vworldApiKey}/Base/{z}/{y}/{x}.png`
                }),
            }),
        ],
        target: 'map',
        view: new View({
            projection: get('EPSG:3857'),
            center: fromLonLat([127.0208657845, 37.5170635319]),
            zoom: 16,
        }),
    });

이렇게 하면 vworld 와 연동해서 지도를 가져올 수 있다.

  • 일단 위에 각 요소의 역할을 살펴보면
    1. new Map : OpenLayers의 Map 객체를 생성, 이 객체는 지도와 관련된 여러 설정을 담고 설정한 값으로 브라우저에 지도를 렌더링
    2. controls : OpenLayers 에서 기본저긍로 제공하는 줌과 회전을 설정한다.
    3. layers : 지도에 기본적으로 렌더링할 레이어 설정
      • new TileLayer : vworld에서 제공하는 타일 기반의 레이어 사용
    4. target : 지도를 렌더링할 HTML 요소 id 지정
    5. new View : 객체의 중심 좌표, 줌, 프로젝션 등등을 설정한다.
      • projection : 프로젝션은 좌표계를 뜻한다. EPSG:3857는 구글지도 등..에서 사용하는 좌표계로 구글 기준으로 일단 작성했다.
      • 다른 좌표 종류는 다음과 같다.
      • 좌표계 EPSG 코드 특징 및 용도
        WGS 84 EPSG:4326 경도/위도를 사용한 전 세계 좌표계. GPS에서 사용.
        Web Mercator EPSG:3857 메르카토르 투영법을 사용, 웹 기반 지도에서 많이 사용.
        Korea 2000 EPSG:5174 / 5179 대한민국 내 지리정보 시스템에서 사용.
        UTM EPSG:326XX 지역별로 분리된 좌표계, 좁은 범위에서 정확한 계산.

결과

이렇게 하면 위와 같은 지도가 출력된다.
근데 내가 하고 싶은건 내가 만든 서버에서 내려준 위치들 (좌표들)의 리스트를 가져다가 마커까지 추가하고 싶었다. 그래서 다음과 같은 코드를 추가했다.


const vectorSource = new VectorSource();
const vectorLayer = new VectorLayer({
    source: vectorSource,
});

map.addLayer(vectorLayer);

propsCoordinateList.forEach((coordinate) => {
    const x = parseFloat(coordinate.mapX);
    const y = parseFloat(coordinate.mapY);

    const markerFeature = new Feature({
        geometry: new Point(fromLonLat([x, y])),
    });

    markerFeature.setStyle(new Style({
        image: new Icon({
            //src: Marker,
            src: Marker,
            scale: 0.2,
        }),
    }));

    vectorSource.addFeature(markerFeature);
});

이 코드의 설명은 다음과 같다.

  1. 벡터 소스를 생성하고, 벡터 레이어에 연결한다.
  2. 벡터 레이어를 만들어둔 지도 객체에 추가한다.
  3. propsCoordinateList (내가 전달한 좌표들)에서 전달된 좌표를 읽어, 각 좌표에 마커를 생성하고 스타일을 설정한 후 벡터 소스에 추가한다.

VectorSource 와 VectorLayer는 다음과 같은 역할을 한다.

  • VectorSource: 지도 위에 여러 벡터 데이터(Features)를 저장하는 객체
  • VectorLayer: VectorSource를 사용하는 레이어로, 벡터 데이터를 지도에 그려주는 역할. 이 레이어를 만들어둔 map 객체에 추가해야 마커가 출력된다.

이렇게 추가하기까지도 오래 걸렸지만, 이렇게 추가해도 마커는 잠깐 표시되고 바로 없어져 버렸다.
무한 삽질에 이어 드디어 해결한 코드는

const map = new Map({
    controls: defaults({ zoom: false, rotate: false }).extend([]),
    layers: [
        new TileLayer({
            properties: { name: 'base-vworld-gray' },
            source: new XYZ({
                url: `https://api.vworld.kr/req/wmts/1.0.0/${vworldApiKey}/Base/{z}/{y}/{x}.png`,
            }),
        }),
    ],
    target: 'map',
    view: new View({
        center: fromLonLat([parseFloat(propsCoordinateList[0].mapX), parseFloat(propsCoordinateList[0].mapY)]),
        zoom: 14,
    }),
});

const vectorSource = new VectorSource();
const vectorLayer = new VectorLayer({
    source: vectorSource,
});

map.addLayer(vectorLayer);

propsCoordinateList.forEach((coordinate) => {
    const x = parseFloat(coordinate.mapX);
    const y = parseFloat(coordinate.mapY);

    const markerFeature = new Feature({
        geometry: new Point(fromLonLat([x, y])),
    });
    markerFeature.setStyle(new Style({
        image: new Icon({
            src: Marker,
            scale:0.1
        })
    }))

    vectorSource.addFeature(markerFeature);
});

잡다한 설정값 minZoom, maxZoom 등등은 빼고 주목해야하는 옵션은 preload : Infinity 이 옵션이다.
preload 옵션은 지도 탐색 시 미리 로드할 타일의 수를 설정하여 사용자가 보기에 더 매끄럽게 움직일 수 있는 옵션이다.
Infinity로 옵션을 주게 된다면 무제한으로 미리 타일을 가져와서 화면에 뿌려버린다. 그래서 마커가 추가되지 않는것으로 추정(?)된다.

그래서 결론적으로 preLoad 옵션을 빼고 실행하면 아래와 같이 마커가 잘 추가되서 출력된다.

  • preload는 따로 설정하지 않으면 default는 2로 설정된다. 좌 우 상 하 모든 방향에 2개의 타일을 가져온다는 뜻이다. 옵션을 주의해서 사용하도록 하자..

반응형