OpenLayers

[OpenLayers] vue-query를 활용하여 지도 레이어 가져오기

캐럿노트 2025. 2. 18. 14:30

현재 지오서버에 전국 시군구 shp파일이 등록되어있는 상태라 지오서버를 활용해서 시군구 레이어를 받아 오려고 한다.

 

🚀 목표

vue-query를 사용해 지오서버(GeoServer)의 시군구 레이어 불러오기

 

vue-query 설치

vue 환경에서 서버 상태를 간편하게 관리하기 위해 react-query와 동일한 개념의 vue-query를 설치하면 된다. axios도 함께 설치 해준다.

npm i axios @tanstack/vue-query

 

🔗 https://tanstack.com/query/v5/docs/framework/vue/installation

 

Hook 작성

  • vue-query를 사용해 Hook을 작성한다.
  • cqlFilter : WMS나 WFS를 호출할 때 다양한 필터링을 적용할 수 있는 기능인데 여기서는 원하는 지역코드를 필터링하여 레이어를 불러올 수 있다.
// src/composables/useGetMapData.ts

import axios from "axios";
import { useMutation } from "@tanstack/vue-query";

/**
 * 지도 레이어 요청에 필요한 매개변수 타입
 */
export interface IPostMapData {
  layerName: string;
  cqlFilter?: string | null;
}

/**
 * GeoServer의 WFS 데이터를 요청하는 함수
 */
const postMapData = async ({ layerName, cqlFilter = null }: IPostMapData) => {
  const url = "지오서버URL/map/api/map/wfs"; // 지오서버 URL

  const response = await axios.post(url, null, {
    params: {
      service: "WFS",
      typeName: layerName,
      request: "GetFeature",
      version: "1.0.0",
      outputFormat: "application/json",
      srsname: "EPSG:5179",
      cql_filter: cqlFilter,
      crtfckey: "인증키", // 인증키
    },
  });

  return response.data; // AxiosResponse가 아니라 데이터만 반환해야 오류가 발생하지 않음!
};

/**
 * OpenLayers에서 사용할 WFS 데이터를 불러오는 커스텀 훅
 */
export const usePostMapData = () => {
  return useMutation({
    mutationFn: postMapData,
  });
};

 

데이터 호출

cqlFilter에 encodeURI(`CD LIKE '${11}%'`) 를 넣었는데 법정동 코드가 11로 시작하는 지역(서울특별시)만 필터링 요청한다는 뜻이다.

서울특별시 법정동 코드는 11로 시작하는 것을 알 수 있다.

🔗 행정표준코드관리시스템 - 법정동코드목록조회
https://www.code.go.kr/stdcode/regCodeL.do

 

// src/components/MapView.vue

<script setup>
import { onMounted, watch } from "vue";
import { usePostMapData } from "../composables/useGetMapData";
import { useMap } from "../composables/useMap";

const { mapContainer, mapInstance } = useMap();

const { mutate, data } = usePostMapData();

// 컴포넌트 마운트 시 POST 요청 실행
onMounted(() => {
  mutate(
    { layerName: "레이어이름", cqlFilter: encodeURI(`CD LIKE '${11}%'`) },
    {
      onSuccess: (geoJsonData) => {
        console.log("✅ 데이터 받아옴:", geoJsonData);
      },
      onError: (error) => {
        console.error("❌ 데이터 요청 실패:", error);
      },
    }
  );
});
</script>

<template>
  <div class="map-wrapper">
    <div ref="mapContainer" class="map-container"></div>
  </div>
</template>

<style scoped>
.map-wrapper {
  width: 100vw;
  height: 100vh; /* 부모 요소의 높이를 명확하게 설정 */
}

.map-container {
  width: 100%;
  height: 100%;
}
</style>

콘솔에서 받아온 데이터를 확인할 수 있다.

 

지도에 레이어 추가하기

받아온 데이터를 사용해 vectorLayer를 반환하는 함수를 만들었다.

// src/composables/useMap.ts

import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import { GeoJSON } from "ol/format";

export const createLayer = (mapData: any) => {
  const vectorLayer = new VectorLayer({
    source: new VectorSource({
      features: new GeoJSON().readFeatures(mapData, {
        dataProjection: "EPSG:5179",
        featureProjection: "EPSG:5179",
      }),
    }),
  });
  return vectorLayer;
};
  • createLayer 함수를 활용해 지도에 레이어를 추가한다.
  • watch를 사용해 mutate에서 데이터를 불러올때를 감지한다.
// src/components/MapView.vue

<script setup>
import { onMounted, watch } from "vue";
import { usePostMapData } from "../composables/useGetMapData";
import { createLayer, useMap } from "../composables/useMap";

const { mapContainer, mapInstance } = useMap();

const { mutate, data } = usePostMapData();

// 컴포넌트 마운트 시 POST 요청 실행
onMounted(() => {
  mutate(
    { layerName: "레이어이름", cqlFilter: encodeURI(`CD LIKE '${11}%'`) },
    {
      onSuccess: (geoJsonData) => {
        console.log("✅ 데이터 받아옴:", geoJsonData);
      },
      onError: (error) => {
        console.error("❌ 데이터 요청 실패:", error);
      },
    }
  );
});

// 데이터를 가져온 후 OpenLayers에 반영
watch(data, (newData) => {
  if (!newData || !mapInstance.value) return;

  const seoulLayer = createLayer(newData);
  mapInstance.value.addLayer(seoulLayer);
});
</script>

<template>
  <div class="map-wrapper">
    <div ref="mapContainer" class="map-container"></div>
  </div>
</template>

<style scoped>
.map-wrapper {
  width: 100vw;
  height: 100vh; /* 부모 요소의 높이를 명확하게 설정 */
}

.map-container {
  width: 100%;
  height: 100%;
}
</style>

서울특별시 시군구 레이어가 지도에 추가된 것을 확인할 수 있다.