Python(Flask)+React+SASS+mongoDB로 웹 서비스 제작하기 프로젝트(1-4) – Kakao Map API 입력받은 값으로 지도중심 및 마커 이동시키기

API분석의 끝이 오는 것 같다.
백엔드에서 날아오는 값으로 지도를 옮기고 마커를 찍는 일은 아마도 다반사일 것이다. 따라서 난 input box에서 데이터를 입력받고 그 입력된 데이터를 이용해 지도를 이동시켜볼 것이다.

지도와 마커를 이동시키는 버튼을 만들자

데이터를 직접 받아서 이동시키기 전에 버튼을 만들어서 값을 바꾸는 간단한 것 부터 해보자!
먼저 API부터 확인하자.

function panTo() {
    // 이동할 위도 경도 위치를 생성합니다 
    var moveLatLon = new kakao.maps.LatLng(33.450580, 126.574942);
    
    // 지도 중심을 부드럽게 이동시킵니다
    // 만약 이동할 거리가마 지도 화면보다 크면 부드러운 효과 없이 이동합니다
    map.panTo(moveLatLon);            
}

진짜 짱 쉽다.
마커 이동은 바로 직전의 글 에서 설명했으니 이번 글에선 생략하도록 한다.

이제 첫번째로 할 일은 버튼을 만들어주자!

render(){
        return(
            <fragment>
                <div className="Map" id="map"></div>
                <button onClick={this.handleTest}>클릭</button>
                <h1>클릭한 위치의 위도는 : {this.state.x}</h1>
                <h1>클릭한 위치의 경도는 : {this.state.y}</h1>
            </fragment>
        );
    }

<button onClick={this.handleTest}>클릭</button> 이 버튼을 추가하기 위해 추가한 소스이고 버튼을 클릭한 경우 onClick에 this.handleTest 함수를 전달한다 만약 this.handleTest()이런식으로 함수를 호출한다면 렌더링 할 때 마다 함수를 부를 것이고 무한루프에 빠질 가능성이 있다.

이제 this.handleTest함수를 작성하자

handleTest = () => {    // 마커의 x와 y값을 바꾸는 함수
        const { x, y } = this.state;
        this.setState({
            x: x + 0.001,
            y: y + 0.001
        })
        kakao.maps.load(() => { //지도를 정보를 바꾸기 위한 LagLng객체를 만들기 위해 로드
            this.myMarker.setPosition(new kakao.maps.LatLng(this.state.x, this.state.y));   // 마커 이동
            this.myMap.panTo(new kakao.maps.LatLng(this.state.x, this.state.y));    // 지도 중심 이동
        })
}

지도에서 위치와 좌표를 바꾸기 위한 함수의 매개변수로 kakao.map.LatLng객체 형식으로 넘겨야 하기 때문에 객체를 만들기 위한 kakao.maps.load()를 한다.

또한 이전에 살펴본 바로 componentDidMount() 메소드에 지도 객체 map과 마커객제 marker가 있는데 이를 수정할 수 없기 때문에 클래스의 필드에 전역으로 변수를 선언하고 componentDidMount() 메소드가 실행될 때 생성되는 map과 marker객체를 point하게한다.

class Map extends Component {
    state = {
        x: 37.536172,
        y: 126.976978,
    };
    myMarker = null;    //marker를 전역으로 사용하기 위한 필드변수
    myMap = null;       //map을 전역으로 사용하기 위한 필드변수

    componentDidMount(){    // 컴포넌트가 만들어지고 첫 렌더링을 다 마친 후 실행되는 메소드
        const script = document.createElement('script');
        script.async = true;    // 브라우저가 페이지를 파싱되는 동안에도 스크립트가 실행됨.
        script.src = "https://dapi.kakao.com/v2/maps/sdk.js?appkey=aa9483f57241f9b715de6016ff873e83&autoload=false";
        document.head.appendChild(script);

        script.onload = () => { // 페이지가 로드될 때 특정 함수를 호출시 사용
            kakao.maps.load(() => { // 가져온 api에 포함된 함수 실행
                this.myLatLng = new kakao.maps.LatLng(this.state.x, this.state.y);

                let container = document.getElementById('map');

                let options = {
                    center: new kakao.maps.LatLng(37.536172, 126.976978),
                    level: 3,
                }
                
                this.myMap = new kakao.maps.Map(container, options);   // 새롭게 만든 map객체를 myMap에 담아준다.

                // 마커가 표시될 위치입니다 
                let markerPosition  = new kakao.maps.LatLng(37.536172, 126.976978); 

                // 마커를 생성합니다
                // 지도를 클릭한 위치에 표출할 마커입니다
                this.myMarker = new kakao.maps.Marker({  // 새롭게 생성되는 마커를 myMarker에 담아준다.
                    // 지도 중심좌표에 마커를 생성합니다
                    position: markerPosition
                });

                this.myMarker.setMap(this.myMap);

                kakao.maps.event.addListener(this.myMap, 'click', (mouseEvent) => {

                    // 클릭한 위도, 경도 정보를 가져옵니다
                    let latlng = mouseEvent.latLng;

                    // 마커 위치를 클릭한 위치로 옮깁니다
                    this.myMarker.setPosition(latlng);
                    
                    // 클릭한 위치의 위도와 경도를 state의 x와 y에 setState해주기
                    this.setState({
                        x: latlng.getLat(),
                        y: latlng.getLng()
                    });
                });
            });
        };
    }
}

이런 방식으로 버튼을 클릭하면 위도와 경도가 0.001씩 증가하여 지도와 마커가 이동하도록 구현할 수 있다.

위 gif는 실행결과다 블로그에 글을 쓰는 시점이 분석을 한 후여서 버튼의 위치와 개수가 위의 소스와 다를 수 있다.

본격적으로 값을 입력받아  위치를 변경시켜보자!

먼저 값을 입력받을 component를 새로 만들 것이다. 그리고 이 컴포넌트는 Map컴포넌트에 Import되어 사용될 것이다. 그러므로 값을 부모(Map.js)에게 넘겨줘야한다.

import React, { Component } from 'react';

class Inputs extends Component{
    state = {
        Lat: '',
        Lng: ''
    }

    handleChange = (e) => {
        this.setState({
            [e.target.name]: e.target.value
        });
    }

    confirmBtn = (e) =>{
        // submit버튼을 누른 경우 리로딩 방지
        e.preventDefault();
        // 상태값을 onCreate 를 통하여 부모에게 전달
        this.props.onCreate(this.state);

        this.setState({
            Lat: '',
            Lng: ''
        })
    }
    render() {
        return (
            <form onSubmit={this.confirmBtn}>
                <input
                    placeholder="위도"
                    value={this.state.Lat}
                    onChange={this.handleChange}
                    name="Lat"
                />
                <input
                    placeholder="경도"
                    value={this.state.Lng}
                    onChange={this.handleChange}
                    name="Lng"
                />
                <h1>{this.state.Lat}, {this.state.Lng}</h1>
                <button type="submit">등록</button>
            </form>
        );
    }
}

export default Inputs;

먼저 input의 value가 변경될 때 마다 웹에서 확인할 수 있게
<h1>{this.state.Lat}, {this.state.Lng}</h1>으로 출력해주고 값을 전달하기 위한 submit버튼 “등록”버튼을 만들어준다.
이어서 input의 value가 변경될 때 마다 onChange 이벤트를 통해 handleChange 함수에서 state 값을 변경시켜주고 등록 버튼을 클릭하면 현재 input들의 value값 즉 state 값을 현재 컴포넌트의 props로 하여 부모(Map.js)에 전달해준다. 그러고 난 후 state는 초기화를 시킨다.

/*global kakao*/

import React, {Component} from 'react';
import './Map.css';
import Inputs from "./Inputs";

class Map extends Component {
    state = {
        x: 37.536172,
        y: 126.976978,
    };
    myMarker = null;    //marker를 전역으로 사용하기 위한 필드변수
    myMap = null;       //map을 전역으로 사용하기 위한 필드변수

    componentDidMount(){    // 컴포넌트가 만들어지고 첫 렌더링을 다 마친 후 실행되는 메소드
        const script = document.createElement('script');
        script.async = true;    // 브라우저가 페이지를 파싱되는 동안에도 스크립트가 실행됨.
        script.src = "https://dapi.kakao.com/v2/maps/sdk.js?appkey=aa9483f57241f9b715de6016ff873e83&autoload=false";
        document.head.appendChild(script);

        script.onload = () => { // 페이지가 로드될 때 특정 함수를 호출시 사용
            kakao.maps.load(() => { // 가져온 api에 포함된 함수 실행
                this.myLatLng = new kakao.maps.LatLng(this.state.x, this.state.y);

                let container = document.getElementById('map');

                let options = {
                    center: new kakao.maps.LatLng(37.536172, 126.976978),
                    level: 3,
                }
                
                this.myMap = new kakao.maps.Map(container, options);

                // 마커가 표시될 위치입니다 
                let markerPosition  = new kakao.maps.LatLng(37.536172, 126.976978); 

                // 마커를 생성합니다
                // 지도를 클릭한 위치에 표출할 마커입니다
                this.myMarker = new kakao.maps.Marker({
                    // 지도 중심좌표에 마커를 생성합니다
                    position: markerPosition
                });

                this.myMarker.setMap(this.myMap);

                kakao.maps.event.addListener(this.myMap, 'click', (mouseEvent) => {

                    // 클릭한 위도, 경도 정보를 가져옵니다
                    let latlng = mouseEvent.latLng;

                    // 마커 위치를 클릭한 위치로 옮깁니다
                    this.myMarker.setPosition(latlng);
                    
                    // 클릭한 위치의 위도와 경도를 state의 x와 y에 setState해주기
                    this.setState({
                        x: latlng.getLat(),
                        y: latlng.getLng()
                    });
                });
            });
        };
    }

    handleTest = () => {    // 마커의 x와 y값을 바꾸는 함수
        const { x, y } = this.state;
        this.setState({
            x: x + 0.001,
            y: y + 0.001
        })
        kakao.maps.load(() => { //지도를 정보를 바꾸기 위한 LagLng객체를 만들기 위해 로드
            this.myMarker.setPosition(new kakao.maps.LatLng(this.state.x, this.state.y));   // 마커 이동
            this.myMap.panTo(new kakao.maps.LatLng(this.state.x, this.state.y));    // 지도 중심 이동
        })
    }

    handleCreate = (data) =>{   // Inputs에서 받아온 값으로 좌표를 이동시키기 위한 함수
        let createX = parseFloat(data.Lat), createY = parseFloat(data.Lng);
        this.setState({
            x: createX,
            y: createY
        })

        kakao.maps.load(() => {
            this.myMap.panTo(new kakao.maps.LatLng(createX, createY));
            this.myMarker.setPosition(new kakao.maps.LatLng(createX, createY));
        });
    }
    render(){
        return(
            <fragment>
                <div className="Map" id="map"></div>
                <Inputs onCreate={this.handleCreate} />
                <button onClick={this.handleTest}>클릭</button>
                <h1>클릭한 위치의 위도는 : {this.state.x}</h1>
                <h1>클릭한 위치의 경도는 : {this.state.y}</h1>
            </fragment>
        );
    }
}

export default Map;

먼저 Inputs.js를 import 하고 render 부분에
<Input onCreate={this.handleCreate} />를 통해 props로 넘겨어온 값을 handleCreate함수의 data라는 parameter 받아온다.
넘어온 값을 이용해 Maps.js 의 state 값을 변경하고 지도를 새롭게 이동한다. 여기서 let createX, createY라는 변수를 사용해 지도를 이동한 이유는 setState한 후 지도를 이동하는 부분에서 this.state.x와 this.state.y로 변경하려 하니 이상하게 버튼을 두번 클릭한 시점부터 1회 전에 클릭한 시점의 state값으로 지도가 변경되었다. 따라서 위와같은 로직으로 구현하였고 이유는 아직 알 수 없다.

실행결과

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다