Eun_Frontend
  • [Frotend] Firebase 공부
    2025년 03월 12일 17시 04분 51초에 업로드 된 글입니다.
    작성자: 동혁이

     

    Firebase 공부

     

     

    Firebase란?

    - Firebase: 실시간 데이터베이스, 인증, 스토리지, 분석 등 다양한 기능을 제공하는 모바일 및 웹앱 개발 플랫폼

    - 2011년 스타트업에서 시작 (실시간 채팅 기능) >> 2014년 구글이 인수 (백엔드 서버리스)

    - 백엔드 서버 구축하고 관리 X. Google Cloud Platform을 이용해 빠르고 효율적인 개발

    - 특히 실시간 기능 서비스, 클라이언트 사이드 집중, 초기 프로토타입 개발에 유용

     

    Firebase 주요 기능

    1. Authentication: 간편한 다중 플랫폼 로그인

    - 이메일 인증, 소셜 미디어 계정 인증, 전화번호 인증방식 제공

    - 보안 강화, 사용자 인증 과정 안정하게 처리

     

    2. Firestore: NoSQL 데이터베이스

    - Firebase에서 제공하는 NoSQL 형식의 클라우드 데이터베이스

    - 실시간 데이터 동기화 지원 (ex. 실시간 채팅)

     

    3. Storage: 사진 및 동영상 저장

    - 사용자 파일 저장 및 공유, 강력한 보안

     

    4. Hosting: 웹앱 호스팅

    - 정적 및 동적 콘텐츠 모두 호스팅 가능, 웹 앱 간단 배포

     

    Firebase 장•단점

    1. 장점

    - 백엔드 서버 없이 개발: 개발 시간 절약, 클라이언트에 집중해서 개발 가능

    - 실시간 데이터베이스: 사용자 데이터 실시간 공유, 실시간 채팅 등의 기능 쉽게 개발

    - Google 플랫폼 통합: Google Cloud Platform 서비스 쉽게 이용 및 분석 가능

     

    2. 단점

    - 쿼리 제한: NoSQL 데이터 베이스의 간단한 쿼리만 사용 가능

    - 비용: 확장성이 중요한 경우 비용이 빠르게 증가할 수 있음

    - 마이그레이션 어려움: 타 백엔드 서비스로 전환하는 경우 추가적인 작업 필요

     

    Firebase 설정 방법

    1. Firebase 공식 사이트 이동

    https://firebase.google.com/?hl=ko

     

    Firebase | Google's Mobile and Web App Development Platform

    개발자가 사용자가 좋아할 만한 앱과 게임을 빌드하도록 지원하는 Google의 모바일 및 웹 앱 개발 플랫폼인 Firebase에 대해 알아보세요.

    firebase.google.com

     

    2. 프로젝트 생성

     

    3. Firebase 앱 설정

    - 생선된 Firebase 프로젝트 페이지에서, "웹"을 선택하고 앱의 별칭을 입력

    - Firebase SDK를 추가하는 과정에서, FIrebase가 제공하는 구성 객체를 복사

     

    3. React 프로젝트에 Firebase 설치

    yarn add firebase

     

    4. React 앱에서 Firebase 초기화

    - SDK 복사 붙여넣기

    - API key는 env로 이동

    // ex) firebaseApp.ts
    // firebaseApp.ts
    import { initializeApp } from "firebase/app";
    
    const {
      REACT_APP_API_KEY,
      REACT_APP_AUTH_DOMAIN,
      REACT_APP_PROJECT_ID,
      REACT_APP_STORAGE_BUCKET,
      REACT_APP_MESSAGING_SENDER_ID,
      REACT_APP_APP_ID,
    } = process.env;
    
    const firebaseConfig = {
      apiKey: REACT_APP_API_KEY,
      authDomain: REACT_APP_AUTH_DOMAIN,
      projectId: REACT_APP_PROJECT_ID,
      storageBucket: REACT_APP_STORAGE_BUCKET,
      messagingSenderId: REACT_APP_MESSAGING_SENDER_ID,
      appId: REACT_APP_APP_ID,
    };
    
    const firebase = initializeApp(firebaseConfig);
    
    export default firebase;

     

    5. React 앱에서 Firebase 사용

    import 'firebase/auth';
    import 'firebase/firestore';
    
    const auth = firebase.auth();
    const db = firebase.firestore();

     

    Firebase 초기 세팅 - 끝 -

     

     

     

    Firebase Auth란?

    - Firebase Authenticaiton: 쉽게 사용자 인증을 구현할 수 있도록 돕는 Firebase 서비스

     

    기능

    - 이메일과 비밀번호 인증, 소셜 미디어 인증, 전화번호 인증

    - 인증 정보 안전하게 저장

    - 인정 정보 변경시 실시간으로 앱에 업데이트

    - 인증 이메일 전송, 비밀번호 재설정 이메일 전송 등의 기능

     

    장점

    1. 편의성

    - 복잡한 인증 과정을 Firebase가 대신 처리

    - 클라이언트 사이드 개발에만 집중

     

    2. 소셜 미디어 계정 인증

    - OAuth 2.0과 OpenID Connect 지원

    - 각종 소셜 미디어 계정 이용한 인증 쉽게 구현

     

    3. 보안

    - 사용자의 비밀번호 안전하게 암호화

    - HTTPS 이용해 데이터 전송 보안

     

    Firebase Auth는 언제 사용하는게 좋을까?

    - 로그인 시스템

    - 사용자 프로필 시스템

     

    Firebase Auth 세팅 방법

    1. Firebase 프로젝트 생성 & Firebase SDK 앱 추가 (앞서 설명)

     

    2. Firebase 초기화 (앞서 설명)

     

    3. Authentication 서비스 불러오기

    - Authentication 탭 클릭 -> 시작하기

     

    로그인 방법 탭에서

     

    기본적인 이메일/비밀번호 클릭 -> 저장

     

     

    회원가입 성공한 사용자들의 정보는 사용자탭에 저장된다.

     

     

    템플릿 탭에서는

    이메일을 보낸다고하면 이메일 템플릿을 변경할 수도 있고, 비밀번호 재설정, 이메일 주소 변경등 여러가지 설정 가능함

     

     

    ❗️ 개발자 문서

    https://firebase.google.com/docs/auth/web/start?hl=ko

     

    웹사이트에서 Firebase 인증 시작하기  |  Firebase Authentication

    4월 9~11일, Cloud Next에서 Firebase가 돌아옵니다. 지금 등록하기 의견 보내기 웹사이트에서 Firebase 인증 시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

    firebase.google.com

     

    실제로 세팅 해보자

    - 기존 코드

    // firebaseApp.ts
    import { initializeApp } from "firebase/app";
    
    const {
      REACT_APP_API_KEY,
      REACT_APP_AUTH_DOMAIN,
      REACT_APP_PROJECT_ID,
      REACT_APP_STORAGE_BUCKET,
      REACT_APP_MESSAGING_SENDER_ID,
      REACT_APP_APP_ID,
    } = process.env;
    
    const firebaseConfig = {
      apiKey: REACT_APP_API_KEY,
      authDomain: REACT_APP_AUTH_DOMAIN,
      projectId: REACT_APP_PROJECT_ID,
      storageBucket: REACT_APP_STORAGE_BUCKET,
      messagingSenderId: REACT_APP_MESSAGING_SENDER_ID,
      appId: REACT_APP_APP_ID,
    };
    
    const firebase = initializeApp(firebaseConfig);
    
    export default firebase;

     

     

    import할때마다 초기화해주는 방식은 비효율적이기 때문에

    - app을 지역변수로 선언해주고 try catch문으로  할당해주는 방식으로 진행

     

    import { FirebaseApp, getApp, initializeApp } from "firebase/app";
    import "firebase/auth";
    
    let app: FirebaseApp;
    
    / ... /
    
    try {
      app = getApp("app");
    } catch (error) {
      app = initializeApp(firebaseConfig, "app");
    }
    
    export default app;

     

    getAuth

    - firebase 사용자가 현재 로그인되었는지 확인

     

    getAuth를 콘솔에 찍어보면 currentUser가 있는데 null이면 로그인 안한거임

     

    onAuthStateChanged

    - Firebase Authentication 서비스에서 제공하는 메서드

    - 인증 상태가 변경될 때마다 호출되는 리스너 설정 (로그인, 로그아웃) => 사용자가 로그인 or 로그아웃 할 때 마다 실시간으로 호출 됨 (로그인 상태일 때는 사용자 정보를, 아니라면 null 리턴함)

     

    // 코드 예시 - 로그인 상태에 따른 조건처리
    export default function App() {
      const auth = getAuth(app);
      const [isAuthenticated, setIsAuthenticated] = useState<boolean>(
        !!auth?.currentUser
      );
      const [init, setInit] = useState<boolean>(false);
    
      useEffect(() => {
        onAuthStateChanged(auth, (user) => {
          user ? setIsAuthenticated(true) : setIsAuthenticated(false);
    
          setInit(true);
        });
      }, [auth]);
    
      return (
        {init ? <Router isAuthenticated={isAuthenticated} /> : <Loader />}
      );
    }

     

     

    createUserWithEmailAndPassword

    신규 사용자를 생성할 때는 createUserWithEmailAndPassword 메서드 호출

     

    // 예시 코드
    const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        try {
          const auth = getAuth(app);
          await createUserWithEmailAndPassword(auth, email, password);
    
          toast.success("회원가입에 성공했습니다!");
          navigate("/");
        } catch (error: any) {
          console.log(error);
          toast.error(error?.code);
        }
      };

     

    signInWithEmailAndPassword

    반대로 로그인할때는 signInWithEmailAndPassword 메서드 호출

    // 예시 코드
    const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        try {
          const auth = getAuth(app);
          await signInWithEmailAndPassword(auth, email, password);
    
          toast.success("로그인에 성공했습니다.");
          navigate("/");
        } catch (error: any) {
          toast.error(error?.code);
          console.log(error);
        }
      };

     

    signOut

    - signOut 메서드 가지고 오면 간단하게 작성가능

    // 예시 코드
    const onSignOut = async () => {
      try {
        const auth = getAuth(app);
        await signOut(auth);
        toast.success("로그아웃 되었습니다.");
      } catch (error: any) {
        console.log(error);
        toast.error(error?.code);
      }
    };
    
    <div onClick={onSignOut}>
      로그아웃
    </div>

     

     

     

     

    Firestore란?

    Firestore: Firebase에서 제공하는 NoSQL 형식의 클라우드 데이터베이스

    - 애플리케이션 개발을 하다보면 데이터를 저장하고 불러오는 일이 매우 중요한데, Cloud Firestore는 이러한 일을 쉽게 도와줌

    - Firestore는 실시간 데이터 동기화를 지원하며, 웹, 안드로이드, iOS에서 데이터를 저장하고 동기화할 수 있음

    - 데이터는 문서(document)와 컬렉션(collection)의 형태로 저장되고, 효율적인 쿼리 작성하게 함

    - 오프라인 지원 제공

     

    Firestore 장점

    1. 실시간 데이터 동기화

    - 실시간 채팅 및 데이터 분석 등 실시간 기능 애플리케이션 개발

     

    2. 구조화된 데이터

    - 문서 / 컬렉션 형태로 데이터 저장 / 구조화된 데이터 쉽게 저장하고 불러올 수 있음

     

    3. 보안

    - 사용자 기반의 보안규칙 설정 가능

     

    Firestore 사용 예시

    1. 실시간 채팅 앱

    2. 다양한 데이터 저장 및 불러올 수 있음 - ex) 게임 점수, 사용자 설정, 텍스트, 이미지

    3. 사용자별 데이터 접근 권한 쿼리 - ex) 사용자 인증 정보와 함께 사용됨

     

    Firestore 사용 방법

    1. Firebase 프로젝트 생성 & Firebase SDK 앱 추가 (앞서 설명)

     

    2. Firebase 인스턴스 가져오기

     

    3. Firestore 세팅하기

    - Cloud Firestore -> 데이터베이스 만들기 클릭

     

    - 위치는 서울

    - 프로덕션 누르면 데이터가 비공개라 사용할 수 없기 때문에 나중에 배포할때 변경

    - 만들기

     

    4. firebaseApp.ts 세팅

    - getFirestore

    import { FirebaseApp, getApp, initializeApp } from "firebase/app";
    import "firebase/auth";
    import { getFirestore } from "firebase/firestore";
    
    const {
      REACT_APP_API_KEY,
      REACT_APP_AUTH_DOMAIN,
      REACT_APP_PROJECT_ID,
      REACT_APP_STORAGE_BUCKET,
      REACT_APP_MESSAGING_SENDER_ID,
      REACT_APP_APP_ID,
    } = process.env;
    
    const firebaseConfig = {
      apiKey: REACT_APP_API_KEY,
      authDomain: REACT_APP_AUTH_DOMAIN,
      projectId: REACT_APP_PROJECT_ID,
      storageBucket: REACT_APP_STORAGE_BUCKET,
      messagingSenderId: REACT_APP_MESSAGING_SENDER_ID,
      appId: REACT_APP_APP_ID,
    };
    
    let app: FirebaseApp;
    
    try {
      app = getApp("app");
    } catch (error) {
      app = initializeApp(firebaseConfig, "app");
    }
    
    export const db = getFirestore(app);
    
    export default app;

     

     

    setDoc()

    - 단일 문서 생성 or 덮어쓰기

    // 코드 예시
    import { doc, setDoc } from "firebase/firestore"; 
    
    // Add a new document in collection "cities"
    await setDoc(doc(db, "cities", "LA"), {
      name: "Los Angeles",
      state: "CA",
      country: "USA"
    });

     

     

    addDoc()

    - 문서마다 id 값을 따로 넣지 않으려면 (자동 id 생성)

    공식문서 내용
    문서에 유의미한 ID가 없는 경우 Cloud Firestore에서 ID를 자동으로 생성할 수 있습니다. 
    다음과 같은 언어별 add() 메서드를 호출할 수 있습니다.
    // 코드 예시
    import { collection, addDoc } from "firebase/firestore"; 
    
    await addDoc(collection(db, "posts"), {
              title,
              summary,
              content,
              createdAt: new Date()?.toLocaleDateString("ko", {
                hour: "2-digit",
                minute: "2-digit",
                second: "2-digit",
              }),
              email: user?.email,
              uid: user?.uid,
              category,
            });
    
            toast?.success("게시글을 생성했습니다.");
            navigate("/");

     

    getDocs()

    - 컬렉션의 모든 문서 가져오기

    ex) 게시글 리스트

    // 코드 예시
    const getPosts = async () => {
      const datas = await getDocs(collection(db, "posts"));
      
      datas.forEach((doc) => {
        const dataObj = { ...doc.data(), id: doc.id };
        setPosts((prev) => [...prev, dataObj])
      })
    }

     

     

    getDoc()

    - 문서 가져오기

    ex) 단일 데이터 가져오기

    // 코드 예시
    const getPost = async (id: string) => {
        if (id) {
          const docRef = doc(db, "posts", id);
          const docSnap = await getDoc(docRef);
          setPost({ id: docSnap.id, ...(docSnap.data() as PostProps) });
        }
      };

     

    // 공식 문서 코드 예시
    import { doc, getDoc } from "firebase/firestore";
    
    const docRef = doc(db, "cities", "SF");
    const docSnap = await getDoc(docRef);
    
    if (docSnap.exists()) {
      console.log("Document data:", docSnap.data());
    } else {
      // docSnap.data() will be undefined in this case
      console.log("No such document!");
    }

     

     

    updateDoc()

    - 문서 업데이트

    ex) 게시글 수정

    // 코드 예시
    const postRef = doc(db, "posts", post.id);
    await updateDoc(postRef, {
      title,
      summary,
      content,
      updatedAt: new Date()?.toLocaleDateString("ko", {
        hour: "2-digit",
        minute: "2-digit",
        second: "2-digit",
      }), // firebase는 no sql 이므로 동적으로 데이터 추가해도 상관없음
      category,
    });
    
    toast?.success("게시글을 수정했습니다.");
    navigate(`/posts/${post.id}`);

     

     

    deleteDoc()

    - 문서 삭제

    ex) 게시글 삭제

    // 코드 예시
    const handleDelete = async () => {
        const confirm = window.confirm("해당 게시글을 삭제하시겠습니까?");
        
        if (confirm && post && post.id) {
          await deleteDoc(doc(db, "posts", post.id));
          toast.success("게시글을 삭제하였습니다.");
          navigate("/");
        }
      };

     

     

     

    ❗️ Firestore에서 쿼리를 사용하려면 색인을 꼭 만들어줘야함

     

    orderBy()

    - 쿼리 정렬

    // 코드 예시 (query, where, orderBy 사용해서 탭 별로 필터링해 불러오기)
    const getPosts = async () => {
        setPosts([]);
    
        let postsRef = collection(db, "posts");
        let postsQuery = query(postsRef, orderBy("orderBy","asc"))
    
        const datas = await getDocs(postsQuery);
    
        datas?.forEach((doc) => {
          const dataObj = { ...doc.data(), id: doc.id };
          setPosts((prev) => [...prev, dataObj as PostProps]);
        });
    };
    
    
    const getPosts = async () => {
        setPosts([]);
    
        let postsRef = collection(db, "posts");
        let postsQuery;
    
        if (activeTab === "my" && user) {
          // 나의 글만 필터링
          postsQuery = query(
            postsRef,
            where("uid", "==", user.uid),
            orderBy("createdAt", "asc")
          );
        } else if (activeTab === "all") {
          // 모든 글 보여주기
          postsQuery = query(postsRef, orderBy("createdAt", "asc"));
        } else {
          // 카테고리 글 보여주기
          postsQuery = query(
            postsRef,
            where("category", "==", activeTab),
            orderBy("createdAt", "asc")
          );
        }
    
        const datas = await getDocs(postsQuery);
    
        datas?.forEach((doc) => {
          const dataObj = { ...doc.data(), id: doc.id };
          setPosts((prev) => [...prev, dataObj as PostProps]);
        });
      };

     

     

    arrayUnion()

    - 배열 요소 업데이트

     

    // 코드 예시 (posts 컬렉션 내부 필드에 배열 형태로 넣어주기 위함)
    const commmentObj = {
        content: comment,
        uid: user.uid,
        email: user.email,
        createdAt: new Date().toLocaleDateString("ko", {
          hour: "2-digit",
          minute: "2-digit",
          second: "2-digit",
        }),
      };
    
      await updateDoc(postRef, {
        comments: arrayUnion(commmentObj),
        updatedAt: new Date().toLocaleDateString("ko", {
          hour: "2-digit",
          minute: "2-digit",
          second: "2-digit",
        }),
    });

     

     

    arrayRemove()

    - 배열 요소 삭제

    // 코드 예시 (댓글 삭제 기능 구현)
    const handleDeleteComment = async (data: CommentsInterface) => {
        const confirm = window.confirm("해당 댓글을 삭제하시겠습니까?");
    
        if (confirm && post.id) {
          const postRef = doc(db, "posts", post.id);
    
          await updateDoc(postRef, {
            comments: arrayRemove(data), // 지우려고 하는 필드에 arrayRemove() 사용하면 됨
          });
    
          toast.success("댓글을 삭제했습니다.");
          // 문서 업데이트
          await getPost(post.id);
        }
      };

     

     

     

    Firebase 보안 작업

     

    1. Firebase Authorization 승인된 도메인 설정

    - 프로젝트를 연결하고자 한다면 승인된 도메인에 추가해줘야함

     

    2. Firestore의 기본 보안규칙(rules) 변경

    - 아래 문서 보면서 수정

    https://firebase.google.com/docs/rules/get-started?hl=ko&authuser=0

     

    Firebase 보안 규칙 시작하기  |  Firebase Security Rules

    4월 9~11일, Cloud Next에서 Firebase가 돌아옵니다. 지금 등록하기 의견 보내기 Firebase 보안 규칙 시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Firebase

    firebase.google.com

     

    // 예시 - 로그인 사용자만 데이터 접근 허용
    rules_version = '2';
    
    service cloud.firestore {
      match /databases/{database}/documents {
      
        match /{document=**} {
          allow read, write: if request.auth != null
        }
      }
    }

     

     

    3. API key 보안 작업

    - 클라우드 구글 콘솔 이동

    https://console.cloud.google.com/apis/credentials

     

    Google 클라우드 플랫폼

    로그인 Google 클라우드 플랫폼으로 이동

    accounts.google.com

    1) 블로그 프로젝트 생성, 선택

    2) 사용자 인증 정보 탭 -> 주의가 나와있는 Browser key 클릭

    - 애플리케이션 제한사항 설정 (웹 사이트 클릭,  승인된 URL 추가 - localhost주소, firebase 도메인(authentication 승인된 도메인 부분에 나와있음),배포한 도메인 주소)

    - 저장 (이제 해당 도메인에서만 API 키를 제한할 수 있음)

     

     

     

    Firebase CLI란?

    Firebase CLI (Command Line Interface)

    - Firebase 프로젝트를 관리하고 다양한 Firebase 기능을 로컬에서 사용할 수 있게 해줌

    - Firebase 프로젝트 설정, 데이터베이스, Cloud Function, 호스팅 등 관리

    https://firebase.google.com/docs/cli?hl=ko

     

    Firebase CLI 참조  |  Firebase 문서

     

    firebase.google.com

     

    Firebase CLI 설치 방법

    1. node.js, npm은 이미 설치했으니까 pass

    2. npm install -g firebase-tools

    3. firebase login -> Y -> 로그인 화면 나오면 로그인

     

    4. firebase projects:list - 로그인된 계정의 firebase 프로젝트를 모두 리스트업 할 수 있음

     

    이제 이 CLI를 통해 배포를 하거나 호스팅과 같은 여러 설정들을 작업할 수 있음

     

     

    배포하기

    1. 프로젝트 초기화

    - firebase init hosting

     

     

    - Command line에 아래와 같이 입력

    1) Use an existing project

    2) 프로젝트 이름

    3) What do you want to use as your public directory? -> build 입력

    4) Configure as a  single-page app (rewrite all urls to /index.html) -> yes

    5) Set up automatic builds and deploys with GitHub -> No

     

    2. 사이트 배포

    - yarn build (빌드 후 배포)

    - firebase deploy --only hosting

    - 배포된 url 확인 (ex) https://XXX-XXX-XXX.web.app)

     

    3. Google console 및 Firebase 보안 적용

    - Firebase Auth > setting > 승인된 도메인 추가

    - Google Console > API > 보안 URL 추가

     

    댓글