24/1/30 카카오맵 API

2024. 1. 31. 08:48카테고리 없음

 

<MapHome.tsx> 오늘은 카카오 맵 API 적용한걸 리뷰해보자.

<"use client";
import { typeOfShop } from "@/app/assets/types/types";  //가게 하나의 type정의 해둔걸 임포트
import { nanoid } from "nanoid";      //map함수 돌렸을떄 최상단 태그의 key값을 나노아이디로 할당했다.
import { useRouter } from "next/navigation";
import React, { ReactNode, useEffect, useRef, useState } from "react";
import { Map, MapMarker } from "react-kakao-maps-sdk";   //카카오맵의 주요태그인 맵과 맵마커
import { useDispatch, useSelector } from "react-redux";
import useKakaoLoader from "../detail/useKaKao";         //최상단에 실행시켜야 카카오맵 기능함
   
import ColumnSlide from "./ColumnSlide";    
import { RootState } from "@/redux/config/configStore";      //툴킷에서 state들을 보관하고 있는 루트스테이트
import { getShop } from "@/redux/modules/detailShopSlice";
import ShopCard from "../home/ShopCard";
import Shopinfo from "../detail/Shopinfo";
import ShopCard2 from "./ShopCard2";
import Image from "next/image";
import upButton from "../../app/assets/images/icon/up.png";
import downButton from "../../app/assets/images/icon/down.png";

type typeOfRef = {       //useRef를 아래에서 쓸건데 그에 대한 타입 선언
  title: string;
  latitude: string;
  longitude: string;
};

export default function MapHome() {
  useKakaoLoader();                              //카카오맵을 실행시키기위한 기초작업
  const shops = useSelector((state: RootState) => state.shops);  //가게들에 대한 정보를 툴킷에서 가져옴
  const router = useRouter();
  const mapCenterRef = useRef({ 시군: "", 시도: "" });  //맵 센터에 대한 참조
  if (shops[0]) {
    const { 시군, 시도 } = shops[0];
    mapCenterRef.current = { 시군, 시도 };   //shops 라는 배열에 첫번째 요소가 있다면
  }                                                                     //그 시군과 시도 정보를 ref로 저장해둠
  const [slide, setSlide] = useState(0);
  const [lat, setLat] = useState(0);
  const [lng, setLng] = useState(0);
  const [render, setRender] = useState(false);
  const [infoToggle, setInfoToggle] = useState(false);        //아래에서 쓰일 useState들
  // const latRef = useRef(0);
  // const lngRef = useRef(0);
  const shopsRef = useRef<typeOfRef[]>([]);
  const shopInfoRef = useRef<typeOfShop>(shops[0]);       //아래에서 쓰일 useRef들
  // const [newArray, setnewArray] = useState<typeOfRef[]>([]);
  const dispatch = useDispatch();                  //툴킷의 state 변화를 일으킬 dispatch 설정
  class NewShops {                                   //실전 프로젝트에서 class문법 처음써봤는데 신박했다.ㅋ
    title: string;
    latitude: string;
    longitude: string;
    constructor(title: string, latitude: string, longitude: string) {
      this.title = title;
      this.latitude = latitude;
      this.longitude = longitude;                                     //이 붕어빵틀로 객체라는 붕어빵을 뽑아낸다.
    }
  }

  useEffect(() => {
    if (!shops[0]) {
      router.push("/");        //애초에 배열에 값이 없으면 메인 메뉴로 보내서 검색부터 하게 유도
    }
    if (window.kakao) {           //window.kakao의 존재 여부로 시작되는 로직
      let geocoder = new window.kakao.maps.services.Geocoder();          //간결한 변수명으로 할당
      geocoder.addressSearch(`${mapCenterRef.current.시도} ${mapCenterRef.current.시군}`, function (result, status) {
        // console.log(result, "이거 레절트");              //카카오맵의 기능인데 주소를 넣으면 위도 경도를 가진 
        setLng(+result[0].x);                                   //result로 반환함
        setLat(+result[0].y);                                   //그 위도 경도가 string형이므로 number형으로 바꿔서 state변경
        // latRef.current = Number(result[0].y);
        // lngRef.current = Number(result[0].x);
      });

      let mappedArray: {                    //이런 형식의 array를 하나 만든다.
        title: string;
        latitude: string;
        longitude: string;
      }[] = [];
      for (let i = 0; i < shops.length; i++) {         //배열매서드는 비동기적 통신을 안기다려준다는 말이있어서
        let OBOB = {                                  //포문을 돌려봤다. 차이를 체감하지는 못했다.
          title: "",
          latitude: "",
          longitude: ""
        };
        geocoder.addressSearch(shops[i].주소, function (result, status) { //shops.배열안에 모든 shop들의
          OBOB.title = shops[i].업소명;                                      //주소정보로 뒤도경도를 뽑아내려고 한다.
          if (result[0]) {
            OBOB.latitude = result[0].y;
            OBOB.longitude = result[0].x;
          }
        });
        mappedArray.push(OBOB);                                     //이제 빈배열에 push하고
      }

      shopsRef.current = mappedArray;             //그것을 useRef에 담는다.
    }
  }, [shops]);                                 //디펜던시 어레이는 shops로 하여 shops의 state를 구독한다.
  // console.log(newArray, "나오겠지?");

  // const moveToDetail = (title: string) => {
  //   const oneshop = shops.find((shop: typeOfShop) => {
  //     return shop.업소명 === title;
  //   });
  //   // console.log(oneshop);
  //   if (oneshop) {
  //     dispatch(getShop(oneshop));
  //     router.push(`/detail/${oneshop.연락처}`);
  //   } else alert("존재하지 않아요");
  // };

  useEffect(() => {                              //일단 임시로 해둔 화면 랜더링을 1초뒤에 다시하기위한로직
    setTimeout(() => {
      setRender((prev) => {
        return !prev;
      });
    }, 1000);
  }, []);

  // const wheelHandler = (e: React.WheelEvent<HTMLDivElement>) => {
  // e.preventDefault();
  // console.log(e.clientY, "클라와이");
  // console.log(e.deltaY, "델타와이");
  // console.log(e.movementY, "무브먼트와이");

  //   setSlide((prev) => {
  //     if (
  //       slide >= 0 ||
  //       Math.ceil((shops.length / 4) * 1000) + slide - 1500 <= 0
  //     )
  //       return prev;
  //     return prev + e.deltaY;
  //   });
  // };

  const upHandler = () => {                            // column슬라이드를 위로 올리기 위한 로직
    if (slide >= 0) return;
    setSlide(slide + 500);
  };

  const downHandler = () => {                              //column슬라이드를 아래로 내리기 위한 로직
    if (Math.ceil((shops.length / 4) * 1000) + slide - 1500 <= 0) return;
    setSlide(slide - 500);                                      //지금은 column슬라이드 안에 따로 함수를 넣어서
  };                                                              //필요없어서 지워야됨 ㅋㅋㅋ 지금 발견함

  const openShopInfo = (title: string) => {
    const foundShop = shops.find((shop: typeOfShop) => {       //맵마커를 누를시 매개변수로 받은값과
      return shop.업소명 === title;                                         //shops 중 업소명이 같은 shop을 골라내는 작업
    });
    if (foundShop) {
      shopInfoRef.current = foundShop;                        //찾은걸 foundShop에 담아줌
    }
    setInfoToggle((prevInfoToggle) => !prevInfoToggle);     //여기서 인포토글을 통해 shopcard를 온오프
  };
  return (
    <>
      <div className="w-screen h-screen bg-yellow-300">
        <div className="bg-green-300 absolute top-[75px] left-[450px] z-10">{/* <SearchForm /> */}</div>
        <Map
          onClick={() => setInfoToggle(!infoToggle)}   //맵을 누를시 온오프
          // ref={mapRef.current}
          className="bg-yellow-100" // 지도를 표시할 Container
          id="map"
          center={{
            // 지도의 중심좌표
            lat,                               //key 와 value가 같기 때문에 하나로 통일
            lng
          }}
          style={{
            // 지도의 크기
            width: "100%",
            height: "100%"
          }}
          level={6} // 지도의 확대 레벨
        >
          {shopsRef.current.map((shop: typeOfRef) => {         //shopsRef로 저장된 값으로 화면에 맵마커들을
            return (                                                         //뿌려준다.
              <MapMarker
                key={nanoid()}       //key값 딱히 사용처가 없어서 일단 nanoid부여
                position={{ lat: +shop.latitude, lng: +shop.longitude }}
                title={shop.title}
                onClick={() => openShopInfo(shop.title)}       
              >
                {/* <div className="flex justify-center items-center w-[140px] p-[5px] m-[5px] font-black">
                  {shop.title}
                </div> */}
                {infoToggle && shop.title === shopInfoRef.current.업소명 && (
                  <>
                    <ShopCard2 type="map" shops={shops} shop={shopInfoRef.current} />{" "}
                  </>                                             //이곳에서 shopcard를 토글해준다. 
                )}                                                                  //프롭스로 shops와 shop에 대한 정보전달.
                {/* <div>클릭하시면정보나옴</div> */}        //사실 shops정보는 필요없다 .ㅋㅋㅋ
              </MapMarker>                                           //로직짜고나서 한참뒤에 shops정보 왜 프롭스로 줬었지?
            );                                                            //바본가라고 생각함 ㅋㅋ
          })}
          {/* <MapMarker
            position={{
              lat,
              lng,
            }}
            title="현재위치"
            image={{
              src: "https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_red.png", // 마커이미지의 주소입니다
              size: {
                width: 64,
                height: 69,
              }, // 마커이미지의 크기입니다
              options: {
                offset: {
                  x: 27,
                  y: 69,
                }, // 마커이미지의 옵션입니다. 마커의 좌표와 일치시킬 이미지 안에서의 좌표를 설정합니다.
              },
            }}
          /> */}
        </Map>
      </div>

      <div className="absolute top-[64px] z-10 h-full flex items-center">
        <div
          // onWheel={(e) => wheelHandler(e)}
          // className={`flex flex-col h-[900px]`}
          className="flex flex-col  my-[30px]  h-full"
        >
          {/* <div
            style={{
              height: `${Math.ceil(shops.length / 4) * 1000}px`,
              transform: `translate(0,${slide}px)`,
              transition: "transform 0.5s",
              display: "flex",
            }}
          >
          </div> */}
          <ColumnSlide />
        </div>
        {/* <div className="flex flex-col justify-center gap-5 h-screen ml-[20px] ">
          <button
            onClick={upHandler}
            className=" bg-purple-300 rounded-full text-4xl hover:scale-110"
          >
            👆
          </button>
          <Image
            onClick={upHandler}
            src={upButton}
            alt="upButton"
            className="w-[30px] h-[30px] cursor-pointer"
          />
          <button
            onClick={downHandler}
            className="bg-purple-300 rounded-full text-4xl hover:scale-110"
          >
            👇
          </button>
          <Image
            onClick={downHandler}
            src={downButton}
            alt="downButton"
            className="w-[30px] h-[30px] cursor-pointer"
          />
        </div> */}
      </div>
    </>
  );
}

//일단 미완성 로직이다. 
//이유는 이미 알고 있다. 화면에 랜더링시 마커가 제대로 찍히지 않거나 해당 값이 없다고 뜬다.
//이유는 카카오맵 API에서 주소로 위도경도를 찾아주는것은 비동기 로직인데 이 프로미스값을 기다리지 않고
//다음 로직이 작동 되기 때문이라고 추측한다.
//일단 결과적으로 에러는 뜨지만 1초뒤 정상적으로 마커를 그려주기 때문에 후순위로 보류해뒀다.