게시글, 궁금해요 질문/답변을 업로드할 때 현재 위치와 날씨가 같이 저장된다.
오늘은 해당 기능을 구현해보기 위해 여러 가지 방법을 알아보았다.
1) 현재 위치
현재 위치는 geolocation을 사용하여 비교적 쉽게 구현할 수 있었다.
https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API/Using_the_Geolocation_API
import React, { Component } from 'react';
class GeolocationKakaoAPI extends Component {
state = {
latitude: null,
longitude: null,
address: '',
error: null,
};
componentDidMount() {
if (navigator.geolocation) {
// Geolocation API를 지원하는 경우
navigator.geolocation.getCurrentPosition(
this.handleSuccess,
this.handleError
);
} else {
// Geolocation API를 지원하지 않는 경우
this.setState({ error: 'Geolocation is not supported in this browser.' });
}
}
handleSuccess = (position) => {
const { latitude, longitude } = position.coords;
this.setState({ latitude, longitude });
};
handleError = (error) => {
this.setState({ error: error.message });
};
render() {
const { latitude, longitude, error } = this.state;
console.log(this.state);
return (
<div>
{error ? (
<p>Error: {error}</p>
) : (
<p>
Latitude: {latitude}, Longitude: {longitude}
<br />
</p>
)}
</div>
);
}
}
export default GeolocationKakaoAPI;
2) 위경도 주소 변환
위도와 경도값을 가져왔으면 XX시 XX구와 같은 행정구역으로 변환을 해야 한다.
이 부분은 kakao api를 사용했다.
https://developers.kakao.com/docs/latest/ko/local/dev-guide#coord-to-district
import React, { Component } from 'react';
class GeolocationKakaoAPI extends Component {
state = {
latitude: null,
longitude: null,
address: '',
error: null,
};
componentDidMount() {
if (navigator.geolocation) {
// Geolocation API를 지원하는 경우
navigator.geolocation.getCurrentPosition(
this.handleSuccess,
this.handleError
);
} else {
// Geolocation API를 지원하지 않는 경우
this.setState({ error: 'Geolocation is not supported in this browser.' });
}
}
handleSuccess = (position) => {
const { latitude, longitude } = position.coords;
this.setState({ latitude, longitude });
// Kakao 지도 API로 Reverse Geocoding 요청
this.getKakaoAddress(latitude, longitude);
};
handleError = (error) => {
this.setState({ error: error.message });
};
getKakaoAddress = (latitude, longitude) => {
const KAKAO_API_KEY = 'key'; // 본인의 Kakao API 키로 대체
// Kakao 지도 API로 Reverse Geocoding 요청
fetch(
`https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x=${longitude}&y=${latitude}`,
{
headers: {
Authorization: `KakaoAK ${KAKAO_API_KEY}`,
},
}
)
.then((response) => response.json())
.then((data) => {
const address =
data.documents.length > 0 ? data.documents[0].address_name : 'Unknown';
this.setState({ address });
})
.catch((error) => {
this.setState({ error: 'Failed to get address from Kakao API.' });
});
};
render() {
const { latitude, longitude, address, error } = this.state;
return (
<div>
{error ? (
<p>Error: {error}</p>
) : (
<p>
Latitude: {latitude} <br/>Longitude: {longitude}
<br />
Address: {address}
</p>
)}
</div>
);
}
}
export default GeolocationKakaoAPI;
address_name을 모두 출력했으므로 시/구/동까지 나왔는데 아마 해당 프로젝트에선 시/구까지만 사용할 것 같다.
3) 기상청 API
현재 위치를 기반으로 한 날씨를 가져오기 위해 기상청 API를 사용했다.
https://www.data.go.kr/data/15084084/openapi.do
공공 API를 사용하려면 공공데이터포털에 가입을 한 후 활용신청을 하면 바로 사용이 가능하다.
출력되는 JSON의 형식을 보기 위해 예시를 확인했다.
{
"response": {
"header": {
"resultCode": "00",
"resultMsg": "NORMAL_SERVICE"
},
"body": {
"dataType": "JSON",
"items": {
"item": [
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "PTY",
"nx": 55,
"ny": 127,
"obsrValue": "0"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "REH",
"nx": 55,
"ny": 127,
"obsrValue": "81"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "RN1",
"nx": 55,
"ny": 127,
"obsrValue": "0"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "T1H",
"nx": 55,
"ny": 127,
"obsrValue": "28.8"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "UUU",
"nx": 55,
"ny": 127,
"obsrValue": "2.9"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "VEC",
"nx": 55,
"ny": 127,
"obsrValue": "258"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "VVV",
"nx": 55,
"ny": 127,
"obsrValue": "0.6"
},
{
"baseDate": "20230726",
"baseTime": "1700",
"category": "WSD",
"nx": 55,
"ny": 127,
"obsrValue": "3"
}
]
},
"pageNo": 1,
"numOfRows": 1000,
"totalCount": 8
}
}
}
입력한 X좌표, Y좌표 위치의 날씨 정보 기준이 되는 날짜, 시간을 확인할 수 있다.
category는 어떠한 정보인지 알려주고 obsrValue에 해당 정보의 값이 나와있다.
정확이 어떤 값을 의미하는지 알아보려면 가이드의 워드 파일을 확인하면 된다.
내가 원하는 초단기실황의 category에는 기온, 강수량, 습도, 풍속등 여러 정보가 나온다.
하지만 해당 기상청 API를 사용하려면 위도 경도 값을 input 하는 것이 아닌 X좌표, Y좌표를 입력해야 한다.
만약 위도와 경도를 그대로 사용하게 되면 obsrValue값이 -999로 출력된다.
4) 위도경도 좌표계 변환
geolocation를 이용해 위도와 경도를 받아왔다면 기상청 API에 사용하기 위해 변환이 필요하다.
proj4 라이브러리를 추가해 보고 다른 블로그의 글을 참고해 보았지만 실패했고 가이드에 나와있는 변환 코드를 javascript로 다른 분이 업로드해주신 것을 찾았다.
https://gist.github.com/fronteer-kr/14d7f779d52a21ac2f16
위 Gist에는 javascript 외에 C#, 파이썬, 안드로이드 등 코드들이 있었다.
해당 코드에선 XY 좌표와 위도경도를 서로 원하는 방식으로 변환할 수 있었는데, 나는 위도 경도로 XY좌표를 구하는 한 가지 방식만 필요하기에 함수를 호출할 때 쓸 매개변수도 지우고 if문도 생략했다.
import React from 'react';
class MyComponent extends React.Component {
// LCC DFS 좌표변환을 위한 기초 자료
RE = 6371.00877; // 지구 반경(km)
GRID = 5.0; // 격자 간격(km)
SLAT1 = 30.0; // 투영 위도1(degree)
SLAT2 = 60.0; // 투영 위도2(degree)
OLON = 126.0; // 기준점 경도(degree)
OLAT = 38.0; // 기준점 위도(degree)
XO = 43; // 기준점 X좌표(GRID)
YO = 136; // 기준점 Y좌표(GRID)
dfs_xy_conv = (v1, v2) => {
let DEGRAD = Math.PI / 180.0;
let RADDEG = 180.0 / Math.PI;
let re = this.RE / this.GRID;
let slat1 = this.SLAT1 * DEGRAD;
let slat2 = this.SLAT2 * DEGRAD;
let olon = this.OLON * DEGRAD;
let olat = this.OLAT * DEGRAD;
let sn = Math.tan(Math.PI * 0.25 + slat2 * 0.5) / Math.tan(Math.PI * 0.25 + slat1 * 0.5);
sn = Math.log(Math.cos(slat1) / Math.cos(slat2)) / Math.log(sn);
let sf = Math.tan(Math.PI * 0.25 + slat1 * 0.5);
sf = Math.pow(sf, sn) * Math.cos(slat1) / sn;
let ro = Math.tan(Math.PI * 0.25 + olat * 0.5);
ro = re * sf / Math.pow(ro, sn);
let rs = {};
rs['lat'] = v1;
rs['lng'] = v2;
let ra = Math.tan(Math.PI * 0.25 + v1 * DEGRAD * 0.5);
ra = re * sf / Math.pow(ra, sn);
let theta = v2 * DEGRAD - olon;
if (theta > Math.PI) theta -= 2.0 * Math.PI;
if (theta < -Math.PI) theta += 2.0 * Math.PI;
theta *= sn;
rs['x'] = Math.floor(ra * Math.sin(theta) + this.XO + 0.5);
rs['y'] = Math.floor(ro - ra * Math.cos(theta) + this.YO + 0.5);
return rs;
};
componentDidMount() {
// Example usage:
let rs = this.dfs_xy_conv(37, 126);
console.log(rs.x, rs.y);
}
render() {
return <div>React Component</div>;
}
}
export default MyComponent;
위도 37과 경도 126을 넣었을 때 X 55 Y 124 좌표가 변환이 잘 되는 것을 확인할 수 있다.