목적 : 서울열린데이터광장의 오픈 api를 통해 서울 시내 축구장의 위도와 경도를 가져오게 되었다. 사용자의 경기날짜와 시간에 이 좌표의 일기 예보 정보를 가져오고자 한다.
기상청 단기예보 오픈 api :https://www.data.go.kr/data/15084084/openapi.do
공공데이터포털에 회원 가입하여 마이페이지에서 API 인증키를 발급 받았다.

encoding된 인증키를 복사하여 사용하면 된다.
다시 단기예보 오픈 api 사이트에서 [활용신청] 버튼을 눌러 슝슝 작성해주면 사용준비 끝
사용시 참고문서의 워드파일을 꼭 참고하자
요청 메시지 형태는 이렇다
http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=인증키&numOfRows=10&pageNo=1&base_date=20210628&base_time=0500&nx=55&ny=127
문제는 날씨 카테고리들이 예보시간대마다 하나씩 하나의 row로 담겨온다.
numOfRows를 290개로 설정하면 하루 모든 시간대의 정보를 page마다 나눌 수 있다. 그리고 base_time을 2300으로 설정하면 base_date의 다음날은 pageNo=1에 담기고 모레는 pageNo=2이 담긴다.
또한, 요청 url에 담는 nx와 ny는 위도, 경도가 아니다. 따라서 우리는 위도, 경도를 api에서 요구하는 좌표계로 변환하여 요청해야한다.
그리고 단기예보는 3일이내의 날씨만 확인 가능하다.
(그 이후의 예보를 얻으려면 중기예보 api를 사용하면 될 듯)
weatherAPI: (gamedate, gametime, inputx, inputy) => {
const nx = latLngToGrid(inputx, inputy).x;
const ny = latLngToGrid(inputx, inputy).y;
const pageNo = checkpageNo(gamedate, gametime);
const url = createUrl(pageNo, nx, ny);
return new Promise((resolve, reject) => {
if (pageNo == -1)
resolve('DateOverError'); //3일 이후의 날씨 요구시 발생 에러 (DateOverError)
http.get(url, (res) => {
let json = "";
res.on("data", (chunk) => {
json += chunk;
});
res.on("end", () => {
const result = extractWeatherInfoByDateTime(gametime, JSON.parse(json));
resolve(result == -1 ? 'ApiError' : result);
});
});
});
}
latLngToGrid 함수는 위도 경도를 좌표계로 바꿔주는 코드
여기에서 얻을 수 있다 !
function checkpageNo(gamedate) {
switch(gamedate - getTime.getDate()){
//오늘인 경우
case 0:
return 1;
//내일인 경우
case 1:
return 2;
//모레인 경우
case 2:
return 3;
//그 이상인 경우
default:
return -1;
}
}
function createUrl (pageNo, nx, ny) {
const base_date = getTime.getDate()-1
return `http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=${weather_key}&numOfRows=290&pageNo=${pageNo}&base_date=${base_date}&base_time=2300&nx=${nx}&ny=${ny}&dataType=JSON`
}
예보를 원하는 날을 확인 할 수있는 pageNo를 주는 함수,
그리고 url을 생성해주는 함수, 발표일자(base_date)와 발표시각(base_time)은 어제의 23시로 설정하여 오늘 날씨부터 받아보도록 했다. 그리고 xml이 아닌 json으로 받아쓴다.
(getTime.getDate는 현재 날짜를 가져오는 코드를 외부모듈로 두었음)
getTime.js
module.exports = {
getDate: () => {
const year = kr_curr.getFullYear();
const month = (kr_curr.getMonth() + 1).toString().padStart(2, '0');
const day = kr_curr.getDate().toString().padStart(2, '0');
const nowdate = `${year}${month}${day}`;
return nowdate;
}
json에서 원하는 정보를 추출하는 함수
function extractWeatherInfoByDateTime (gametime, json) {
var time = gametime.endsWith('30') ? gametime.slice(0,2) +'00' : gametime;
const weatherType = ['TMP', 'POP', 'PCP', 'fcstDate', 'fcstTime']
// TMP : 1시간 기온
// POP : 강수확률
// PCP : 1시간 강수량
// fcstDate : 예측일자
// fcstTime : 예측시간
var weatherObj = {};
try {
const route = json.response.body.items.item;
for (var i=0; i<route.length; i++) {
if (route[i].fcstTime == time) {
weatherObj.fcstDate = route[i].fcstDate;
weatherObj.fcstTime = route[i].fcstTime;
const category = route[i].category;
if (weatherType.includes(category))
weatherObj[category] = route[i].fcstValue;
}
}
return weatherObj;
} catch (err) {
return -1;
}
}
페이지에 담긴 그 날짜의 날씨 정보 중 원하는 시간 대의
원하는 날씨 카테고리만의 정보를 가져와 객체에 담아 return
(카테고리는 단기예보api 사용가이드 참고)
찾고자 하는 시간(gmaetime)이 30분 단위로 넘기고 있는데 기상청에서는 정시에의 예보를 넘겨주기 때문에 30을 짤라 내림 해버렸다.

어잇 잘 가져온다!
이제 axios를 사용하여 요청 보내도록 코드 수정해야지 !!
require("dotenv").config({ path: __dirname+"/../config/.env" });
const http = require('http');
const getTime = require("../modules/getTime")
const latLngToGrid = require("../modules/latLngToGrid")
const weather_key = process.env.WEATHER_KEY;
function checkpageNo(gamedate) {
switch(gamedate - getTime.getDate()){
//오늘인 경우
case 0:
return 1;
//내일인 경우
case 1:
return 2;
//모레인 경우
case 2:
return 3;
//그 이상인 경우
default:
return -1;
}
}
function createUrl (pageNo, nx, ny) {
const base_date = getTime.getDate()-1
return `http://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=${weather_key}&numOfRows=290&pageNo=${pageNo}&base_date=${base_date}&base_time=2300&nx=${nx}&ny=${ny}&dataType=JSON`
}
function extractWeatherInfoByDateTime (gametime, json) {
var time = gametime.endsWith('30') ? gametime.slice(0,2) +'00' : gametime;
const weatherType = ['TMP', 'POP', 'PCP', 'fcstDate', 'fcstTime']
// TMP : 1시간 기온
// POP : 강수확률
// PCP : 1시간 강수량
// fcstDate : 예측일자
// fcstTime : 예측시간
var weatherObj = {};
try {
const route = json.response.body.items.item;
for (var i=0; i<route.length; i++) {
if (route[i].fcstTime == time) {
weatherObj.fcstDate = route[i].fcstDate;
weatherObj.fcstTime = route[i].fcstTime;
const category = route[i].category;
if (weatherType.includes(category))
weatherObj[category] = route[i].fcstValue;
}
}
return weatherObj;
} catch (err) {
return -1;
}
}
module.exports = {
weatherAPI: (gamedate, gametime, inputx, inputy) => {
const nx = latLngToGrid(inputx, inputy).x;
const ny = latLngToGrid(inputx, inputy).y;
const pageNo = checkpageNo(gamedate, gametime);
const url = createUrl(pageNo, nx, ny);
return new Promise((resolve, reject) => {
if (pageNo == -1)
resolve('DateOverError'); //3일 이후의 날씨 요구시 발생 에러 (DateOverError)
http.get(url, (res) => {
let json = "";
res.on("data", (chunk) => {
json += chunk;
});
res.on("end", () => {
const result = extractWeatherInfoByDateTime(gametime, JSON.parse(json));
resolve(result == -1 ? 'ApiError' : result);
});
});
});
}
}
공감하며 읽었습니다. 좋은 글 감사드립니다.