NodeJS ๋ฐ React ๐Ÿ”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋™์  ๊ฒ€์ƒ‰ ์ž๋™ ์™„์„ฑ ์ถ”๊ฐ€

12700 ๋‹จ์–ด webdevreactnodejavascript

์†Œ๊ฐœ



Google์˜ ์ž๋™ ์™„์„ฑ ๊ฒ€์ƒ‰ ํ‘œ์‹œ์ค„์ด ์ž…๋ ฅํ•  ๋•Œ ๋‹ค์Œ ํ‚ค ์ž…๋ ฅ์„ ์˜ˆ์ธกํ•˜๋Š” ๊ฒƒ์„ ๋ณด์…จ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.



๊ทธ๋ž˜์„œ ์ €๋Š” ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ค๊ณ  ์ œ๊ฐ€ ๋ฐœ๊ฒฌํ•œ ๊ฒƒ์„ ์—ฌ๋Ÿฌ๋ถ„ ๋ชจ๋‘์™€ ๊ณต์œ ํ•  ์ƒ๊ฐ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
์‹œ์ž‘ํ•˜์ž ๐Ÿš€

Google์˜ ๊ฒ€์ƒ‰ ์ž๋™์™„์„ฑ API



๋‚˜๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํƒ€์ดํ•‘์„ ์‹œ์ž‘ํ•˜๊ณ  ์ผ๋ถ€ ๊ฒ€์ƒ‰ ์ œ์•ˆ์ด ๊ทธ์˜ ์–ผ๊ตด(๋ฌผ๋ก  div์—์„œ)์— ๋‚ ์•„์˜ค๋Š” ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ๊ฒƒ์„ ๊ตฌํ˜„ํ•ด์•ผ ํ•˜๋Š” ์•ฑ์„ ๋งŒ๋“ค๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ช‡ ๊ฐ€์ง€ ๋ฌด๋ฃŒ API๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด Google์—์„œ ๋ฐฉํ™ฉํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ€˜์ŠคํŠธ์—์„œ this ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ ํ† ๋ก ์„ ์šฐ์—ฐํžˆ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉํ•˜๋„๋ก ์ œ์•ˆ๋œ ๋‹ต๋ณ€ ์ค‘ ํ•˜๋‚˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

https://www.google.com/complete/search?client=hp&hl=en&sugexp=msedr&gs_rn=62&gs_ri=hp&cp=1&gs_id=9c&q=a&xhr=t&callback=hello


์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ google.com ์ž์ฒด๊ฐ€ ๊ฒ€์ƒ‰ ์ œ์•ˆ์— ์‚ฌ์šฉํ•˜๋Š” URL์ž…๋‹ˆ๋‹ค.



API ๋์ 



์ด์ œ ํ…Œ์ŠคํŠธ ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ Google์˜ API์— ๋Œ€ํ•œ ์ผ๋ถ€ GET ์š”์ฒญ์„ ์ž‘์„ฑํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ €๋Š” codesandbox.io๊ฐ€ ์ด๋Ÿฌํ•œ ์ž‘์—…์„ ์œ„ํ•œ ๋น ๋ฅธ ํ˜ธ์ŠคํŒ… ์›น ํ™˜๊ฒฝ์„ ์„ค์ •ํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๋ผ๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค.



๊ทธ๋ฆฌ๊ณ .....์ƒˆ๋กœ๊ณ ์นจ!



ํ , ์ด๊ฒƒ์€ CORS ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. NodeJS๋กœ ์ž‘์—…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋น„๊ต์  ์‰ฝ๊ฒŒ ๊ณ ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ;)

"๋ฐฉ๊ธˆ ๋ฌด์Šจ ์ผ์ด์•ผ?"



์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๋Š” ํ—ค๋”Access-Control-Allow-Origin๊ฐ€ ์—†์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ํ—ค๋”๊ฐ€ ์—†๋Š” ์š”์ฒญ์˜ ์‘๋‹ต์€ ์š”์ฒญ์ด 200 OK๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋”๋ผ๋„ ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์ฐจ๋‹จ๋ฉ๋‹ˆ๋‹ค.

The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin.



์ด ํ—ค๋”here์—์„œ ์ž์„ธํ•œ ๋‚ด์šฉ์„ ์ฐพ์œผ์‹ญ์‹œ์˜ค. ์ด ์˜ค๋ฅ˜๋Š” ๋ธŒ๋ผ์šฐ์ € ํ™˜๊ฒฝ์—์„œ๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด์ œ ์ด ์š”์ฒญ ์ž‘์„ฑ ์ฝ”๋“œ ๋ธ”๋ก์„ ๋…ธ๋“œ ํ™˜๊ฒฝ์œผ๋กœ ์ „ํ™˜ํ•œ ๋‹ค์Œ ๋…ธ๋“œ API ๋์ ์— ๋Œ€ํ•œ ์š”์ฒญ ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•œ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ์šฐ์ฒด๋ถ€ ๋˜๋Š” ๋ถˆ๋ฉด์ฆ๊ณผ ๊ฐ™์ด ๋กœ์ปฌ ์‹œ์Šคํ…œ์œผ๋กœ GET ์š”์ฒญ์„ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ์ง€์ •๋œ URL ์ธ์ฝ”๋”ฉ ์ฟผ๋ฆฌ์— ๋Œ€ํ•ด ์ •์ƒ์ ์ธ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹คq.



์ด์ œ ๊ฒ€์ƒ‰ ์ œ์•ˆ์„ ๋ฐ›๊ณ  ์ด๋ฅผ JSON ์‘๋‹ต์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋…ธ๋“œ ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ฝ”๋”ฉํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.



ํ˜ธ์ŠคํŒ… ํ™˜๊ฒฝ์— ์ฆ‰์‹œ ๋ฐฐํฌ๋˜๋Š” ๋น ๋ฅธ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ์„ ์œ„ํ•ด ์ €๋Š” ์ข…์ข… replit.com์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.
์ด ์ฝ”๋“œ์—์„œ๋Š” express(์›น ์„œ๋ฒ„ ๊ฐ€๋™์šฉ) ๋ฐ axios(์ธํ„ฐ๋„ท์„ ํ†ตํ•ด ์†Œ์Šค์— ๋Œ€ํ•œ GET/POST ์š”์ฒญ ์ƒ์„ฑ์šฉ)์„ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ ์ต์Šคํ”„๋ ˆ์Šค ์„œ๋ฒ„/์•ฑ์„ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  .get(url, (request, response) => {}) ๋ฉ”์„œ๋“œ๋กœ API GET ENDPOINT๋ฅผ ์ •์˜ํ•˜๊ณ  axios๋กœ Google์˜ API์— GET ์š”์ฒญ์„ ๋งŒ๋“ค๊ณ  ๋ฐ˜ํ™˜๋œ ์ œ์•ˆ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋ƒˆ์Šต๋‹ˆ๋‹ค.

์ƒˆ๋กœ ๋งŒ๋“  ๋์ ์„ ํ…Œ์ŠคํŠธํ•˜๋ฉด ๋ถ„๋ช…ํžˆ 200 OK ์‘๋‹ต์ด ๋ฐ˜ํ™˜๋˜๊ณ  ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.





ํ•˜์ง€๋งŒ ๋์ ์— ์ž‘์—…์ด ํ•˜๋‚˜ ๋” ๋‚จ์•„ ์žˆ์œผ๋ฉฐ JSON ํ˜•์‹์œผ๋กœ ๋ฉ‹์ง€๊ณ  ๋ฉ‹์ง„ ํ˜•์‹์˜ ๊ฒ€์ƒ‰ ์ œ์•ˆ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.



์ฝ”๋“œ์˜ ์ด ๋ถ€๋ถ„์—์„œ๋Š” ์ธ๋ฑ์‹ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ์˜ โ€‹โ€‹์ฃผ์š” ๋ถ€๋ถ„์„ ์ฐธ์กฐํ•˜๊ณ , ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์ œ ์™„๋ฃŒ ํ…์ŠคํŠธ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ถ€๋ถ„๋งŒ ๊ฐ€์ ธ์˜ค๊ณ , ์ •๊ทœ์‹ ์ผ์น˜์— ๋Œ€ํ•œ ์ผ๋ถ€ null ๊ฒ€์‚ฌ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ API ์‘๋‹ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.


React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ API ๊ตฌํ˜„



์ด์ œ ์‹ค์ œ๋กœ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๊ฒ€์ƒ‰ ํ‚ค ์ž…๋ ฅ์„ ๋™์ ์œผ๋กœ ์ œ์•ˆํ•˜๋Š” ๋ถ€๋ถ„์ด ์™”์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ์ž…๋ ฅ์„ ๋ฐ›์•„ ์ƒํƒœ์— ์ €์žฅํ•˜๋Š” ๊ธฐ๋ณธ ๋ฐ˜์‘ ์•ฑ์„ ์‹คํ–‰ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.



์ด์ œ ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ํ‚ค๋ฅผ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ์ˆ˜์‹ญ ๊ฐœ์˜ ์š”์ฒญ์œผ๋กœ ์—”๋“œํฌ์ธํŠธ์— ์ŠคํŒธ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹œ๊ฐ„ ์ดˆ๊ณผ ๊ธฐ๋ฐ˜ ์š”์ฒญ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

import axios from "axios";
import { useState } from "react";

export default function App() {
  const [searchResults, setSearchResults] = useState([]);
  const [inputText, setInputText] = useState("");
  const [inputTimer, setInputTimer] = useState(null);

  const handleInputChange = async (e) => {
    setInputText(e.target.value);

    clearTimeout(inputTimer);
    let timeout = setTimeout(() => {
      console.log("FETCHING RESULTS");
      axios
        .get(
          `https://autocomplete-google-search.kuvambhardwaj.repl.co/autocomplete?q=${e.target.value}`
        )
        .then((res) => {
          setSearchResults(res.data);
        });
    }, 300);
    setInputTimer(timeout);
  };

  return (
    <div className="App">
      <center>
        <input
          value={inputText}
          onChange={handleInputChange}
          placeholder="Type something"
          style={{ fontSize: "24px" }}
        />

        <div style={{ marginTop: "30px" }}>
          <ul>
            {searchResults.map((searchResult) => (
              <li>{searchResult}</li>
            ))}
          </ul>
        </div>
      </center>
    </div>
  )
}


์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ํ•˜๋Š” ์ผ์€ ์ž…๋ ฅ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค 300ms์™€ ๊ฐ™์€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ƒํƒœ์— ์‹œ๊ฐ„ ์ดˆ๊ณผ ์ฐธ์กฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋ฌธ์ž๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ž…๋ ฅ ๊ฐ’์ด 300ms ์ด๋‚ด์— ๋ณ€๊ฒฝ๋˜๋ฉด ์ด์ „ ์„ค์ •์ด ์ง€์›Œ์ง‘๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ดˆ๊ณผํ•˜๊ณ  ์ƒˆ ๊ฒƒ์„ ์ดˆ๊ธฐํ™”ํ•˜์‹ญ์‹œ์˜ค. 300ms์˜ ์ž…๋ ฅ ๋น„ํ™œ์„ฑํ™” ํ›„ ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฐ€์žฅ ์ตœ๊ทผ์˜ ์ž…๋ ฅ ๊ฐ’์œผ๋กœ ์ž๋™ ์™„์„ฑ ์š”์ฒญ์„ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.






๋‚ด ๊ฒŒ์‹œ๋ฌผ์ด ๋งˆ์Œ์— ๋“ค๋ฉด ๋‹ค์Œ์„ ๊ณ ๋ คํ•˜์‹ญ์‹œ์˜ค :)

ํŠธ์œ„ํ„ฐ ->
Github -> @kuvamdazeus
๋งํฌ๋“œ์ธ ->
ํฌํŠธํด๋ฆฌ์˜ค -> kuvambhardwaj.vercel.app

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ