[LG CNS AM Inspire Camp 1기] React (13) - [LAB] 근처 카페 검색 웹 어플리케이션 끄적이기 (2)

정성엽·2025년 1월 12일
0

LG CNS AM Inspire 1기

목록 보기
23/53
post-thumbnail

INTRO

이전 포스팅에서 Naver Map API의 명세를 확인하지 않아서 삽질한 내용을 정리했다.

주소 기반 검색 으로 근처 OO을 찾아 화면에 보여주는 웹 어플리케이션을 만들어보자


7. 현재 위치 찍어보기

카페를 찾는다면 내 주변에 있는 카페만 찾으면 되지 않을까?라는 생각이 든다.

그래서 우선 지도에서 현재 내 위치가 렌더링되도록 코드를 작성해보자

지도에서 현재 위치 렌더링

 const onSuccessGeolocation = (position) => {
    if (mapInstance.current) {
      setAddress({
        x: position.coords.longitude,
        y: position.coords.latitude,
      });
    }
  };

  const onErrorGeolocation = () => {
    alert("위치 정보를 가져오는데 실패했습니다.");
  };

  useEffect(() => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        onSuccessGeolocation,
        onErrorGeolocation
      );
    }
  }, []);

이전 코드에서 추가된 부분만 가져와봤다.

HTML5 Geolocation API 활용하기를 참고해서 만들어본 코드인데, js의 navigator API를 사용해서 현재 위치 위도와 경도를 가져올 수 있다.

해당 코드는 가져온 위도/경도를 address에 세팅해주는 코드이다.

그러면 지도가 최초 마운트되는 시점에 내 위치를 기반으로 렌더링된다.

(그냥 사용하는 방법만 알아도 괜찮을 것 같다)

전체 코드는 다음과 같으니 대충 훑어보자

전체 코드

const NaverMap = ({ searchKeyword }) => {
  const mapElement = useRef(null);
  const mapInstance = useRef(null);
  const { naver } = window;
  const [address, setAddress] = useState({
    x: 127.105399,
    y: 37.3595704,
  });

  const onSuccessGeolocation = (position) => {
    if (mapInstance.current) {
      setAddress({
        x: position.coords.longitude,
        y: position.coords.latitude,
      });
    }
  };

  const onErrorGeolocation = () => {
    alert("위치 정보를 가져오는데 실패했습니다.");
  };

  useEffect(() => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        onSuccessGeolocation,
        onErrorGeolocation
      );
    }
  }, []);

  useEffect(() => {
    if (searchKeyword) {
      naver.maps.Service.geocode(
        {
          query: searchKeyword,
        },
        function (status, res) {
          if (res.v2.addresses.length === 0) {
            alert("검색 결과가 없습니다.");
          } else {
            const resAddress = res.v2.addresses[0];
            const x = parseFloat(resAddress.x);
            const y = parseFloat(resAddress.y);
            setAddress({ x, y });
          }
        }
      );
    }
  }, [searchKeyword]);

  useEffect(() => {
    if (!mapElement.current) return;

    if (mapInstance.current) {
      mapInstance.current.destroy();
    }

    mapInstance.current = new naver.maps.Map(mapElement.current, {
      center: new naver.maps.LatLng(address.y, address.x),
      zoom: 16,
    });
  }, [address]);

  return (
    <div ref={mapElement} style={{ width: "100%", height: 400 }}>
      NaverMap
    </div>
  );
};

export default memo(NaverMap);

8. 카페 검색은 어떻게?

이제 현재 위치의 위도와 경도가 address에 저장되었으니 이를 기반으로 근처 카페를 검색해서 화면에 뿌려주면 된다.

그런데 검색은 어떻게 하지..??

지금 내가 작성하고 있는 Naver Map API는 다시말하지만 주소를 입력해야 보여준다.

내가 근처 카페의 모든 도로명 주소를 알고 있는 것도 아니라서 검색을 어떻게하지 라는 고민이 조금 있었다.

해결 방법으로 네이버 검색 API를 사용하기로 했다.

해결 방안

  • 네이버 검색 API로 근처 가게 검색
  • 검색된 가게의 위도와 경도를 파싱해서 지도에 넘겨주기

9. Naver Search API 등록

다행히 명세를 찾아보다가 관련 API를 찾을 수 있었다.
Naver Search API - Local에서 살펴보면 다음 사진과 같은 명세를 볼 수 있다.

API 명세

다행히도 위도 경도로 변환이 가능한 어떤 데이터를 넘겨준다.

이걸 사용하기 위해 Naver에 또 API 사용 요청을 해야했다.

마찬가지로 Client ID가 할당된다.

이제 이걸 .env.development에서 관리하고 관련 API를 만들어보자


10. CORS

Naver Search API 명세와 같이 코드를 작성했다.

API 호출 코드

// searchInstance.js
import axios from "axios";

const searchInstance = axios.create({
  baseURL: import.meta.env.VITE_NAVER_SEARCH_BASE_URL,
  headers: {
    "X-Naver-Client-Id": import.meta.env.VITE_NAVER_CLIENT_ID,
    "X-Naver-Client-Secret": import.meta.env.VITE_NAVER_CLIENT_SECRET,
  },
});

export default searchInstance;

import searchInstance from "./searchInstance";

export const searchBlog = async (query) => {
  try {
    const response = await searchInstance.get("/v1/search/blog", {
      params: { query },
    });
    return response.data;
  } catch (error) {
    console.error("Error : ", error);
    throw error;
  }
};

// App.jsx
...
const response = await searchBlog("맛집");
...

Console View

우선 search Blog API를 사용해서 호출해봤는데 위 사진처럼 오류가 뜨는 모습을 볼 수 있었다.

Network Error가 뜨는 모습을 볼 수 있었고 찾아보니 CORS 문제였다.

그렇다면 왜 CORS 문제가 발생했을까?

CORS 원인

차단되는 경우

  • 브라우저 -> 네이버 API 호출

허용되는 경우

  • 서버 -> 네이버 API 호출

CORS의 문제는 브라우저의 보안정책에 의해 네트워크 에러가 발생하는 것이었다.

정확하게 말하면 네이버 서버쪽으로 Request가 가지 못하는데 브라우저에서 들어오는 요청을 허용하지 않기 때문이다.

이를 해결하기 위해서는 우회하면 된다.

즉, 프록시 서버를 사용하여 문제를 해결할 수 있다.

CORS 해결

  • 브라우저 -> 프록시 서버 -> 네이버 API 호출

브라우저 단에서 막히기 때문에 서버에서 요청이 진행되도록 설정을 변경해주면 된다.

vite를 사용해서 개발환경을 구축했으니 vite.config.js를 수정해주면 된다.

vite.config.js

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      "/v1": {
        target: "https://openapi.naver.com",
        changeOrigin: true,
      },
    },
  },
});
  • 참고로 config.js에서는 환경변수를 직접적으로 불러올 수 없다!
  • config가 환경변수보다 더 빨리 세팅되기 때문이다.

프록시 서버를 사용하게 됨에 따라 관련 API 호출 코드도 수정해야 한다.

searchInstance.js

import axios from "axios";

const searchInstance = axios.create({
  baseURL: "", // baseURL을 없애버림
  headers: {
    // 환경변수는 여기서 로드
    "X-Naver-Client-Id": import.meta.env.VITE_NAVER_CLIENT_ID,
    "X-Naver-Client-Secret": import.meta.env.VITE_NAVER_CLIENT_SECRET,
  },
});

export default searchInstance;

Console View

  • 오이도 로 검색하니 관련 블로그가 제대로 응답되는 것을 볼 수 있었다.

지금까지 검색 API와 지도 API가 제대로 호출되는 모습을 확인할 수 있었다.

이제부터는 검색된 가게의 위도/경도를 지도 API에 넘겨서 화면에 보이도록 해보자 (다음 포스팅에서)


OUTRO

개발할 때는 슥슥 넘어간 부분인데 막상 블로그로 정리하려니 생각보다 시간이 많이 걸린다.

비율로 따지면 개발 : 포스팅 = 1 : 1.5 느낌이다.

이번에는 CORS를 정리했는데 꽤 중요한 내용이라 기록하는데 의미가 있었다.

axios를 사용할 때 인스턴스를 사용하는 이유는 필요한 정보를 미리 지정할 수 있기 때문이다.

인스턴스와 인터셉터는 다른 목적으로 사용되니 알아두자


참고
HTML5 Geolocation API 활용하기

profile
코린이

0개의 댓글

관련 채용 정보