상세 컨텐츠

본문 제목

리액트기반으로 공공데이터와 지도API 연동06

노드js·자바스크립트

by 김일국 2022. 8. 13. 18:09

본문

----주요실습 순서(아래)------------------------------------------------------------------------

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

관련글 더보기

댓글 영역