페어 활동 기록_Day16_한빈_현정 - boostcampwm-2022/web33-Mildo GitHub Wiki

✂️ 분배된 이슈

  • (지난주) 핀이 포커싱 될 경우 커지는 기능 완성(#29)
  • (지난주) 핀 클릭시 상세 모달창 출력(#34)
    • 배열의 index보다 _id를 저장하는 것이 좋을 것 같음

🚩 구현 목표

  • 핀 관련 기능을 완성한다.

🍀 세부 목표

  • 핀마다 이벤트 추가
  • 핀 클릭 시 핀이 화면 가운데로 이동
  • 핀 클릭 시 핀 크기가 커짐
  • InfoDetailModal 열고 닫는 기능 구현, 전역 변수로 관리
  • 상세 정보 모달에 실제 데이터베이스에서 가져온 데이터 출력
  • 상세 정보 모달에 넘치는 글자 애니메이션 적용

🖥️ 구현 내용

Untitled

  • 다른 핀을 클릭했을 때 이미 클릭되었던 핀 클릭 해제
  • 핀을 클릭하면 해당 핀과 관련된 정보를 담은 모달창을 출력

📖 학습 내용

marquee, keyframes

🩺 의사결정

Marker 컴포넌트 분리

  • 기존 로직에는 Map 컴포넌트에 네이버 지도를 렌더하는 로직과 마커를 그리는 로직이 같이 있어 한 컴포넌트가 많은 일을 함

  • Map 컴포넌트에서는 마커를 찍을 때 필요한 정보들(위도/경도, 인구 밀도 정보 등)만 가져오고 그 데이터를 Marker 컴포넌트에 내려주기

    Map 컴포넌트는 네이버 지도를 그리고 데이터를 불러오는 로직만 가지고 있고, Marker 컴포넌트는 마커를 그려주기만 하면 된다.

  • Marker 와 관련된 비즈니스 로직과 렌더링 코드를 분리

이전에 선택된 마커 해제 후 현재 선택된 마커 포커싱

  1. 이전에는 naver.maps.Marker 들의 배열이 있었으나, 리팩토링 이후 응답으로 받은 데이터의 배열(areas)을 map 을 이용하여 Marker 컴포넌트를 생성함

    naver.maps.Marker 배열이 없음

  2. 선택된 marker의 인덱스를 저장할 수 없으므로 선택된 marker가 무엇인지 찾으려면 areas 배열을 순회하며 찾아야 함

    → 너무 비효율적

  3. 상위 컴포넌트인 Map 컴포넌트에서 onClickMarker 함수를 Marker 컴포넌트의 props로 내려줌

    • Marker 컴포넌트에서는 현재 marker가 클릭되면 marker의 크기를 크게 함
    • onClickMarker 함수
      1. 이전에 선택된 marker(prevPlace.current.marker)의 크기를 작게 함
      2. 지금 클릭한 marker를 prevPlace.current.marker 에 저장

    → 이전에 선택된 마커를 저장해놓았기 때문에 순회를 하지 않아도 크기 조정 가능

전역 변수 관리

컴포넌트 구조

MainPage
ㄴ InfoDetailModal
ㄴ Map
	 ㄴ Marker

props 전달 방식

  • Marker 컴포넌트에서 이벤트가 발생하면 InfoDetailModal 이 보여져야 함
  • InfoDetailModal 의 state인 isInfoDetailModalOpen 를 접근해야 함, 관련 함수 접근 방법은
    1. props 드릴링
    2. 전역 변수로 관리
  • 모달창이 열리는 것은 맵을 클릭하거나, 마커를 클릭했을 때 등 관련된 컴포넌트가 많을 것으로 보여 jotai를 사용하여 전역으로 관리

이벤트 위임을 네이버 이벤트로 변경

  • 핀의 클릭 이벤트를 부모(Map)의 이벤트 위임으로 설정하면 문제점이 있음
    • 모바일과 데스크탑에서 동시에 같은 이벤트를 걸어줄 수 없음
    • click 이벤트를 사용하면 데스크탑에서만 작동함
    • touchend 이벤트를 사용하면 모바일에서 자기 마음대로 선택이됨
  • 네이버 마커 이벤트로 click 이벤트를 걸어줌
    • 모바일과 데스크탑에서 동시에 같은 이벤트를 걸어줄 수 있음
    • 모바일에서 자기 마음대로 선택되는 일이 없음

🚧 Trouble Shooting

리액트에서 props가 함수일 때 어떻게 주는지?

  • 함수의 인자에 타입을 설정하고, 함수 자체는 () → void로 설정하면 됨

setState가 안먹히는 이유

  • Map.tsx의 prevPlace를 useState로 선언하면 setState가 안먹힘

    • 그래서 대안으로 useRef로 설정했더니 멀쩡하게 값이 잘 변경됨
    const Map: React.FC<MapComponentProps> = ({ latitude, longitude }) => {
      const mapRef = useRef(null);
      const [naverMap, setNaverMap] = useState<naver.maps.Map | null>(null);
      const [areas, setAreas] = useState<SortAllAreasTypes[]>([]);
      const prevPlace = useRef<PrevPlaceTypes | null>(null);
    }

구조분해 할당 타입 선언

  1. optional

    const onClickMarker = (marker: object, populationLevel: string) => {
    	...
    	const { _nmarker_id: newMarkerId }:{ _nmarker_id?: string } = marker;
    	...
    }
  2. naver.maps.Marker, MarkerObjectTypes

    interface MarkerObjectTypes {
      _nmarker_id?: string;
    }
    
    const onClickMarker = (marker: naver.maps.Marker, populationLevel: string) => {
    	...
    	const { _nmarker_id: newMarkerId }: MarkerObjectTypes = marker;
    	
      prevPlace.current.marker.setIcon({
        content: `<div>${createPinSvg(prevPlace.current.populationLevel)}</div>`,
        size: new naver.maps.Size(35, 50),
        anchor: new naver.maps.Point(17.5, 50),
        origin: new naver.maps.Point(0, 0)
      });
    	...
    }
    • MarkerObjectTypes 커스텀 타입을 만들고 marker 타입을 naver.maps.Marker로 바꾸어줌
    • (prevPlace.current.marker 의 타입도 naver.maps.Marker 임)
    • 문제점
      • naver.maps.Marker 에는 MarkerObjectTypes 가 존재하지 않아 _nmarker_id 를 불러오지 못함
      • markerMarkerObjectTypes 로 하면 MarkerObjectTypes 에는 setIcon 이 존재하지 않아 해당 메소드 사용 불가
  3. MarkerObjectTypes에 setIcon 추가

    interface MarkerObjectTypes {
      _nmarker_id?: string;
      setIcon: (
        icon:
          | string
          | naver.maps.ImageIcon
          | naver.maps.SymbolIcon
          | naver.maps.HtmlIcon
      ) => void;
    }
    
    const onClickMarker = (marker: MarkerObjectTypes, populationLevel: string) => {
    	...
    	const { _nmarker_id: newMarkerId } = marker;
    	
      prevPlace.current.marker.setIcon({
        content: `<div>${createPinSvg(prevPlace.current.populationLevel)}</div>`,
        size: new naver.maps.Size(35, 50),
        anchor: new naver.maps.Point(17.5, 50),
        origin: new naver.maps.Point(0, 0)
      });
    	...
    }
    • setIcon 을 포함한 MarkerObjectTypes 인터페이스 생성
    • (prevPlace.current.marker 의 타입도 MarkerObjectTypes 임)

못다한 일

  • 핀 이외의 지도 영역을 클릭했을 때 핀 포커싱 해제

참고 사이트

⚠️ **GitHub.com Fallback** ⚠️