PROGRAMMING/๊ธฐํƒ€

[Firebase, Algolia] Cloud Extension ์œผ๋กœ Algolia ์—ฐ๊ฒฐํ•ด์„œ ๊ฒ€์ƒ‰ ๊ตฌํ˜„

\b\t 2022. 1. 25. 14:12

 

Firebase ๋Š” text ๊ฒ€์ƒ‰์ด exact search ๊ฒ€์ƒ‰๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

https://firebase.google.com/docs/firestore/solutions/search

 

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

์œ„ ๋งํฌ์—์„œ๋Š” ๋‹ค๋ฅธ ์—…์ฒด๋“ค๋„ ์†Œ๊ฐœํ•˜์˜€์œผ๋‚˜ Algolia ๊ฐ€ ์ฒ˜์Œ ์‚ฌ์šฉํ•˜๊ธฐ์— ํŽธํ•˜๋‹ค๊ณ  ํ•˜์—ฌ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

 

firebase ์—์„œ Algolia ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹ค์Œ์˜ ๊ณผ์ •์„ ๋”ฐ๋ผ๊ฐ€๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

https://www.algolia.com/developers/firebase-search-extension/

 

Firebase Extension ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„ํŽธํ•˜๊ฒŒ ๋ ๊ฑฐ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ, Firebase Extension ์ด beta ๊ธฐ๋„ ํ•˜๊ณ , ์ง์ ‘ Functions ๋กœ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ๊ทธ๋ ‡๊ฒŒ ์ง„ํ–‰ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

1) Firebase Functions ์ค€๋น„

Firebase Functions ๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— Firebase Functions ๋ฅผ ์ค€๋น„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ํฌ์ŠคํŒ…์œผ๋กœ ์ด ๋‚ด์šฉ์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

2022.01.20 - [PROGRAMMING/๊ธฐํƒ€] - [Firebase] Firebase Functions CLI ์‚ฌ์šฉํ•˜๊ธฐ, ํ•จ์ˆ˜ ๋ฐฐํฌ, ๋ฆฌ์ „ ๋ณ€๊ฒฝ

 

[Firebase] Firebase Functions CLI ์‚ฌ์šฉํ•˜๊ธฐ, ํ•จ์ˆ˜ ๋ฐฐํฌ, ๋ฆฌ์ „ ๋ณ€๊ฒฝ

Firebase ๋Š” Google ์˜ ๋ชจ๋ฐ”์ผ ๋ฐ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ํ”Œ๋ ›ํผ์ž…๋‹ˆ๋‹ค. ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ๋“ฑ์˜ ์žฅ์ ์ด ์žˆ์–ด ๋ฐฑ์—”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์—ฌ๊ฑด์ด ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋น„์šฉ์ ์ธ ์ธก๋ฉด์„ ๊ณ ๋ คํ•œ๋‹ค๋ฉด ์ถฉ๋ถ„ํžˆ ๋งค๋ ฅ์ ์ธ

iforint.tistory.com

๋˜ํ•œ, ์ด ์•„ํ‹ฐํด์—์„œ ์ „๋ฐ˜์ ์ธ ๊ณผ์ •์„ ์ดํ•ดํ•œ ํ›„ ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. (์˜ค๋ฅ˜๊ฐ€ ๋‚˜๋Š” ๋ถ€๋ถ„ ๋“ฑ)

์„ค๋ช…์ด ์ž˜ ๋˜์–ด ์žˆ์œผ๋‹ˆ ์ญ‰ ์ฝ์–ด๋ณด๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

https://rsfarias.medium.com/how-to-set-up-firestore-and-algolia-319fcf2c0d37

 

How to set up Firestore and Algolia using Cloud Functions.

An unofficial step-by-step guide on how to set up and perform full-text search using Firestore and Algolia.

rsfarias.medium.com

 

2) algoliasearch ์„ค์น˜

functions ํด๋”๋กœ ์ด๋™ํ•˜์—ฌ npm ์œผ๋กœ algoliasearch ๋ฅผ ์„ค์น˜ํ•ด์ค๋‹ˆ๋‹ค.

$ cd functions
$ npm install algoliasearch --save

 

3) Algolia Keys ๋“ฑ๋ก

Algolia ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Algolia ์— ๊ฐ€์ž… ํ›„ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์„œ API Keys ๋ฅผ ์–ป์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด API Keys ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

(์ฐธ๊ณ : dev ๋ฅผ ์œ„ํ•œ ๊ฒƒ๊ณผ ํ”„๋กœ๋•์…˜์„ ์œ„ํ•œ ๊ฒƒ์„ ๋ถ„๋ฆฌํ•ด์„œ ํ™˜๊ฒฝ์„ ์กฐ์„ฑํ•˜๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.)

 

 

Admin API Key ๋Š” ์ ˆ๋Œ€ ๋…ธ์ถœํ•˜๋ฉด ์•ˆ๋˜๋Š” ์ •๋ณด์ž…๋‹ˆ๋‹ค. (front-end ๋“ฑ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์—๋Š” ์ ˆ๋Œ€ ๋‘๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค)

Firebase Cloud Functions ๋Š” ์ด๋Ÿฐ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ (https://firebase.google.com/docs/functions/config-env) ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

 

๋”ฐ๋ผ์„œ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

$ firebase functions:config:set algolia.appid="YOUR_APP_ID" algolia.apikey="YOUR_API_KEY"
  • appid: Algolia ์•ฑ id
  • apikey: Admin API Key

 

๋‹ค์Œ๊ณผ ๊ฐ™์ด config ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋˜์—ˆ๋‹ค๊ณ  ๋‚˜์˜ค๋ฉด ์™„๋ฃŒ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

ํ™•์ธํ•˜๊ณ  ์‹ถ์œผ๋ฉด

$ firebase functions:config:get

 

์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

 

 

4) Algolia ์— index ๋งŒ๋“ค๊ธฐ

์ด์ œ firestore ์˜ ์–ด๋–ค collection ์„ Algolia ์— index ํ•  ๊ฒƒ์ธ์ง€๋ฅผ ์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๊ทธ ์ „์— Algolia ์™€ ์—ฐ๋™ํ•˜๋Š” ๊ณผ์ •์„ ์งง๊ฒŒ ์งš์–ด๋ณด๋ฉด,

  • 1) Algolia ์— ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
  • 2) Algolia ์— index ์ƒ์„ฑ
  • 3) ๋ฐ์ดํ„ฐ๋ฅผ Algolia ์— ์˜ฌ๋ฆฌ๊ธฐ (file / API / manually)
  • 4) ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์‹œ Algolia ์— ๋ฐ˜์˜๋˜๋„๋ก functions ์ž‘์„ฑ
  • 5) Search ์ž‘์„ฑ

 

์ด ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” Algolia ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ์ƒ์„ฑ ์‹œ์—๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

Algolia ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ index ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฆ„์€ ํŽธํ•œ๋Œ€๋กœ ์ง€์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

5) Firestore ๋‚ด์šฉ์„ Algolia ์— ์˜ฌ๋ฆฌ๊ธฐ

์ด ๊ณผ์ •์€ ๊ธฐ์กด์— ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์ง„ํ–‰ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ฆ‰, ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ Algolia ์— ์˜ฌ๋ ค์„œ ๊ฒ€์ƒ‰์ด ๋˜๋„๋ก indexing ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ ์˜ฌ๋ฆฌ๋Š” ๋ฐ์—๋Š” json file ๋กœ ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜ API ๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ํ•˜๋‚˜ํ•˜๋‚˜ ์ง์ ‘ ์˜ฌ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

 

API ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด script ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•ž์„œ ๋งํ–ˆ๋“ฏ, ์ด ๊ณผ์ •์€ ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฌ๋ฆฌ๋Š” ๊ณผ์ •์ด๋ฏ€๋กœ ๋‹จ ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค!

 

ํŒŒ์ผ์„ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด์„œ ์‹คํ–‰์‹œ์ผœ๋„ ๋˜์ง€๋งŒ, cloud functions ๋กœ ํ•ด๋‹น ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const functions = require("firebase-functions");
const admin = require("firebase-admin");

// Set up Algolia
const {default: algoliasearch} = require("algoliasearch");
const algoliaClient = algoliasearch(
    functions.config().algolia.appid_dev,
    functions.config().algolia.apikey_dev,
);
const indexName = "dev_title";
const collectionIndex = algoliaClient.initIndex(indexName);

// Create a HTTP request cloud functions
exports.sendCollectionToAlgolia = functions
    .region("asia-northeast2")
    .https.onRequest(async (request, response) => {
      const firestore = admin.firestore();
      const algoliaRecords = [];
      const snapshot = await firestore.collection("Record").listDocuments.get();
      snapshot.forEach((doc) => {
        const document = doc.data();
        const record = {
          objectID: doc.id,
          title: document.title,
        };
        algoliaRecords.push(record);
      });

      // After all records are created, save them to Algolia
      collectionIndex.saveObjects(algoliaRecords, (_error, content) => {
        response.status(200)
            .send("COLLECTION was indexed to Algolia successfully.");
      });
    });

 

์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ๋‚ด๋ถ€ ์ฝ”๋“œ๋Š” ๋ณ€ํ˜•ํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

 

6) Firestore ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋  ๋•Œ Algolia ์— ๋ฐ˜์˜๋˜๋„๋ก functions ์ž‘์„ฑ

Firebase ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œ๋  ๋•Œ, Algolia ๋„ ํ•ด๋‹น ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  "์ตœ์‹ "์œผ๋กœ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Firestore ์—์„œ ํŠน์ • ๊ฒฝ๋กœ์˜ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ/์ˆ˜์ •/์‚ญ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒฝ์šฐ์— ํŠธ๋ฆฌ๊ฑฐ๋˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๊ทธ ์ „์— ๋จผ์ € Algolia client ๋ž‘ index ๋ฅผ ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” 5) ์— ํฌํ•จ๋œ ๊ณผ์ •์ด์ง€๋งŒ, ํ˜น์‹œ ํ•ด๋‹น ๊ณผ์ •์ด ํ•„์š”์—†์–ด์„œ ๊ฑด๋„ˆ๋›ฐ์—ˆ๋‹ค๋ฉด ์ฒดํฌํ•˜๊ณ  ๋„˜์–ด๊ฐ€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// Set up Algolia
const {default: algoliasearch} = require("algoliasearch");
const algoliaClient = algoliasearch(
    functions.config().algolia.appid_dev,
    functions.config().algolia.apikey_dev,
);
const indexName = "dev_title";
const collectionIndex = algoliaClient.initIndex(indexName);

์ด์ฒ˜๋Ÿผ appId ์™€ APIKey ๋กœ client ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ๊ณ , index ๋ฅผ ์ง€์ •ํ•ด์ค๋‹ˆ๋‹ค.

 

 

6-1) onCreate

์ƒˆ๋กœ์šด document ๊ฐ€ ์ƒ๊ธธ ๊ฒฝ์šฐ, Algolia ์— ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค.

// define functions:collectionOnCreate
exports.collectionOnCreate = functions
    .region("asia-northeast2")
    .firestore.document("Record/{recordId}")
    .onCreate(async (snapshot, context) => {
      await saveDocumentInAlgolia(snapshot);
    });

const saveDocumentInAlgolia = async (snapshot) => {
  if (snapshot.exists) {
    const data = snapshot.data();
    console.log(snapshot.id);
    if (data) {
      const record = {
        objectID: snapshot.id,
        title: data.title,
      };
      collectionIndex.saveObject(record)
          .catch((res) => console.log("Error with: ", res));
    }
  }
};
  • Record Collections ์ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๋ฉด, ๊ทธ id ๋Š” ์œ„์™€ ๊ฐ™์ด {rocordId} ๋กœ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • indexing ํ•  ๋‚ด์šฉ๋งŒ record ์— ๋‹ด์•„ collectionIndex ์— saveObject ๋กœ ์ €์žฅํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

์ด๋•Œ, Algolia ์—์„œ ์ทจ๊ธ‰ํ•˜๋Š” unique ํ•œ id ์ธ "objectID" ๋ฅผ ๊ผญ ์š”์†Œ๋กœ ๋„ฃ์–ด์ฃผ๊ฑฐ๋‚˜ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

index.saveObject(object object, {
  autoGenerateObjectIDIfNotExist: true
})

 

 

6-2) onUpdate

Algolia ์— ์›๋ž˜ ์žˆ๋Š” ์š”์†Œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” 2๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • saveObject(record): record ์— ์žˆ๋Š” ์ •๋ณด๋กœ ๊ธฐ์กด ์ •๋ณด๋ฅผ ๋ฎ์–ด์”๋‹ˆ๋‹ค.
  • partialUpdateObjects(record): record ์— ๋„ฃ์–ด์ค€ ์ •๋ณด๋งŒ ์ƒˆ๋กœ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

 

ํŠน์ • ์š”์†Œ๋งŒ ์—…๋ฐ์ดํŠธํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด partialUpdateObjects ๋ฅผ, ๋ชจ๋“  ์ •๋ณด๋ฅผ ์žฌ๊ฐฑ์‹ ํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด saveObject ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์„œ๋Š” ๊ธฐ์กด์˜ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ•„์š”ํ•œ ์ •๋ณด๋งŒ ์—…๋ฐ์ดํŠธํ•  ๊ฒƒ์ด๋ฏ€๋กœ, partialUpdateObjects ๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

// define functions:ticcleOnUpdate
exports.ticcleOnUpdate = functions
    .region("asia-northeast2")
    .firestore.document("Record/{recordId}")
    .onUpdate(async (change, context) => {
      await updateDocumentInAlgolia(context.params.recordId, change);
    });


const updateDocumentInAlgolia = async (objectID, change) => {
  const before = change.before.data();
  const after = change.after.data();
  if (before && after) {
    const record = {objectID: objectID};
    let flag = false;
    if (before.title != after.title) {
      record.title = after.title;
      flag = true;
    }
    if (before.content != after.content) {
      record.content = after.content;
      flag = true;
    }

    if (flag) {
      // update
      collectionIndex.partialUpdateObject(record)
          .catch((res) => console.log("Error with: ", res));
    }
  }
};
  • .firestore.document("Record/{recordId}"): ์—ฌ๊ธฐ ๊ฒฝ๋กœ์— ์žˆ๋Š” {recordId} ๋Š”
  • .onUpdate(async (change, context): context ์— ํฌํ•จ๋˜์–ด ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ context.params.recordId ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • title ์™€ content ๋ฅผ indexing ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ before data ์™€ after data ๋ฅผ ๋น„๊ตํ•ด์„œ ํ•„์š”ํ•œ ๋‚ด์šฉ๋งŒ partialUpdateObjects ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•ด์ค๋‹ˆ๋‹ค.

 

6-3) onDelete

์‚ญ์ œ๋Š” ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹นํ•˜๋Š” objectID ๋ฅผ ์‚ญ์ œํ•ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

// define functions:ticcleOnDelete
exports.ticcleOnDelete = functions
    .region("asia-northeast2")
    .firestore.document("Record/{recordId}")
    .onDelete(async (snapshot, context) => {
      await deleteDocumentInAlgolia(snapshot);
    });
    
const deleteDocumentInAlgolia = async (snapshot) => {
  if (snapshot.exists) {
    const objectID = snapshot.id;
    collectionIndex.deleteObject(objectID)
        .catch((res) => console.log("Error with: ", res));
  }
};

 

 

์—ฌ๊ธฐ๊นŒ์ง€ ํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด๋ฉด ์ •์˜ํ•œ ํ•จ์ˆ˜๋“ค์ด ์ž˜ ๋™์ž‘ํ•˜๊ณ , Algolia ์—๋„ ์ž˜ ๋ฐ˜์˜๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

๋‹ค์Œ์—๋Š” React Native ์— Algolia ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ๊ฒ€์ƒ‰์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.