Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Kimyeongkyung

네이버 오픈 검색 API로 장소 검색 기능 구현하기 본문

사이드 프로젝트

네이버 오픈 검색 API로 장소 검색 기능 구현하기

yeongk0825 2023. 12. 31. 17:15

혼자 진행하고 있는 사이드 프로젝트가 있는데 장소명을 검색해서 지도로 표시하는 기능이 필요했다.

알아보니 지도관련 기능은 Naver Cloud Platform의 지도 api를 활용하면 될 것 같았고 장소명을 검색하는건 Naver Developers 사이트의 네이버 오픈 검색 api를 써보기로 했다.

시작하기에 앞서 머릿속으로 순서를 좀 구성해봤다.

1. 장소명을 검색 -> 검색결과 리스트에서 원하는 장소 선택

2. 선택한 장소의 주소정보를 백엔드에 전달
(장소명 만으로는 네이버 api로 위도,경도값을 알아내기 어려운 것 같았다.)

3. 백엔드에서 해당 장소의 주소 정보를 받아서 위도, 경도값으로 변환 후 데이터베이스에 저장

4. 유저가 선택한 장소를 보여줄 상세 페이지에서 위도, 경도값을 활용해 지도로 위치 보여주기

 

먼저 장소명을 검색하고, 그에 따른 검색 결과를 가져오기 위해 Naver Developers에서 애플리케이션을 등록해야한다.

애플리케이션 등록 클릭 후 내 애플리케이션 이름을 적고, 사용할 네이버 api를 선택한다.

 

나는 검색 api를 사용할 것이기 때문에 상단에 검색을 선택해줬다. 이거 제대로 선택 안하고 다른 거 선택한 후 api 연결하려고 시도하면 권한 관련 에러난다. 꼭 내가 사용할 api 선택하기!

만약 권한 관련 에러가 났는데 api key도 제대로 입력했다면? 이 부분을 의심해보는 것도 좋을 것 같다.

간단하게 애플리케이션을 등록한 후에는 Client ID와 Client Secret key를 받을 수 있다.

 

 

이제 vscode로 넘어간다. 나는 백엔드에서 관리하기로 결정했기 때문에 node.js 프로젝트에 장소명 검색 api를 하나 새로 만들었다.

프론트에서 /searchPlace 로 post요청을 보내면 서버에서 네이버 api로 get요청을 보내는 방식이다.

 

//프론트 코드
const Place = () => {
  const [query, setQuery] = useState("");
  const [searchResults, setSearchResults] = useState([]); // 검색 결과 상태
  //백엔드로 전달할 title과 address(장소값 전달할 때 selectedPlace값 사용)
  const [selectedPlace, setSelectedPlace] = useState({
    title: "",
    address: "",
  });

  // 장소 검색 - 네이버 오픈 api
  const handleSearch = async () => {
    try {
      const response = await axios.post("http://localhost:4000/searchPlace", {
        placeName: query,
      });
      setSearchResults(response.data.places);
    } catch (error) {
      console.error("Error searching for place:", error);
    }
  };

// 검색 결과 리스트 중 하나 선택
  const handlePlaceSelection = (title, address) => {
    // 사용자가 장소를 선택했을 때 실행되는 함수
    setSelectedPlace({ title, address });
    setQuery(title); // 선택한 장소의 title을 인풋에 설정

    // 선택한 장소를 콘솔에 출력
    console.log("Selected place:", { title, address });
    // 장소 선택 후에는 검색 결과 리스트 닫기
    setSearchResults([]);
  };

  const handleNext = () => {
    // 여기서 필요한 데이터 수집
    const newPlaceData = {
      // 장소명, 주소값 전달
      placeName: selectedPlace.title,
      address: selectedPlace.address,
      // 이 외에 전달할 정보 추가 
    };
    //setFormData : 작성 페이지가 단계가 나뉘어져 있어 사용하는 props
    setFormData((prevData) => {
      return {
        ...prevData,
        placeInfo: { ...prevData.placeInfo, firstPlace: { ...newPlaceData } },
      };
    });
    // 다음 단계로 이동하는 함수
    handleNextStep();
  };
  
    return (
    //이전 코드 생략
    
    <div className="label">주소</div>
      <Search>
        <Input
          type="text"
          id="id"
          value={query}
          style={{ fontSize: "14px" }}
          placeholder="장소명을 입력해주세요."
          onChange={(e) => setQuery(e.target.value)}
        ></Input>
        <CheckBtn onClick={handleSearch}>검색</CheckBtn>{" "}
        {/* 검색 결과 표시 */}
        <WrapSearchResult showResults={searchResults.length > 0}>
          {searchResults?.length > 0 && (
            <>
              {searchResults.map((result) => (
                <div
                  onClick={() =>
                    handlePlaceSelection(
                      result.title.replace(/<[^>]*>/g, ""),
                      result.address
                    )
                  }
                  key={result.id}
                  className="content"
                >
                  <SearchResultTitle>
                    {result.title.replace(/<[^>]*>/g, "")}
                  </SearchResultTitle>
                  <SearchResultAddress>
                    {result.address}
                  </SearchResultAddress>
                </div>
              ))}
            </>
          )}
        </WrapSearchResult>
      </Search>
      )
  }

 

//백엔드 코드
const app = express();
const port = 4000;
const axios = require("axios");

//장소명 검색
app.post("/searchPlace", async (req, res) => {
  const { placeName } = req.body;
//display : 검색 결과 개수
  try {
    const response = await axios.get(
      `https://openapi.naver.com/v1/search/local.json?query=${placeName}&display=10`,
      {
        headers: {
          "X-Naver-Client-Id": "발급 받은 Client ID",
          "X-Naver-Client-Secret": "발급 받은 Client Secret",
        },
      }
    );
    //응답값 프론트에 전달
    res.json({ places: response.data.items });
    console.log("응답값", response.data);
  } catch (error) {
    console.error("Error searching for place:", error);
    res.status(500).json({ error: "Internal Server Error" });
  }
});

// 서버 시작
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

 

프론트로부터 장소에 대한 정보를 받아 위도, 경도 값을 저장하고, 지도로 표시하는 과정은 다음 편에 기록해야겠다.

 

2024.1.9 수정

 

생각해보니 내가 필요한 위도, 경도값이 네이버 오픈 api로 받아오는 데이터에 다 담겨있다.

geocode를 사용하지 않아도 될 것 같아서 방법을 바꿨다.

1. 백엔드로부터 장소 검색 결과 데이터를 받을 때 모든 정보를 받는다.

2. 프론트에서 보여줄 정보만 ui에 뿌리고, 검색결과 리스트 중 유저가 선택한 값에 대한 정보를 다시 백엔드로 모두 전달한다.

(이 데이터안에 장소에 대한 위도, 경도를 비롯한 모든 정보가 담겨있다.)

3. 데이터베이스에 저장

4. 지도를 보여줄 상세 페이지에서 Naver Dymamic Map api 이용해서 백엔드로부터 받아온 위도, 경도값을 넣어서 지도로 표시