TIL (231208)

Jtiiin:K·2023년 12월 8일
0

내일배움캠프

목록 보기
51/85
post-thumbnail

오늘 한 일

알고리즘
팀프로젝트 진행
└ 지도 붙이기


오늘 공부한 내용

✅ 툴킷에서 state 변경하기

👉 상황 : firebase 에 정보를 저장한 다음 화면에 리렌더링 시키기 위해서
(원래는 이렇게 하면 안되지만)
리덕스에 단순한 상태값(true)을 만들어 놓고 서버통신이 일어났을 때
상태값을 변경시켜(false) 전달해주고 사용하려는 컴포넌트에서 useEffect로
전달받은 상태값의 변화에 따라 서버통신을 하려는 아이디어

💥 state 값은 변하는데 useEffect가 새로 실행되지 않는 것
💡 리듀서에서 state = !state 로직으로 상태값을 변경시켰는데
return !state로 바꾸니 useEffect가 다시 실행되면서 원하는 동작이 수행됐다

🤔 왜그럴까?
redux toolkit 공식문서
Immer는 중첩된 필드에 할당하거나 값을 변경하는 함수를 호출하여 기존의 초안 상태 값을 변경하려는 시도를 추적하는 방식으로 작동합니다. 즉, Immer가 변경 시도를 확인하려면 state가 JS 객체 또는 배열이어야 합니다. (슬라이스의 상태가 문자열이나 부울과 같은 기본형(Primitive Type)일 수도 있지만, 기본형(Primitive Type)은 절대 변경할 수 없으므로 새 값을 반환하기만 하면 됩니다).

👉 기본값이 false 인 기본형이었기 때문에 직접 변경을 하려고 해서 오류가 난 것!


✅ kakao 지도 사용

  • API 키 얻기
  • 가져오기
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY"></script>
  • 라이브러리 설치 yarn add react-kakao-maps-sdk
  • 맵 위에 마커와 인포윈도우 올리기
function(){
  return (
    <Map
      center={{ lat: 33.5563, lng: 126.79581 }}
      style={{ width: "100%", height: "360px" }}
    >
      <MapMarker position={{ lat: 33.55635, lng: 126.795841 }}>
        <div style={{color:"#000"}}>Hello World!</div>
      </MapMarker>
    </Map>
  )
}


  • 그리고 삽질...
    └ 키워드 검색
    └ 마커찍기
    └ 마커찍은 곳의 주소 가져오기
    └ 그 후에 쓸모없어지기^^
    (키워드보다 주소를 검색해서 해당 위치 하나만 마커를 찍어주는 게 더 좋은 방법인 것 같아서 다른 코드로 가기로!)
import {useEffect, useState} from 'react';
import {Map, MapMarker} from 'react-kakao-maps-sdk';

const MapComponent = () => {
  // State 정의
  const [info, setInfo] = useState(); // 마커 클릭 시 정보를 담는 상태
  const [markers, setMarkers] = useState([]); // 키워드 검색 결과 마커 정보를 담는 상태
  const [map, setMap] = useState(); // 지도 객체를 담는 상태
  const [searchAddress, setSearchAddress] = useState(''); // 검색어를 담는 상태
  const [position, setPosition] = useState(); // 클릭한 위치 좌표를 담는 상태
  const [isOpen, setIsOpen] = useState(false); // 마커 여닫기

  const {kakao} = window; // 카카오 맵 API

  useEffect(() => {
    // 주소를 좌표로 변환하는 함수
    const searchAddrFromCoords = (coords, callback) => {
      const geocoder = new kakao.maps.services.Geocoder();
      geocoder.coord2RegionCode(coords.getLng(), coords.getLat(), callback);
    };

    // 좌표를 상세한 주소로 변환하는 함수
    const searchDetailAddrFromCoords = (coords, callback) => {
      const geocoder = new kakao.maps.services.Geocoder();
      geocoder.coord2Address(coords.getLng(), coords.getLat(), callback);
    };

    // 지도 중심 좌표에 대한 주소 정보를 표시하는 함수
    const displayCenterInfo = (result, status) => {
      if (status === kakao.maps.services.Status.OK) {
        // centerAddr 엘리먼트 찾기
        let infoDiv = document.getElementById('centerAddr');

        if (!infoDiv) {
          // 존재하지 않으면 엘리먼트 생성
          infoDiv = document.createElement('div');
          infoDiv.id = 'centerAddr';
          // 여기서 필요한 스타일이나 속성을 추가할 수 있습니다.
          // 예: infoDiv.style.color = '#000';
          // ...

          // 생성한 엘리먼트를 어딘가에 추가 (예: body에 추가)
          document.body.appendChild(infoDiv);
        }

        // 검색 결과가 있을 때 주소 정보 표시
        for (let i = 0; i < result.length; i++) {
          if (result[i].region_type === 'H') {
            infoDiv.innerHTML = result[i].address_name;
            break;
          }
        }
      }
    };

    // 지도가 생성되었을 때 이벤트 핸들러 등록
    if (map) {
      // 마커 클릭 시 상세 주소 표시 이벤트
      kakao.maps.event.addListener(map, 'click', function (mouseEvent) {
        searchDetailAddrFromCoords(mouseEvent.latLng, function (result, status) {
          if (status === kakao.maps.services.Status.OK) {
            // const {address_name} = result[0].road_address;
            const {address_name} = result[0].address;
            console.log(address_name);
            let detailAddr = !!result[0].road_address
              ? result[0].road_address.address_name + '</div>'
              : result[0].address.address_name;
            const content = '<div class="bAddr">' + detailAddr + '</div>';
            const marker = new kakao.maps.Marker();
            const infowindow = new kakao.maps.InfoWindow({zindex: 1});
            marker.setPosition(mouseEvent.latLng);
            marker.setMap(map);
            infowindow.setContent(content);
            infowindow.open(map, marker);

            console.log('클릭한 위치의 주소:', result[0].address.address_name);
          }
        });
      });

      // 지도 중심 좌표 변경 시 이벤트
      kakao.maps.event.addListener(map, 'idle', function () {
        searchAddrFromCoords(map.getCenter(), displayCenterInfo);
      });
    }
  }, [map, searchAddress]);

  // 검색어 입력 핸들러
  const handleSearchAddress = e => {
    setSearchAddress(e.target.value);
  };

  // 키워드 검색 함수
  const search = () => {
    if (!map) return;
    const ps = new kakao.maps.services.Places();

    ps.keywordSearch(searchAddress, (data, status, _pagination) => {
      if (status === kakao.maps.services.Status.OK) {
        const bounds = new kakao.maps.LatLngBounds();
        let newMarkers = [];

        for (let i = 0; i < data.length; i++) {
          newMarkers.push({
            position: {
              lat: data[i].y,
              lng: data[i].x,
            },
            content: data[i].place_name,
          });
          bounds.extend(new kakao.maps.LatLng(data[i].y, data[i].x));
        }
        setMarkers(newMarkers);
        map.setBounds(bounds);
      }
    });
  };

  // 컴포넌트 렌더링
  return (
    <>
      {/* 지도 컴포넌트 */}
      <Map
        center={{
          lat: 37.566826,
          lng: 126.9786567,
        }}
        style={{
          width: '100%',
          height: '500px',
        }}
        level={3}
        onCreate={setMap}
        onClick={(_, mouseEvent) => {
          setPosition({
            lat: mouseEvent.latLng.getLat(),
            lng: mouseEvent.latLng.getLng(),
          });
          search(); // 클릭 이벤트에서 search 함수 호출
        }}
      >
        {/* 클릭한 위치에 마커 표시 */}
        {position && <MapMarker position={position} />}
        {/* 검색 결과 마커 표시 */}
        {markers.map(marker => (
          <MapMarker
            key={`marker-${marker.content}-${marker.position.lat},${marker.position.lng}`}
            position={marker.position}
            onClick={() => setInfo(marker)}
            onMouseOver={
              // 마커에 마우스오버 이벤트가 발생하면 인포윈도우를 마커위에 표시합니다
              () => setIsOpen(true)
            }
            onMouseOut={
              // 마커에 마우스아웃 이벤트가 발생하면 인포윈도우를 제거합니다
              () => setIsOpen(false)
            }
            draggable={true}
            clickable={true}
            removable={true}
          >
            {/* 클릭한 마커에 대한 정보 표시 */}
            {info && info.content === marker.content && (
              <div style={{color: '#000', width: '100px', height: '50px'}}>{marker.content}</div>
            )}
          </MapMarker>
        ))}
      </Map>
      {/* 검색어 입력창과 버튼 */}
      <div>
        <input value={searchAddress} onChange={handleSearchAddress}></input>
        <button onClick={search}>클릭</button>
      </div>
    </>
  );
};

export default MapComponent;

어려웠던 내용

외부 API 사용하는게 이렇게 어렵네..
각 코드에 주석을 붙여서 이해하려고 해보았으나..
쉽지 않아서 커스텀하기가 너무 어려웠다 ㅠ
지도화면에 대한 기획도 부족해서 이것 저것 다 갖다 붙이려다보니 더 헤맨 것도 있었다
결론은 주소검색으로 하기로 결정나서 어느정도 해결!
막막했는데 일단 한고비 넘긴 것 같아 다행이다
근데 오늘이 금..요일..^^ㅋㅋㅋ


느낀점

💀

profile
호기심 많은 귀차니즘의 공부 일기

0개의 댓글