----주요실습 순서(아래)------------------------------------------------------------------------
1 구름IDE에서 리액트 컨테이너 생성 후 npm install 로 기본 패키지 설치 후 리액트 홈 실행하기.
2 1).공공데이터 포털에서 API 활용신청하기 및 2).카카오 개발자센터에서 지도 API 애플리케이션 생성하기.
3 구름IDE에서 공공데이터 포털의 API 데이터를 노드js API의 서버기능 으로 데이터 가져오기.
4 리액트에서 카카오 맵 API 샘플코드를 사용하여 클래스형 컴포넌트 생성 후 라우터 기능으로 메뉴에 추가하기.
5 이전에 생성한 노드js API 데이터를 리액트 카카오 맵 클래스형 컴포넌트에 바인딩해서 지도에 표시하기.
6 클래스형 카카오 맵 컴포넌트를 참조하여 함수형 컴포넌트를 생성 후 노드js API 데이터를 바인딩해서 지도에 표시하기.
7 npm빌드 대신 yarn 빌드를 사용하기 위한 yarn 설치 및 빌드로 build 폴더의 index.html 생성하기.
8 컨테이너 나가기 후에도 노드js 서버 실행을 유지하기 위한 forever 설치(node 버전 업드레이드 필수) 및 forever 로 앱 실행하기.
------------------------------------------------------------------------------------------------------
6 클래스형 카카오 맵 컴포넌트를 참조하여 함수형 컴포넌트를 생성 후 노드js API 데이터를 바인딩해서 지도에 표시하기.
- 리액트JS 의 특징중 생명주기에 대해서 한번 더 알아보고 코딩에 들어간다.(아래)
/* React 클래스형 컴포넌트의 생명 주기란? (지난시간에 사용) */
영어로 라이프사이클(Life cycle)이라고도 표현.
컴포넌트가 실행되거나 업데이트되거나 제거될 때, 특정한 이벤트들이 자동으로 발생된다.
클래스 마운트(렌더링) 전: componentWillMount()
클래스 마운트(렌더링) 후: componentDidMount()
클래스 업데이트(리렌더링) 후: componentDidUpdate()
클래스 언마운트(컴포넌트 화면전환) 전: componentWillUnmount()
/* React 함수형 컴포넌트의 생명 주기란? (이번시간에 사용 예정) */
클래스형 컴포넌트처럼 명시적인 함수를 사용하지 않고ㅡ useEffect() 함수를 사용해서 위 4가지 상태를 구현한다.
예를 들어 keyword이라는 state(상태)가 있다고 가정하면, keyword가 바뀌는 것에 따라서 useEffect()함수가 재 실행되는 라이프사이클로 정할 수 있다.(우리 실습 코드 에서는 검색버튼이 클릭 할때 render함수를 실행 시키는 구현이라서 keyword를 제거한다.)
useEffect(() => {
console.log('keyword의 한 단어라도 바뀔때마다 useEffect함수가 재실행 된다.');
}, [keyword]);
위 코드는 컴포넌트가 첫 렌더링될 때 한 번 실행되고, 그 다음부터는 keyword의 한 단어라도 바뀔때마다 useEffect함수가 재실행 된다
즉, componentDidMount와 componentDidUpdate가 합쳐진 셈이다.
componentWillUnmount의 역할은 아래처럼 return으로 함수에 반환값을 추가하면 된다.
useEffect(() => {
console.log('keyword의 한 단어라도 바뀔때마다 useEffect함수가 재실행 된다.');
return () => {
console.log('keyword변화 때문에 화면이 바뀔 예정이다.');
};
}, [keyword]);
- 함수형 컴포넌트 FunctionKakaoMap.js 파일을 추가하고, API json데이터 파싱 후 바인딩 하기.(아래)
/*global kakao*/
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
function KakaoMap(props) {
var [keyword, setKeyword] = useState('천안시');
var onChange = (e) => setKeyword(e.target.value);
function removeAllChildNods(el) {
while (el.hasChildNodes()) {
el.removeChild (el.lastChild);
}//기술참조:https://apis.map.kakao.com/web/sample/keywordList/
}
var onSearch = () => { //alert(keyword);
var mapContainer = document.getElementById('map');
removeAllChildNods(mapContainer);//기존 카카오맵 겍체 지우기
setPageNo(1);//화면 처리
pageNo = 1;//js 처리
getData();
};
var [totalCount, setTotalCount] = useState(0);
var [pageNo, setPageNo] = useState(1);
var onPage = (e) => { //12주차 3교시 Chart.js의 onChage함수 사용 참조
setPageNo(e.target.value);//12주차 2교시 App.js 의 화면처리, js처리 가 필요한 이유 참조
pageNo = e.target.value;//js 처리
var mapContainer = document.getElementById('map');
removeAllChildNods(mapContainer);//기존 카카오맵 겍체 지우기
getData();
};
function repeatPage(totalCount) {
console.log("여기1: " + totalCount);
var pagingNo = Math.ceil(totalCount/10);
var arr = [];
for(var i=1; i<=pagingNo; i++) {
arr.push(
<option key={i} value={i}>{i}</option>
);
}
return arr;
}
var getData = () => {
var url = 'https://server-basic-fekuw.run.goorm.io/openapi/getdata?keyword='+keyword+'&pageNo='+pageNo;
fetch (url, {method:'get'})
.then (response => response.json()) //응답데이터를 json 형태로 변환
.then (contents => { //json으로 변환된 응답데이터인 contents 를 가지고 구현하는 내용
totalCount = contents['response']['body']['totalCount']['_text'];//js 처리용
setTotalCount(contents['response']['body']['totalCount']['_text']);//화면 처리용
console.log(totalCount + "/" + pageNo);
//setPageNo(jsonData=contents['response']['body']['pageNo']['_text']);
var positions = [];//배열 선언
var jsonData;
console.log(contents);
jsonData=contents['response']['body']['items'];
jsonData['item'].forEach((element) => {//람다식 사용 function(element) {}
positions.push(
{
content: "<div>"+element["csNm"]['_text']+"</div>",//충전소 이름
latlng: new kakao.maps.LatLng(element["lat"]['_text'], element["longi"]['_text']) // 위도(latitude), 경도longitude)
}
);
});
var index = parseInt(positions.length/2);//배열은 인덱스순서 값을 필수로 가지고, 여기서는 반환 값의 개수로 구한다.
console.log(jsonData["item"][index]["lat"]['_text']);
//console.log(jsonData);
var mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(jsonData["item"][index]["lat"]['_text'], jsonData["item"][index]["longi"]['_text']),
//center: new kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표
level: 10 // 지도의 확대 레벨
};
var map = new kakao.maps.Map(mapContainer, mapOption); // 지도를 생성합니다
// 마커를 표시할 위치와 내용을 가지고 있는 객체 배열입니다
/*var positions = [
{
content: '<div>카카오</div>',
latlng: new kakao.maps.LatLng(33.450705, 126.570677)
},
{
content: '<div>생태연못</div>',
latlng: new kakao.maps.LatLng(33.450936, 126.569477)
},
{
content: '<div>텃밭</div>',
latlng: new kakao.maps.LatLng(33.450879, 126.569940)
},
{
content: '<div>근린공원</div>',
latlng: new kakao.maps.LatLng(33.451393, 126.570738)
}
];*/
for (var i = 0; i < positions.length; i ++) {
// 마커를 생성합니다
var marker = new kakao.maps.Marker({
map: map, // 마커를 표시할 지도
position: positions[i].latlng // 마커의 위치
});
// 마커에 표시할 인포윈도우를 생성합니다
var infowindow = new kakao.maps.InfoWindow({
content: positions[i].content // 인포윈도우에 표시할 내용
});
// 마커에 mouseover 이벤트와 mouseout 이벤트를 등록합니다
// 이벤트 리스너로는 클로저를 만들어 등록합니다
// for문에서 클로저를 만들어 주지 않으면 마지막 마커에만 이벤트가 등록됩니다
kakao.maps.event.addListener(marker, 'mouseover', makeOverListener(map, marker, infowindow));
kakao.maps.event.addListener(marker, 'mouseout', makeOutListener(infowindow));
}
// 인포윈도우를 표시하는 클로저를 만드는 함수입니다
function makeOverListener(map, marker, infowindow) {
return function() {
infowindow.open(map, marker);
};
}
// 인포윈도우를 닫는 클로저를 만드는 함수입니다
function makeOutListener(infowindow) {
return function() {
infowindow.close();
};
}
})
.catch ((err) => console.log ('에러: ' + err + '때문에 접속할 수 없습니다.'));
}
useEffect(() => { //화면에 변화가 있는지 확인 후 실행할 때(=화면이 html객체모두 로딩 후) useEffect 함수를 사용한다.
getData();
}, []);//keyword 를 입력하면 실시간으로 바뀐다.
return (
<div>
<h2>함수형 전기차 충전소 위치</h2>
<span>충전소 시검색</span>
<input className="form-control" type="text" onChange={onChange} value={keyword} />
<input className="form-control btn btn-primary" type="button" onClick={onSearch} value="검색" />
<span>페이지이동(아래 번호를 선택하면 화면이 전환된다.)</span><select className="form-select" onChange={onPage} value={pageNo}>
{repeatPage(totalCount)}
</select>
<Link to="/"><button className="form-control btn btn-primary" id="btnHome">홈으로</button></Link>
<div id="map" style={{width:"100%",height:"70vh"}}></div>
</div>
);
}
export default KakaoMap;
- 실행하기 전 index.js 파일의 import에 FunctionKakaoMap 과 라우트에 /functionkakaomap 경로를 추가한다.(아래)
import ClassKakaoMap from './components/ClassKakaoMap';
import FunctionKakaoMap from './components/FunctionKakaoMap';
중략...
<Route path="/classkakaomap" element={<ClassKakaoMap />} />
<Route path="/functionkakaomap" element={<FunctionKakaoMap />} />
- 실행하기 전 App.js 파일(메인페이지)의 링크버튼 /functionkakaomap 을 추가한다.(아래)
<Link to="/classkakaomap"><button id="btnHome">클래스형 카카오 맵</button></Link>
<Link to="/functionkakaomap"><button id="btnHome">함수형 카카오 맵</button></Link>
- App.js 메인페이지에 버튼이 추가된 것을 확인 할 수 있다.(아래)
- 실행 결과는 이전에 작업한 클래스형과 같다(아래)
- 이번 시간에 작업한 소스: https://github.com/kimilguk/react-basic/tree/basic06
리액트기반으로 공공데이터와 지도API 연동08 (0) | 2022.08.13 |
---|---|
리액트기반으로 공공데이터와 지도API 연동07 (0) | 2022.08.13 |
리액트기반으로 공공데이터와 지도API 연동05 (0) | 2022.08.13 |
리액트기반으로 공공데이터와 지도API 연동04 (0) | 2022.08.13 |
리액트기반으로 공공데이터와 지도API 연동03 (0) | 2022.08.13 |
댓글 영역