MenuPicker 개발기 #2 - React에서 현재 위치 불러오기 & Kakao API를 써서 위치 검색하기

안녕하세요! 지난 글을 쓰고 약 한 달 반이 흘러갔네요,, 직장에서 일도 하고 또 진행하는 다른 프로젝트들도 여러 개 있어서 글 쓸 기회가 너무 없었습니다ㅠ 특히 10월 그리고 11월이 정말 바빴는데 이때 있었던 일들도 또 다른 게시글로 적어보도록 하겠습니당

 

오늘은 지난 글에 이어서 MenuPicker 에 대해서 적어보려고 합니다~~

 

2025.10.06 - [Frontend] - MenuPicker 개발기 #1 - React에서 QR 코드 생성 및 기기 환경 판별 기능 구현하기

 

MenuPicker 개발기 #1 - React에서 QR 코드 생성 및 기기 환경 판별 기능 구현하기

추석 연휴 중이라 간단한 기술을 기록해보겠습니다! 제가 토이 프로젝트로 진행했던 서비스에서 사용했던 기술이고 또 쓰게 될 것 같아서 기록해놓겠습니다. 깃허브 레포지토리 주소https://github

nyeonseok.tistory.com


1. 배경


우선 MenuPicker를 만들게 된 이유는 토이프로젝트로 기술 학습을 위함도 있지만 제가 술집이나 밥집을 갈 때 어딜 가야할 지 모르겠을 때가 너무 많아서 만들게 됐어요. 주요 흐름은 다음과 같아요.

카테고리 선택(술집 or 밥집) => 주소 입력 => 원하는 프롬프트 작성 => gemini가 해당 주소 + 프롬프트 통해 결과 제공

 

여기서 주소 입력 부분을 오늘 글로 작성해보려고 합니다!

 

2. 현재 위치 불러오기


주소를 입력하는 단계에선 단순히 버튼 클릭으로 현재 위치 불러오기 그리고 직접 검색하여 주소 선택하기 이렇게 두 기능을 넣었어요. 현재 위치 불러오는 기능부터 알아볼게요.

 

다양한 다른 기술들이 있겠지만 저는 웹에서 기본으로 제공하는 navigator.geolocation를 사용했어요.

우선 코드부터 바로 볼게요!

  const getLocation = () => {
    if (!navigator.geolocation) {
      setText("현재 브라우저에서 위치 정보를 지원하지 않습니다.");
      setIsOpen(true);
      return;
    }

    navigator.geolocation.getCurrentPosition(
      (position) => {
        const { latitude, longitude } = position.coords;
        console.log("위도:", latitude, "경도:", longitude);
        // 여기서 주소 API 호출 가능
        getAddressFromCoords(latitude, longitude);
      },
      (error) => {
        console.error(error);
        setText("위치 정보를 가져오는데 실패했습니다.");
        setIsOpen(true);
      }
    );
  };

 

우선 코드를 보시면 알겠지만 여기서는 현재 위치의 위도와 경도를 제공해줍니다.

 

그런데 우리는 위도 경도만 보고선 어디에 있는지 알 수가 없죠? 그래서 이 위도, 경도를 가지고 현재 위치를 뽑아오는 Kakao API를 써줬어요. 그 부분이 바로 getAddressFromCoords(latitude, longitude); 입니다!

 

const getAddressFromCoords = async (lat: number, lng: number) => {
    try {
      const response = await fetch(
        `https://dapi.kakao.com/v2/local/geo/coord2address.json?x=${lng}&y=${lat}`,
        {
          method: "GET",
          headers: {
            Authorization: `KakaoAK ${import.meta.env.VITE_KAKAO_REST_API_KEY}`,
          },
        }
      );

      const data = await response.json();
      if (data.documents && data.documents.length > 0) {
        const address = data.documents[0].address.address_name;
        // console.log("현재 주소:", address);

        const keyword = address.split(" ").slice(0, 3).join(" ");
        setInputValue(keyword);
        handleAddress(keyword);
        return address;
      } else {
        console.log("주소를 찾을 수 없습니다.");
        return "";
      }
    } catch (err) {
      console.error(err);
      return "";
    }
  };

 

기본적인 API 사용 함수입니다. 불러온 주소를 검색창에 띄울거기 때문에 주소를 찾을 수 없거나 error가 있을 떄는 빈 문자열을 return해줬어요. setInputValue(keyword);로 검색창에 받은 주소를 띄워줬습니다!

 

그런데 혹시라도 위도, 경도가 조금 차이가 날 수도 있잖아요? 그래서 이 주소를 가지고 주변에 존재하는 주소들을 받아와서 리스트로 보여줬습니다!  handleAddress(keyword);로 호출하고 있어요.

 

const handleAddress = async (query: string) => {
    if (!query) return;

    try {
      const resultsArr: any[] = [];

      for (let page = 1; page <= 3; page++) {
        const response = await fetch(
          `https://dapi.kakao.com/v2/local/search/keyword.json?query=${query}&size=15&page=${page}`,
          {
            method: "GET",
            headers: {
              Authorization: `KakaoAK ${
                import.meta.env.VITE_KAKAO_REST_API_KEY
              }`,
            },
          }
        );
        const data = await response.json();
        resultsArr.push(...(data.documents || []));
      }

      setResults(resultsArr);
    } catch (error: any) {
      console.error(error.message);
    }
  };

 

이제 정말 끝입니다.. 마찬가지로 Kakao keward API를 사용해서 근처 위치들을 리스트로 뽑아서 보여줬어요.

다시 정리해보면, 

내위치 불러오기 => 받은 위도, 경도로 주소 불러오기 => 받은 주소 근처의 주소도 불러와서 리스트로 띄우기

 


UI로 한번 확인해볼게요

 

주소 검색 첫 진입
검색창 focus
내 위치 눌렀을 때 권한 요청
현재 기기가 있는 위치 기반으로 탐색된 결과

 

 

3. 검색으로 위치 불러오기


주소 검색으로 불러오는 부분은 위에서 쓴 함수를 또 같이 씁니다!

 

먼저 input 태그에서 onChange로 값을 받아와요. useState를 사용했습니다!

          <input
            type="text"
            value={inputValue}
            className={addressStyle.addressInput}
            placeholder="주소 또는 장소를 입력해주세요"
            onChange={(e) => {
              setInputValue(e.target.value);
              // handleAddress(e.target.value);
            }}
            onFocus={() => setShowLocation(true)}
          />

 

그리고 제가 만든 커스텀 훅인데 간단히 설명하면 inputValue가 바뀌고 0.5초 뒤에 debouncedValue가 바뀌는 거에요. 그리로 debouncedValue가 바뀔 때 실행 할 수 있도록 useEffect를 사용했습니다. 참고로 커스텀 훅 사용한 이유는, inputValue가 바뀔 때마다 계속 함수 호출을 하면 과부하 가능성이 있기 때문이에요. 

  const debouncedValue = useDebounce(inputValue, 500);

  useEffect(() => {
    if (debouncedValue) {
      handleAddress(debouncedValue);
    } else {
      setResults([]);
    }
  }, [debouncedValue]);

 

아까 위에서 봤던 handleAddress();를 사용해서 주소를 호출합니다! 그리고 마찬가지로 리스트로 보여줘요.

 

검색창에 검색할 때, 0.5초 뒤에 함수 실행
연관된 검색어가 리스트에 보임

 

4. 마무리


네 사실 그렇게 대단한 기술은 아닙니다 ㅎㅎ

 

여기서 가장 배울 수 있었던 점은 바로 위에서 언급한 커스텀 훅인데요, 커스텀 훅을 쓰기 전에는 input에 값을 적을 때마다 함수호출을 해서 Network보면 너무 많이 찍히더라고요,,

 

그래서 성능 개선을 위해 리팩토링한 부분이고 다음에 기회가 된다면 커스텀 훅도 설명해보겠습니다!

 

감사합니다!