안녕하세요
오늘은 React로 공공데이터 API 호출하여 날씨 어플리케이션 만드는 방법을 알려드리겠습니다.
1.react 설치 명령어
npx create-react-app .
2.앱 실행
npm start
->안될시, cmd창으로
3.공공데이터포털 단기예보 or 중기예보 활용 신청
회원가입 후 개인 key 발급 하시고 권한 신청하시면 자동으로 권한을 얻습니다.

예보기간과 구역을 시공간적으로 세분화하여 발표하는 자료로 3시간 간격으로 발표합니다. 단기예보는 예보시점부터 3일 이내 기간에 대하여 행하는 예보로, 전국의 읍,면,동 단위로 상세한 날씨를 예보하기 위하여 2시부터 3시간 간격으로 일 8회 발표하며, 예보시간은 +4시간부터 +58~67시간까지 생산됩니다.
www.data.go.kr

사진 설명을 입력하세요.
4.json 형식으로 데이터 받기
저는 단기예보를 예로 설명드리겠습니다.

사진 설명을 입력하세요.
클릭하셔서 본인 api 키값 넣고 오늘날짜로 조회하시면 데이터가 갖고 와지는 것을 확인하실 수 있습니다.

사진 설명을 입력하세요.
항목값 통해 값 불러오기
SKY-하늘상태
POP-강수확률
REH-습도
TMX-최고기온
TMN-최저기온
5.indext.html에서 로직 전개
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- <link rel="stylesheet" type="text/css" href="styles.css"> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>날씨데이터 API</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
/* font-family: 'poppins', sans-serif; */
font-family: 'Noto Sans KR', sans-serif;
width: auto;
}
.outBox {
color: white;
background-color: #5262F1;
width: 100%;
height: 100%;
text-align: center;
}
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 20px 10%;
background: transparent;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 100;
}
body {
color: white;
background: #081b29;
}
.carwash {
width: 400px;
height: 200px;
}
.result {
font-size: 20px;
color: white;
text-align: center;
}
.date-box1 {
border: 1px solid #000;
padding: 10px;
margin: 10px;
background-color: #18a9ec;
float: left;
width: 30%;
}
.date-box2 {
border: 1px solid #000;
padding: 10px;
margin: 10px;
background-color: #f55da4;
float: left;
width: 30%;
}
.date-box3 {
border: 1px solid #000;
padding: 10px;
margin: 10px;
background-color: #ba29f3;
float: left;
width: 30%;
}
</style>
</head>
<body>
<div class="outBox">
<div id="root"></div>
<h1>언제 세차하기 좋을까?</h1>
<h2>날씨 API 연동한 일기 예보 어플리케이션</h2>
<!-- <p class="result">오늘 날짜: <span id="todayDate"></span></p> -->
<img class="carwash" src="./carwash.png" alt="">
<div class="date-info">
<!-- <h3>날짜: <span class="date">날짜 데이터</span></h3> -->
<!-- <ul class="info-list"></ul> -->
</div>
<p class="result">
</p>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
//오늘 날짜 갖고오기
const date = new Date();
const year = date.getFullYear();
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
const hour = ('0' + date.getHours()).slice(-2);
const minute = ('0' + date.getMinutes()).slice(-2);
const initDate = `${year}${month}${day}`;
console.log(initDate);
const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
const content1 = `${monthNames[parseInt(month) - 1]} ${parseInt(day)}일 ${hour}시 ${minute}분`;
$(".result").text(content1);
$.getJSON(`https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=7wBLp26vUkaDcY0jNArxs2YPO48VzTk2zTuhj2%2B9xjACQaxkFxQ%2F1sdyxhP%2BO6U0v4ipEvKlURhwTaNELVd6tg%3D%3D&pageNo=1&numOfRows=1000&dataType=JSON&base_date=${initDate}&base_time=0500&nx=61&ny=120`,
function (data) {
console.log(data.response.body.items.item);
const items = data.response.body.items.item;
const skyValues = {}; // 시간대별 SKY 값을 저장할 객체
const popValues = {}; // 시간대별 POP 값을 저장할 객체
const rehValues = {}; // 시간대별 REH 값을 저장할 객체
const tmxValues = {}; // 시간대별 TMX 값을 저장할 객체
const tmnValues = {}; // 시간대별 TMN 값을 저장할 객체
// 날짜 정보 객체 생성
for (const item of items) {
const category = item.category;
const fcstValue = item.fcstValue;
const fcstTime = item.fcstTime;
const fcstDate = item.fcstDate;
// 날씨 정보를 해당 카테고리에 따라 분류
if (category === 'SKY') {
const datetime = fcstDate + fcstTime;
skyValues[datetime] = getSkyDescription(fcstValue);
} else if (category === 'POP') {
const datetime = fcstDate + fcstTime;
popValues[datetime]
6.app.js 에서 로직 전개
import './App.css';
import React, { useEffect } from 'react';
function App() {
useEffect(() => {
// 오늘 날짜 갖고오기
const date = new Date();
const year = date.getFullYear();
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
// const hour = ('0' + date.getHours()).slice(-2);
// const minute = ('0' + date.getMinutes()).slice(-2);
const initDate = `${year}${month}${day}`;
console.log(initDate);
// API 호출 및 데이터 처리
fetch(`https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=7wBLp26vUkaDcY0jNArxs2YPO48VzTk2zTuhj2%2B9xjACQaxkFxQ%2F1sdyxhP%2BO6U0v4ipEvKlURhwTaNELVd6tg%3D%3D&pageNo=1&numOfRows=1000&dataType=JSON&base_date=${initDate}&base_time=0500&nx=61&ny=120`)
.then(response => response.json())
.then(data => {
const items = data.response.body.items.item;
const skyValues = {};
const popValues = {};
const rehValues = {};
const tmxValues = {};
const tmnValues = {};
for (const item of items) {
const category = item.category;
const fcstValue = item.fcstValue;
const fcstTime = item.fcstTime;
const fcstDate = item.fcstDate;
if (category === 'SKY') {
const datetime = fcstDate + fcstTime;
skyValues[datetime] = getSkyDescription(fcstValue);
} else if (category === 'POP') {
const datetime = fcstDate + fcstTime;
popValues[datetime] = fcstValue;
} else if (category === 'REH') {
const datetime = fcstDate + fcstTime;
rehValues[datetime] = fcstValue;
} else if (category === 'TMX') {
const datetime = fcstDate + fcstTime;
tmxValues[datetime] = fcstValue;
} else if (category === 'TMN') {
const datetime = fcstDate + fcstTime;
tmnValues[datetime] = fcstValue;
}
}
const dateInfoMap = {};
for (const datetime in skyValues) {
if (datetime.endsWith("0600") || datetime.endsWith("1200") || datetime.endsWith("1800")) {
const monthVal = datetime.slice(4, 6);
const dayVal = datetime.slice(6, 8);
const timeVal = datetime.slice(8, 10);
const datePart = `${monthVal}월 ${dayVal}일`;
const skyInfo = `${timeVal}시 - SKY: ${skyValues[datetime]}`;
const popInfo = `강수확률: ${popValues[datetime]}%`;
const rehInfo = `습도: ${rehValues[datetime]}%`;
const tmxInfo = `최고기온: ${tmxValues[datetime]}°C`;
const tmnInfo = `최저기온: ${tmnValues[datetime]}°C`;
if (!dateInfoMap[datePart]) {
dateInfoMap[datePart] = [];
}
dateInfoMap[datePart].push(skyInfo);
dateInfoMap[datePart].push(popInfo);
dateInfoMap[datePart].push(rehInfo);
dateInfoMap[datePart].push(tmxInfo);
dateInfoMap[datePart].push(tmnInfo);
}
}
console.log(dateInfoMap);
});
}, []); // 빈 배열은 컴포넌트가 로드될 때 한 번만 실행
function getSkyDescription(skyValue) {
switch (skyValue) {
case "1":
return "맑음";
case "3":
return "구름많음";
case "4":
return "흐림";
default:
return "알 수 없음";
}
}
return (
<div className="outBox">
<div id="root"></div>
<h1>언제 세차하기 좋을까?</h1>
<h2>날씨 API 연동한 일기 예보 어플리케이션</h2>
<div className="date-info">
{/* 날씨 정보를 여기에 렌더링할 것입니다. */}
</div>
<p className="result"></p>
</div>
);
}
export default App;
7.중기예보 데이터 활용
중기 육상예보 조회
다음 데이터 값을 갖고 오겠습니다.
rnSt3Am - 3일 후 오전 강수 확률
rnSt4Am - 4일 후 오전 강수 확률
rnSt5Am - 5일 후 오전 강수 확률
rnSt6Am - 6일 후 오전 강수 확률
rnSt7Am - 7일 후 오전 강수 확률
wf3Am - 3일 후 오전 날씨예보
wf4Am - 4일 후 오전 날씨예보
wf5Am - 5일 후 오전 날씨예보
wf6Am - 6일 후 오전 날씨예보
wf7Am - 7일 후 오전 날씨예보

사진 설명을 입력하세요.
id가 단기예보인 요소를 찾아서 HTML 엘리먼트 추가 appendChild는 새로운 자식 엘리먼트를 추가 그런 다음 dateBox를 short-term 에 추가
index.html
head 및 body 부분
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- <link rel="stylesheet" type="text/css" href="styles.css"> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>날씨데이터 API</title>
</head>
<body>
<div class="outBox">
<div id="root"></div>
<h1>언제 세차하기 좋을까?</h1>
<h2>날씨 API 연동한 일기 예보 어플리케이션</h2>
<!-- <p class="result">오늘 날짜: <span id="todayDate"></span></p> -->
<img class="carwash" src="./carwash.png" alt="">
<div id="short-term-forecast"></div>
<div id="mid-term-forecast"></div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"
integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
// 단기예보가 상단에 출력되도록 DOMContentLoaded 이벤트 핸들러 추가
단기예보 값
// 단기예보가 상단에 출력되도록 DOMContentLoaded 이벤트 핸들러 추가
document.addEventListener("DOMContentLoaded", function () {
//오늘 날짜 갖고오기
const date = new Date();
const year = date.getFullYear();
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
const hour = ('0' + date.getHours()).slice(-2);
const minute = ('0' + date.getMinutes()).slice(-2);
const initDate = `${year}${month}${day}`;
const initDate1 = initDate + '0600';
// console.log(initDate);
// console.log(initDate1);
const monthNames = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월'];
const content1 = `${monthNames[parseInt(month) - 1]} ${parseInt(day)}일 ${hour}시 ${minute}분`;
$(".result").text(content1);
$.getJSON(`https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=7wBLp26vUkaDcY0jNArxs2YPO48VzTk2zTuhj2%2B9xjACQaxkFxQ%2F1sdyxhP%2BO6U0v4ipEvKlURhwTaNELVd6tg%3D%3D&pageNo=1&numOfRows=1000&dataType=JSON&base_date=${initDate}&base_time=0500&nx=61&ny=120`,
function (data) {
console.log(data.response.body.items.item);
const items = data.response.body.items.item;
const skyValues = {}; // 시간대별 SKY 값을 저장할 객체
const popValues = {}; // 시간대별 POP 값을 저장할 객체
const rehValues = {}; // 시간대별 REH 값을 저장할 객체
const tmxValues = {}; // 시간대별 TMX 값을 저장할 객체
const tmnValues = {}; // 시간대별 TMN 값을 저장할 객체
// 날짜 정보 객체 생성
for (const item of items) {
const category = item.category;
const fcstValue = item.fcstValue;
const fcstTime = item.fcstTime;
const fcstDate = item.fcstDate;
// 날씨 정보를 해당 카테고리에 따라 분류
if (category === 'SKY') {
const datetime = fcstDate + fcstTime;
skyValues[datetime] = getSkyDescription(fcstValue);
} else if (category === 'POP') {
const datetime = fcstDate + fcstTime;
popValues[datetime] = fcstValue;
} else if (category === 'REH') {
const datetime = fcstDate + fcstTime;
rehValues[datetime] = fcstValue;
} else if (category === 'TMX') {
const datetime = fcstDate + fcstTime;
tmxValues[datetime] = fcstValue;
} else if (category === 'TMN') {
const datetime = fcstDate + fcstTime;
tmnValues[datetime] = fcstValue;
}
}
// 결과 출력
let content2 = content1 + "<br>시간대별 날씨(0600, 1200, 1800):<br>";
// 날짜별 정보를 담을 객체
const dateInfoMap = {};
// 20231026 값을 갖고 있는 baseDate값 날씨 정보 가공
for (const datetime in skyValues) {
if (datetime.endsWith("0600") || datetime.endsWith("1200") || datetime.endsWith("1800")) {
const monthVal = datetime.slice(4, 6);
const dayVal = datetime.slice(6, 8);
const timeVal = datetime.slice(8, 10);
const datePart = `${monthVal}월 ${dayVal}일`;
const skyInfo = `${timeVal}시 - SKY: ${skyValues[datetime]}`;
const popInfo = `강수확률: ${popValues[datetime]}%`;
const rehInfo = `습도: ${rehValues[datetime]}%`;
const tmxInfo = `최고기온: ${tmxValues[datetime]}°C`;
const tmnInfo = `최저기온: ${tmnValues[datetime]}°C`;
// dateInfoMap에 날짜별 정보를 추가
if (!dateInfoMap[datePart]) {
dateInfoMap[datePart] = [];
}
dateInfoMap[datePart].push(skyInfo);
dateInfoMap[datePart].push(popInfo);
dateInfoMap[datePart].push(rehInfo);
dateInfoMap[datePart].push(tmxInfo);
dateInfoMap[datePart].push(tmnInfo);
content2 += `${datePart} ${timeVal} - ${skyInfo}<br>`;
}
}
// 날씨정보 화면에 표시
let boxCount = 1;
for (const datePart in dateInfoMap) {
const dateBox = document.createElement('div');

사진 설명을 입력하세요.
undefined 값이 있어 삼항연산자로 값이 없을 시 삭제
$.getJSON(`https://apis.data.go.kr/1360000/VilageFcstInfoService_2.0/getVilageFcst?serviceKey=7wBLp26vUkaDcY0jNArxs2YPO48VzTk2zTuhj2%2B9xjACQaxkFxQ%2F1sdyxhP%2BO6U0v4ipEvKlURhwTaNELVd6tg%3D%3D&pageNo=1&numOfRows=1000&dataType=JSON&base_date=${initDate}&base_time=0500&nx=61&ny=120`,
function (data) {
console.log(data.response.body.items.item);
const items = data.response.body.items.item;
const skyValues = {}; // 시간대별 SKY 값을 저장할 객체
const popValues = {}; // 시간대별 POP 값을 저장할 객체
const rehValues = {}; // 시간대별 REH 값을 저장할 객체
const tmxValues = {}; // 시간대별 TMX 값을 저장할 객체
const tmnValues = {}; // 시간대별 TMN 값을 저장할 객체
// 날짜 정보 객체 생성
for (const item of items) {
const category = item.category;
const fcstValue = item.fcstValue;
const fcstTime = item.fcstTime;
const fcstDate = item.fcstDate;
// 날씨 정보를 해당 카테고리에 따라 분류
if (category === 'SKY') {
const datetime = fcstDate + fcstTime;
skyValues[datetime] = getSkyDescription(fcstValue);
} else if (category === 'POP') {
const datetime = fcstDate + fcstTime;
popValues[datetime] = fcstValue;
} else if (category === 'REH') {
const datetime = fcstDate + fcstTime;
rehValues[datetime] = fcstValue;
} else if (category === 'TMX') {
const datetime = fcstDate + fcstTime;
tmxValues[datetime] = fcstValue;
} else if (category === 'TMN') {
const datetime = fcstDate + fcstTime;
tmnValues[datetime] = fcstValue;
}
}
// 결과 출력
let content2 = content1 + "<br>시간대별 날씨(0600, 1200, 1800):<br>";
// 날짜별 정보를 담을 객체
const dateInfoMap = {};
// 20231026 값을 갖고 있는 baseDate값 날씨 정보 가공
for (const datetime in skyValues) {
if (datetime.endsWith("0600") || datetime.endsWith("1200") || datetime.endsWith("1800")) {
const monthVal = datetime.slice(4, 6);
const dayVal = datetime.slice(6, 8);
const timeVal = datetime.slice(8, 10);
const datePart = `${monthVal}월 ${dayVal}일`;
const skyInfo = `${timeVal}시 - 날씨: ${skyValues[datetime]}`;
const tmxInfo = tmxValues[datetime] ? `최고: ${tmxValues[datetime]}°C` : "";
const tmnInfo = tmxValues[datetime] ? `최저: ${tmnValues[datetime]}°C` : "";
const rehInfo = `습도: ${rehValues[datetime]}%`;
const popInfo = `강수확률: ${popValues[datetime]}%`;
// 값이 undefined가 아닌 경우에만 추가
if (popValues[datetime] !== '') {
// dateInfoMap에 날짜별 정보를 추가
if (!dateInfoMap[datePart]) {
dateInfoMap[datePart] = [];
}
dateInfoMap[datePart].push(skyInfo);
dateInfoMap[datePart].push(tmxInfo);
dateInfoMap[datePart].push(tmnInfo);
dateInfoMap[datePart].push(rehInfo);
dateInfoMap[datePart].push(popInfo);
content2 += `${datePart} ${timeVal} - ${skyInfo}<br>`;
}
}
}
// 날씨정보 화면에 표시
let boxCount = 1;
for (const datePart in dateInfoMap) {
const dateBox = document.createElement('div');
dateBox.className = `date-box${boxCount}`;
boxCount++;
dateBox.innerHTML = `<h3>${datePart}</h3>`;
const infoList = document.createElement('ul');
infoList.className = 'info-list';
for (const info of dateInfoMap[datePart]) {
const infoItem = document.createElement('div');
infoItem.innerHTML = info;
infoList.appendChild(infoItem);
}
dateBox.appendChild(infoList);
// id가 단기예보인 요소를 찾아서 HTML 엘리먼트
중기예보 객체화
// 중기예보 API 호출
$.getJSON(`https://apis.data.go.kr/1360000/MidFcstInfoService/getMidLandFcst?serviceKey=7wBLp26vUkaDcY0jNArxs2YPO48VzTk2zTuhj2%2B9xjACQaxkFxQ%2F1sdyxhP%2BO6U0v4ipEvKlURhwTaNELVd6tg%3D%3D&pageNo=1&numOfRows=10&dataType=JSON®Id=11B00000&tmFc=202310270600`, function (midTermData) {
const items2 = midTermData.response.body.items.item[0];
const midTermForecastData = {}; // midTermForecastData 객체를 먼저 정의
// 중기예보 데이터를 객체에 추가
for (let i = 3; i <= 7; i++) {
midTermForecastData[i] = {
whAAm: items2[`wh${i}AAm`],
wfAm: items2[`wf${i}Am`],
rnStAm: items2[`rnSt${i}Am`],
wfPm: items2[`wf${i}Pm`]
};
}
// 중기예보 데이터 출력 함수
function displayMidTermForecast(data) {
const container = document.querySelector(".mid-term-forecast");
for (const day in data) {
const dateBox = document.createElement('div');
dateBox.className = 'date-box';
const dateHeader = document.createElement('h3');
dateHeader.textContent = `${day}일 후`;
dateBox.appendChild(dateHeader);
const infoList = document.createElement('ul');
infoList.className = 'info-list';
for (const key in data[day]) {
const infoItem = document.createElement('div');
infoItem.textContent = `${key}: ${data[day][key]}`;
infoList.appendChild(infoItem);
}
dateBox.appendChild(infoList);
container.appendChild(dateBox);
}
}
// 중기예보 데이터 출력
displayMidTermForecast(midTermForecastData);
});
다음과 같은 화면을 출력할 수 있습니다.

사진 설명을 입력하세요.
8.현재 위치 기반으로 좌표값 격자 변환한 다음 API호출하여 데이터 출력
단기 예보 (nx, ny)와 중기 예보 (regId) 값을 가져오기

위경도 데이터형식 : 위도, 경도 37.579871128849334, 126.98935225645432 35.101148844565955, 129.02478725562108 33.500946412305076, 126.54663058817043 1: 2: 3: 위경도 -> GridXY 격자(Grid) XY 데이터형식 : gridX, gridY 1: 위경도 <- GridXY Reference http://www.kma.go.kr/weather/forecast/digital_forecast.jsp 변환 소스 출처는 기상청 홈페이지 내에 ...
fronteer.kr
1.HTML5의 Geolocation API를 사용하여 사용자의 현재 위치를 가져옵니다.
2.가져온 위치 정보를 이용해 단기 예보와 중기 예보 API에 요청을 보냅니다.
3.API 응답을 처리하여 날씨 정보를 화면에 표시합니다.
감사합니다.
'React' 카테고리의 다른 글
React boilerplate dom 설치 방법 (0) | 2023.10.20 |
---|---|
Vscode에서 React 설치 및 실행1 (0) | 2023.10.19 |
nodeJS 설치 및 vscode에서 react 실행하는 법 (0) | 2023.10.13 |