The Debugging Chronicles : "코드의 미학"

[카카오 지도 API] 2. 화면에 보이는 곳에만 필터링하여 마커 찍기(Next.js,TypeScript) 본문

FrontEnd

[카카오 지도 API] 2. 화면에 보이는 곳에만 필터링하여 마커 찍기(Next.js,TypeScript)

sweetseonah1004 2025. 1. 14. 17:21

 

2.5주 동안 카카오지도와 씨름하면서 가장 힘들었던 필터링하여 적용하기

 

2. 화면에 보이는 곳에만 필터링하여 마커 찍기

이전 글 참고.

https://sweetseonah1004.tistory.com/203

화면 반경 내에 마커를 찍었다.

하지만 반경이 화면의 크키를 넘어서

불필요한 데이터까지 가지고 오고 있었다.

 

원이 찍그러졌지만

R 반지름 값을 중심으로 원의 반경내의 데이터들을 다 가지고 와서 마커를 찍는 다면 

너무 많은 불필요한 값들이 오고 있다.

 

그래서 필터링이 필요했다.

 

아이디어는

getAllFranchises라는 함수에서

getFranchiesAll API 로부터 응답값을 받아오고,

받아온 데이터를 상태(setAllList)로 업데이트 한다.  

getAllFranchises함수가 쓰여지는 곳 마다

업데이트 된 함수인 getAllFranchises 의 결과 값으로 필터링하는 getAllFranchisesAndFilter 함수로 대체되도록 하는 것이다.

 

  const filterLocation = (
    allList: AllFranchiseResult,
    swLat: number,  // 남서쪽 위도
    swLng: number,  // 남서쪽 경도
    neLat: number,  // 북동쪽 위도
    neLng: number   // 북동쪽 경도
  ) => {
    // 필터링 조건에 맞는 데이터를 반환합니다.
    const result = allList.result.filter((element) => {
      // null 또는 undefined 체크
      if (!element.geo_lat || !element.geo_lng) {
        return false;  // 좌표 정보가 없으면 제외
      }

      const lat = parseFloat(element.geo_lat);  // 위도 값
      const lng = parseFloat(element.geo_lng);  // 경도 값

      // 좌표 범위 내의 데이터만 필터링
      return (
        lat >= swLat && lat <= neLat &&  // 위도: 남 → 북
        lng >= swLng && lng <= neLng     // 경도: 서 → 동
      );
    });

    return result;
  };


  const getAllFranchisesAndFilter = (
    updatedParams: AllFranchiseParam,
    swLatLng: kakao.maps.LatLng,
    neLatLng: kakao.maps.LatLng
  ) => {
    return getAllFranchises(updatedParams)
      .then((rs) => {
  
        if (!rs || !rs.result || rs.result.length === 0) {
          return null;
        }
  
        //데이터 필터링
        const filteredData = filterLocation(
          rs,
          swLatLng.getLat(),
          swLatLng.getLng(),
          neLatLng.getLat(),
          neLatLng.getLng()
        );
  
        // 리스트 업데이트
        setAllList({
          result: filteredData,
          total: filteredData.length.toString(),
        });
  
        return filteredData;
      })
      .catch((error) => {
        return null;
      });
  };

 

그리고 함수가 사용되었던 곳 중에 하나인

체크박스 상태에 따라 데이터 가져오기

  // 체크박스 상태에 따라 데이터 가져오기
useEffect(() => {
  const fetchFilteredData = async (typeValue:string) => {
    resetList('all');

    // 지도 범위 가져오기
    const bounds = await getMapBounds();
    const swLatLng = bounds.getSouthWest();  // 남서쪽 좌표
    const neLatLng = bounds.getNorthEast();  // 북동쪽 좌표

    // 파라미터 업데이트 및 데이터 요청
    setAllFranchiseParams((prev) => {
      const updatedParams = {
        ...prev,
        geo_lat: mapCenter.lat,
        geo_lng: mapCenter.lng,
        type: typeValue,
      };
      getAllFranchisesAndFilter(updatedParams, swLatLng, neLatLng);
      return updatedParams;
    });
  };

  if (isInitialRender) {
    setIsInitialRender(false); // 이후로는 실행되지 않음
    return;
  }

  if (fillters.franchise && fillters.no_franchise) {
    // 가맹점, 비가맹점 모두 선택
    fetchFilteredData('3');
  } else if (fillters.franchise && !fillters.no_franchise) {
    // 가맹점만 선택
    fetchFilteredData('1');
  } else if (!fillters.franchise && fillters.no_franchise) {
    // 비가맹점만 선택
    fetchFilteredData('2');
  } else {
    // 가맹점, 비가맹점 둘다 선택 안됨
    resetList('company');
    resetList('franchise');
    resetList('all');
  }
}, [fillters]);

 

 

그리고 적용하기 제일 애를 먹은 시/도 , 구/군 셀렉트 박스

 

  const selectCity = async (event: SelectChangeEvent) => {
    setFranchiesList({
      result: [],
      total: '0',
    });
  
    let selectedCityName = event.target.value as string;
    let searchCityName = selectedCityName;
  
    // "광주" 선택 시 검색은 "광주광역시"로, UI는 "광주"로 유지
    if (selectedCityName === "광주") {
      searchCityName = "광주광역시"; // 검색용
    }
  
    const currentSelectedCity = searchCityName;
  
    // UI에는 "광주"로 표시
    setSelectedCity(selectedCityName);
    setSelectedGu('');
    await getLocationCodes(selectedCityName);
  
    let updatedParams = {
      ...allFranchiseParams,
      city: selectedCityName,
      location: '',
    };
    setAllFranchiseParams(updatedParams);
  
    if (searchCityName === '') {
      setAllFranchiseParams((prev) => {
        const updatedParams = {
          ...prev,
          city: '',
          location: '',
        };
        // 지도 위치 변경 없이 데이터만 업데이트
        getAllFranchises(updatedParams);  
        return updatedParams;
      });
    }else{
      // ✅ 검색어가 있을 때만 주소 검색 실행
      const res = await kakaoAddressSearch(searchCityName);
    
      if (currentSelectedCity !== searchCityName) {
        return;
      }
    
      if (res.result === true && res.latitude && res.longitude) {
        const latitude = parseFloat(res.latitude);
        const longitude = parseFloat(res.longitude);
    
        if (isNaN(latitude) || isNaN(longitude)) {
          return;
        }
    
        handleCenterChange(latitude.toString(), longitude.toString());
        moveMapCenter(latitude.toString(), longitude.toString());
    
        // 지도 범위를 가져와서 SW/NE 좌표 계산
        const bounds = await getMapBounds();
    
        const swLatLng = bounds.getSouthWest();  // 남서쪽 좌표
        const neLatLng = bounds.getNorthEast();  // 북동쪽 좌표
    
        let distance = getDistance(
          swLatLng?.getLat(),
          swLatLng?.getLng(),
          neLatLng?.getLat(),
          neLatLng?.getLng(),
        ) / 2;
    
        updatedParams = {
          ...updatedParams,
          geo_lat: latitude.toString(),
          geo_lng: longitude.toString(),
          radius: distance < 1 ? 1 : setRadiusAccurate(distance),
        };
    
        await getAllFranchisesAndFilter(updatedParams, swLatLng, neLatLng);
      } else {
        handleCenterChange('37.5665', '126.9780');
        moveMapCenter('37.5665', '126.9780');
    
        const bounds = await getMapBounds();
        const swLatLng = bounds.getSouthWest();
        const neLatLng = bounds.getNorthEast();
    
        await getAllFranchisesAndFilter(updatedParams, swLatLng, neLatLng);
      }
    
      setShouldUpdateCenter(true);
    }
  };

 

const selectGu = async (event: SelectChangeEvent) => {
    setFranchiesList({
      result: [],
      total: '0',
    });
  
    let newLocation = event.target.value as string;
    let searchCity = selectedCity;
  
    // "광주" → "광주광역시"로 변환 (구/군 목록 조회 및 검색 시)
    if (selectedCity === "광주") {
      searchCity = "광주광역시";
    }
  
    let searchLocation = `${searchCity} ${newLocation}`;
  
    // 선택한 구/군 업데이트 (UI에는 원래대로 유지)
    setSelectedGu(newLocation);
  
    // 파라미터 업데이트 (검색용 city는 광주광역시, UI는 광주)
    let updatedParams = {
      ...allFranchiseParams,
      city: selectedCity,    // 검색 시 "광주광역시" 사용
      location: newLocation, // 선택한 구/군
    };
    setAllFranchiseParams(updatedParams);
  
    if (newLocation === '') {
      setAllFranchiseParams((prev) => {
        const updatedparams = {
          ...prev,
          city: selectedCity,
          location: '',
        };
        getAllFranchises(updatedparams);
        return updatedparams;
      });
    } else {
      // 선택한 구/군 주소 검색
      const res = await kakaoAddressSearch(searchLocation);
  
      if (res.result === true && res.latitude && res.longitude) {
        const latitude = parseFloat(res.latitude);
        const longitude = parseFloat(res.longitude);
  
        if (isNaN(latitude) || isNaN(longitude)) {
          return;
        }
  
        // 지도 중심 이동
        handleCenterChange(latitude.toString(), longitude.toString());
        moveMapCenter(latitude.toString(), longitude.toString());
  
        // 지도 범위를 가져와서 SW/NE 좌표 계산
        const bounds = await getMapBounds();

        const swLatLng = bounds.getSouthWest();  // 남서쪽 좌표
        const neLatLng = bounds.getNorthEast();  // 북동쪽 좌표

        let distance =
        getDistance(
          swLatLng?.getLat(),
          swLatLng?.getLng(),
          neLatLng?.getLat(),
          neLatLng?.getLng(),
        ) / 2;

        // 파라미터 업데이트 (좌표, 반경 포함)
        updatedParams = {
          ...updatedParams,
          geo_lat: latitude.toString(),
          geo_lng: longitude.toString(),
          radius: distance < 1 ? 1 : setRadiusAccurate(distance),
        };
  
        // 필터링된 데이터 요청
        await getAllFranchisesAndFilter(updatedParams, swLatLng, neLatLng);
      } else {
        handleCenterChange('37.5665', '126.9780');
        moveMapCenter('37.5665', '126.9780');

        // 지도 범위를 가져와서 SW/NE 좌표 계산
        const bounds = await getMapBounds();

        const swLatLng = bounds.getSouthWest();  // 남서쪽 좌표
        const neLatLng = bounds.getNorthEast();  // 북동쪽 좌표
      
        await getAllFranchisesAndFilter(updatedParams, swLatLng, neLatLng);
      }
    }
  
    setShouldUpdateCenter(true);  // 지도 중심 이동 활성화
  };