사실 앞서 JSONP에 포스팅한 것은 v-world의 포인트를 기반으로 행정구역을 가져오는 데이터 API를 사용하며 공부한 것이다.
나도 일반적으로 여타 다른 API 처럼 정상적으로 REST API req를 날렸는데 자꾸 CORS 에러가 뜨길래 개발자 툴도 분석해보고 url에 그냥 대입도 해보고 post man 까지 써보고 CORS가 나자 아 이건 무언가 잘 못 됐다 다른 것을 느끼고 Q&A를 뒤지기 시작했는데 아니나 다를까 나와 같은 문제를 겪고있는 사람이 또 있었다.
고맙게도 난 이분 덕에 바로 문제를 해결할 수 있었다.
하지만 난 조금 상황이 달랐던 것이 한 개의 행정구역이 아닌 mission 별로 있는 불특정 다수의 좌표를 행정구역을 찍어줘야했기 때문에 script를 동적으로 생성하고 다 썼으면 바로 지우는 로직까지 생성해야했다.
그럼 구간을 나눠서 일단 script를 동적으로 생성하는 코드부터 보자.
// 이름 그대로 콜백을 생성하는 함수
function generateCallbackName() {
return "jsonp_callback_" + Math.floor(Math.random() * 100000);
}
function jsonpRequest(url, callback) {
const callbackName = generateCallbackName();
window[callbackName] = function (data) {
delete window[callbackName];
// 여기 이 data 부분이 밑에 코드에서 res 부분이다.
callback(data);
};
const script = document.createElement("script");
script.src = `${url}&callback=${callbackName}`;
document.body.appendChild(script);
}
generateCallbackName : 이름 그대로 콜백함수 이름을 생성하는 함수이다. 콜백 이름을 왜 만드냐하면 동작원리와 작동법은 따로 포스팅하였다.
jsonpRequest : 콜백 함수를 동적으로 생성하고, 스크립트 태그를 사용하여 JSONP 요청을 보낸다. 서버는 응답을 생성할 때 지정된 콜백 함수를 호출하며, 클라이언트는 이를 통해 응답 데이터를 받아 처리한다.
사실 데이터 사용하는 것이 더 헷갈릴 수 있다. 앞서 로직을 짤 때 delete window로
즉, JSONP의 특징 중 하나는 비동기적인 자바스크립트 코드 실행이므로,
JSONP 요청을 보낸 후에 데이터를 접근하려면 해당 콜백 함수 내부에서 데이터 처리를 해야 한다는 것이다. JSONP 요청과 콜백 함수 간의 타이밍 문제로 인해 JSONP 요청이 완료되기 전에 데이터를 접근하려고 하면 제대로 동작하지 않을 수 있다는 것이다.
다만 JSONP로 얻은 데이터를 나중에 여러 곳에서 사용하고 싶다면, 콜백 함수 내부에서 전역 변수나 상태 관리 방법을 사용하여 데이터를 저장하고 접근할 수 있다.
하지만 이렇게 하는 것은 데이터의 동기화와 보안에 관한 문제를 야기할 수 있으므로 주의해야 한다.
const mkMissionPolygon = async () => {
// 각 미션 별 좌표를 data로 선언
for (const data of droneMissionData.value) {
// 좌표에 따라 행정구역을 갖고오는 url 선언
const url = `https://api.vworld.kr/req/data?
service=data&request=GetFeature&data=LP_PA_CBND_BUBUN&key=${본인키}&
domain=${본인도메인주소}&crs=EPSG:3857&geomFilter=POINT(${data[1].lon}%20${data[1].lat})`;
// url과 함께 콜백받아서 데이터를 처리할 함수 작성
await jsonpRequest(url, (res) => {
const feature = new Feature({
geometry: new Polygon(
res.response.result.featureCollection.features[0].geometry.coordinates[0]
),
});
// 받은 값을 위에서 feature 생성자로 생성하여 변수에 저장
feature.setProperties({ ...data[1] });
// 벡터 레이어에 넣고
const baseVector = new VectorLayer({
className: "missionPolygonVectorLayer",
source: new VectorSource({
features: [feature],
}),
style: {
"fill-color": "rgba(255, 0, 0, 0.3)",
"stroke-color": "rgba(255, 0, 0, 0.9)",
"stroke-width": 0.5,
},
});
baseVector.setZIndex(500);
// 지도에 레이어 추가함으로써 표시.
vMap.value.addLayer(baseVector);
missionPolygonList.value.push(baseVector);
});
}
};