React

React (카카오 맵) 마커 생성하기

Brad Daeho Lee 2021. 4. 20. 15:04

 

리액트에서 카카오 맵 API를 가져오는 방법은 다른 블로그에 많이 올라와 있어서 따로 올리지 않겠습니다. 그리고 카카오 맵 페이지에도 친절하게 잘 설명되어있습니다.😀

 

카카오 맵에서 주어지는 일반 마커를 이용해서 어떻게 마커를 생성했는지 알아보도록 하겠습니다.

 

 

Map.js

일단 먼저, Map을 생성하고 관리하는 코드는 모두 useEffect안에다가 넣었습니다. 그렇게해서 처음 렌더링 됬을 때 맵을 볼 수 있도록 했습니다. 새 마커를 생성하기 위해서는 일단 맵에 클릭 이벤트를 주어서 맵을 클릭했을 때 파란색 기본 마커가 클릭한 곳에 나타나게 하고, 그곳에 위치 정보(위도, 경도)를 useState를 사용해서 변수에 담아냅니다. 기본 마커에도 클릭 이벤트를 주어서 마커를 클릭했을 때 마커 생성 모달창이 보이게 했습니다. 

 

리덕스에 새롭게 저장된 마커 정보를 useSelector를 사용해서 정보를 가져오고 useEffect가 normalMarker 정보가 바뀔 때마다 실행되게 설정을 해놨기 때문에 useEffect가 실행되고 새로 생성된 마커가 맵에 찍히게 됩니다.

 

더보기
const Map = (props) => {
  const dispatch = useDispatch()
  const [ is_modal, setModal ] = useState(false);
  const [ markerId, setmarkerId ] = useState();
  const [latitude, setLatitude] = useState();
  const [longitude, setLongitude] = useState();
  const normalMarker = useSelector((state) => state.marker.normal)
  
  useEffect(() => {
    const container = document.getElementById('myMap'); //지도 표시할 div
      const options = {
        center: new kakao.maps.LatLng(37.545642179638556, 126.98117041998981), //지도 중심 좌표
        level: 8 //지도의 확대 레벨
      };
    const map = new kakao.maps.Map(container, options);//지도를 생성합니다.

    // useEffect밖으로 map정보를 가져오기위해 useState로 함수를 만들었습니다.
    setMap(map)

    // noraml마커 이미지입니다.
    var normalImageSrc = "https://cdn0.iconfinder.com/data/icons/small-n-flat/24/678111-map-marker-128.png"

    // 게시물 수가 10개 미만인 마커를 표시합니다.
    normalMarker.map((p, idx) => {

      var imageSize = new kakao.maps.Size(35, 35);

      var markerImage = new kakao.maps.MarkerImage(normalImageSrc, imageSize);

      const markers = new kakao.maps.Marker({
        // 지도 중심좌표에 마커를 생성합니다.
        map: map,
        position: new kakao.maps.LatLng(p.latitude, p.longitude) ,
        image: markerImage,
      });
    })

    


    const marker = new kakao.maps.Marker({
      position: map.getCenter()
    })



    // 지도에 마커를 표시합니다.
    kakao.maps.event.addListener(map, 'click', function(mouseEvent){
    
      //클릭한 위도, 경도 정보를 가져옵니다.
      const latlng = mouseEvent.latLng;
      
      //위도 경도 값을 useState를 이용해서 useEffect 밖으로 빼냅니다.
      setLatitude(latlng.getLat())
      setLongitude(latlng.getLng())
      
      //마커 위치를 클릭한 위치로 옮깁니다.
      marker.setPosition(latlng);
      
      //마커를 지도상에 보여줍니다.
      marker.setMap(map);
      
    })
    
    
    
    //마커에 클릭이벤트를 등록하기
    kakao.maps.event.addListener(marker, 'click', function(){
      //마커 생성 모달창을 띄워준다. 
      setModal(true)
    })
	// 마커가 생성될때 바로 화면상에 새로생성된 마커를 보여주기 위해 배열안에 normalMarker를 넣어놨습니다.
  }, [normalMarker])



  const closeModal = () => {
    setModal(false)
  }
  

  return(
    <React.Fragment>
        <MapContainer id='myMap'>
        </ MapContainer>
      {is_modal? <MarkerModal close={closeModal} latitude={latitude} longitude={longitude} />
      : null }
    </React.Fragment>
  )

}

const MapContainer = styled.div`
  position: relative;
  margin: auto;
  margin-bottom: 60px;
  width: 900px;
  height: 500px;
  @media (max-width: 1000px){
    width: 80%;
  };
  @media (max-width: 450px){
    width: 90%;
    height: 400px;
  }
`


export default Map

 

MarkerModal.js

마커생성 모달은 Map.js에서 받아온 위치 정보와 모달에다 작성한 마커 이름을 미들웨어 함수에다가 전달하는 역할을 합니다.  

 

더보기
import React, {useState} from 'react';
import styled from 'styled-components';
import TextField from '@material-ui/core/TextField';
import CloseIcon from '@material-ui/icons/Close';

import {useDispatch} from 'react-redux';
import {actionCreators as markerActions} from '../redux/modules/marker';


const MarkerModal =(props) => {
  const dispatch = useDispatch()

  // 사용자가 지정한 마커 이름을 담아냅니다.
  const [ title, setTitle ] = useState()


  const selectTitle = (e) => {
    console.log(e.target.value)
    setTitle(e.target.value)
  }

  // 마커를 생성할 때 사용되는 함수입니다.
  const addMarker = () => {
    let marker = {
      // Map.js에서 받아온 값도 있고 여기서 생성한 값도 있습니다.
      latitude: props.latitude,
      longitude : props.longitude,
      title: title,
    }

    // 새 마커 값을 담아서 미들웨어 함수를 실행시킵니다.
    dispatch(markerActions.addMarkerAX(marker))

    // 마커 생성 모달 창을 닫습니다.
    props.close()
  }

  return(
    <React.Fragment>
      <Component onClick={props.close} />
      <ModalComponent>
        <ModalExitBtn onClick={props.close} >
          <CloseIcon/>
        </ModalExitBtn>
        <ModalHeader>마커를 생성하시겠습니까?</ModalHeader>
        <ModalInput>
          <TextField id="standard-basic" label="장소를 입력해주세요." style={{width: '100%'}} onChange={selectTitle} />
        </ModalInput>
        <ModalButtonContainer>
          <ModalSubmitBtn onClick={addMarker}>
            마커 생성
          </ModalSubmitBtn>
        </ModalButtonContainer>
      </ModalComponent>
    </React.Fragment>
  )
}

const Component = styled.div`
  position: fixed;
  top: 0;
  opacity: 0.4;
  height: 100vh;
  width: 100vw;
  background-color: black;
  z-index: 10;
`

const ModalComponent = styled.div`
  position: fixed;
  top: 50%;
  left: 50%;
  width: 500px;
  height: 300px;
  transform: translate(-50%, -50%);
  background-color: white;
  z-index: 20;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
`

const ModalHeader = styled.div`
  margin-top: 30px;
  font-weight: 600;
  font-size: 18px;
`

const ModalInput = styled.div`
  box-sizing: border-box; 
  width: 50%;
`
const ModalButtonContainer = styled.div`
  box-sizing: border-box; 
  width: 50%;
  margin-bottom: 30px;
`
const ModalSubmitBtn = styled.button`
  width: 100%;
  background-color: #FFE812;
  border: none;
  outline: none;
  padding: 10px 0;
  cursor: pointer;
  font-weight: 600;
  font-size: 15px;
  border-radius: 4px;
  &:hover {
    opacity: 0.7;
  }
`
const ModalExitBtn = styled.button`
  position: absolute;
  top: 0;
  right: 0;
  padding: 8px 12px;
  cursor: pointer;
  background-color: transparent;
  outline: none;
  border: none;
  color: black;
`

export default MarkerModal

 

marker.js

MarkerModal.js에서 받은 마커 정보를 서버에다 보내서 db에 저장시킵니다. 그리고 리듀서에도 마커 정보를 보내서 redux store에 마커 정보를 저장시킵니다.(리덕스에 저장을 하게되면 서버에서 계속 값을 불러오지 않아도 바로바로 새롭게 생성된 마커를 확인할 수 있습니다.)

 

더보기
const addMarkerAX = (marker) => {
  return function (dispatch){
    // 로그인을 했을 때만 마커를 생성할 수 있기 때문에 token 값을 서버에 넘겨줍니다.
    const _token= sessionStorage.getItem("JWT")
    let token = {
      headers : { authorization: `Bearer ${_token}`}
    }
    // 생성된 마커 정보를 서버에 보냅니다.
    axios.post(`${config.api}/marker`, {
      markername: marker.title, location: [marker.latitude.toString(), marker.longitude.toString()], 
    }, token).then((response) => {
      // 서버에서 마커 오브젝트 id와 boardcount를 보냅니다.
      console.log(response.data)
      let marker_info = {...marker, id: response.data.markerId, boardcount: 0}
      // 액션 함수에 마커 정보를 담아서 보냅니다.
      dispatch(addMarker(marker_info))
    })
  }
}

const ADD_MARKER = "ADD_MARKER";

//액션함수
const addMarker = createAction(ADD_MARKER, (marker) => ({marker}))

//리듀서
//새로운 marker 정보를 redux store에 저장
    [ADD_MARKER]: (state, action) => produce(state, (draft) => {
      draft.normal.unshift(action.payload.marker)
    }),