Firebase 적용해보기

sham·2024년 10월 28일

SkyScope 개발일지

목록 보기
10/12

다음 토이프로젝트의 개발 기록이다.

이전에 작성한 firebase 세팅글을 참조해서 진행하였다.

Firebase 세팅

설치

https://console.firebase.google.com

규칙 변경

프로젝트 세팅

루트 상의 경로에 firebase.ts 파일을 생성했다.

firebaseConfig 안에 환경변수를 세팅한다.

firebase 홈페이지의 프로젝트 설정에서 확인할 수 있다.

firebase.ts

// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore/lite';

// 나중에 환경 변수 처리
const firebaseConfig = {
  apiKey: import.meta.env.VITE_DB_API_KEY,
  authDomain: 'skyscope-73aa7.firebaseapp.com',
  projectId: 'skyscope-73aa7',
  storageBucket: 'skyscope-73aa7.appspot.com',
  messagingSenderId: import.meta.env.VITE_DB_SENDER_ID,
  appId: import.meta.env.VITE_DB_APP_ID,
  measurementId: import.meta.env.VITE_DB_MEASUREMENT_ID,
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

export default db;

Firebase Store/lite 사용

firebase.ts에서 생성한 db를 이용해 firebase store를 사용할 수 있다. firestore 보다 조금 더 가벼운 lite를 사용할 것인데, 메서드 사용에서 자잘한 차이가 있을 뿐 본질은 비슷하다.

  • doc 메서드로 특정 문서에 대한 참조를 만든다.
  • getDoc, setDoc, updateDoc 메서드를 사용해서 해당 참조에 접근에 데이터를 가져오고, 수정하는 작업을 할 수 있다.
  • setDoc 등을 사용하면 존재하지 않는 document라도 자동으로 생성해준다.
  • doc 사용 시 인자로 들어가는 값은 무조건 string이여야 한다.
  • JSON.stringify, JSON.parse를 이용해 객체, 배열 등의 데이터도 손쉽게 관리할 수 있다.
import { useState, useEffect, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';

import { RootState } from '@src/Store/store';
import { addToast } from '@src/Store/toastWeatherSlice';
import { errorAccured } from '@src/Store/requestStatusSlice';
import { LocateDataType, KakaoSearchType, KakaoMapMarkerType, markerStatus } from '@src/Types/liveDataType';
import { transLocaleToCoord } from '@src/Util';

// import { doc, collection, getDoc, setDoc, updateDoc, deleteDoc } from 'firebase/firestore/lite';
import { doc, getDoc, setDoc, updateDoc } from '@firebase/firestore/lite';

import db from '@src/firebase';

interface Props {
  map: kakao.maps.Map | null;
}

const useMapInfo = ({ map }: Props) => {
  const [searchPlaces, setSearchPlaces] = useState<LocateDataType[]>([]);
  const [currentPlaces, setCurrentPlaces] = useState<KakaoSearchType[]>([]);
  const [bookmarkPlaces, setBookmarkPlaces] = useState<KakaoSearchType[]>([]);
  const [mapMarkers, setMapMarkers] = useState<KakaoMapMarkerType[]>([]);
  const [isBlinkPlaces, setIsBlinkPlaces] = useState<boolean[]>([true, true]); // bookmark : 0, current : 1

  const [viewportWidth, setViewportWidth] = useState(window.innerWidth);

  const { isLogin, id } = useSelector((state: RootState) => state.globalDataSliceReducer);
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const postFirestoreData = async (newBookmarkPlaces: KakaoSearchType[]) => {
    const data = JSON.stringify(newBookmarkPlaces);
    const docRef = doc(db, 'mapData', id + '');
    await setDoc(
      docRef,
      {
        bookmarks: data,
      },
      { merge: true },
    );
  };

  const getFirestoreData = async () => {
    // firebase
    const docRef = doc(db, 'mapData', id + '');
    const docSnap = await getDoc(docRef);
    if (docSnap.exists()) {
      const result = docSnap.data();
      const parsedBookmarks: KakaoSearchType[] = JSON.parse(result.bookmarks);
      setBookmarkPlaces(parsedBookmarks);
      const parsedOnMapMarkers: KakaoMapMarkerType[] = parsedBookmarks.map((bookmark: KakaoSearchType) => {
        const image = getNewImage('bookmark');
        const { position, placeName, placeId } = bookmark;
        const status = 'bookmark';
        return { placeName, placeId, position, status, image };
      });
      changeOnMapMarkers(parsedOnMapMarkers);

      // parsedOnMapMarkers의 length가 있을 때만 bound 설정
      if (map && parsedOnMapMarkers.length) {
        const bounds = new kakao.maps.LatLngBounds();
        parsedOnMapMarkers.forEach((marker: KakaoMapMarkerType) => {
          const position = new kakao.maps.LatLng(marker.position.lat, marker.position.lng);
          bounds.extend(position);
        });
        map.setBounds(bounds);
      }
    }
  };

  useEffect(() => {
    getFirestoreData();

    const localBookmarks = localStorage.getItem('bookmarks');
    if (localBookmarks) {
      const parsedBookmarks: KakaoSearchType[] = JSON.parse(localBookmarks);
      setBookmarkPlaces(parsedBookmarks);

      const parsedOnMapMarkers: KakaoMapMarkerType[] = parsedBookmarks.map((bookmark: KakaoSearchType) => {
        const image = getNewImage('bookmark');
        const { position, placeName, placeId } = bookmark;
        const status = 'bookmark';
        return { placeName, placeId, position, status, image };
      });
      changeOnMapMarkers(parsedOnMapMarkers);

      // parsedOnMapMarkers의 length가 있을 때만 bound 설정
      if (map && parsedOnMapMarkers.length) {
        const bounds = new kakao.maps.LatLngBounds();
        parsedOnMapMarkers.forEach((marker: KakaoMapMarkerType) => {
          const position = new kakao.maps.LatLng(marker.position.lat, marker.position.lng);
          bounds.extend(position);
        });
        map.setBounds(bounds);
      }
    }
  }, [map]);

  return {
    searchPlaces,
    currentPlaces,
    bookmarkPlaces,
    mapMarkers,
    isBlinkPlaces,
    onClickMarker,
    onSearchPlace,
    onFocusPlace,
    onTogglePlace,
    onDeletePlace,
    onClickFooterPlace,
    setIsBlinkPlaces,
  };
};

export default useMapInfo;
profile
씨앗 개발자

0개의 댓글