관리 메뉴

Dev Blog

1일차 셋업/기본 로직 구현 본문

Projects/JStargram

1일차 셋업/기본 로직 구현

Nomad Kim 2021. 4. 8. 01:25

목적: 파이어베이스를 사용해보고 싶었다. 백엔드에 들이는 시간을 아낄 수 있기에 프론트엔드 파트에 집중할 수 있을 것 같다.

컨셉: 유저들이 사진을 공유하고 사진에 대해 대화를 나눌 수 있는 반응형 채팅 웹. 파이어베이스의 소셜로그인과 실시간 데이터베이스 기능을 사용할 계획이다.


Initial Set-up

Set up react environment with necessary dependencies and new Firebase.

Connect App with database and Storage of Firebase.

 

리액트 개발환경을 세팅했다. 일단, 토이 프로젝트이기 때문에 많은 모듈은 필요하지 않다고 판단하여 styled component 정도만 적용했다.

 

파이어베이스에 데이터베이스와 스토리지(이미지저장) 을 생성하여 앱과 연결시켰다.

먼저, 파이어베이스 모듈을 설치하고 import 한 후, 생성 및 *초기화 한다.

*초기화: 객체를 선언하고 값을 할당하는 것.

import firebase from "firebase/app";
import "firebase/storage"; //store images
import "firebase/firestore"; //database
import "firebase/auth";
import dotenv from "dotenv";
dotenv.config();
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
var firebaseConfig = {
  apiKey: process.env.REACT_APP_APIKEY,
  authDomain: process.env.REACT_APP_AUTHDOMAIN,
  projectId: process.env.REACT_APP_PROJECTID,
  storageBucket: process.env.REACT_APP_STORAGEBUCKET,
  messagingSenderId: process.env.REACT_APP_MESSAGESENDER,
  appId: process.env.REACT_APP_APPID,
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// firebase.analytics();
const projectStorage = firebase.storage(); //This reference points to the root of Cloud Storage bucket.
const projectFirestore = firebase.firestore();
const timeStamp = firebase.firestore.FieldValue.serverTimestamp;
const projectAuth = firebase.auth();

export { projectStorage, projectFirestore, timeStamp, projectAuth };

Complete Basic Logics

- Upload form

- Firebase storage hook

- Make progress bar

- Firestore hook & showing image

- Create modal for selected image

 

1) 이미지 storage

이미지 파일을 storage 에 업로드하고, url 을 리턴받아 작성일자와 함께 파이어 데이터베이스에 올린다.

useEffect hooks 를 이용, file(이미지파일)에 변화가 생길때마다 즉, 새 이미지가 업로드될 때마다 데이터베이스에 추가시킨다.

ref() 는 해당 데이터베이스를 참조(바라보는 것)하는 메소드이다.

//useStorage.js

import { useState, useEffect } from "react";
import {
  projectStorage,
  projectFirestore,
  timeStamp,
} from "../firebase/config";

const useStorage = (file) => {
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState(null);
  const [url, setUrl] = useState(null);

  useEffect(() => {
    //references : if images is uploaded in default storage, file.name should be used as a name
    const storageRef = projectStorage.ref(file.name);
    //collection is a container of documents
    //document is a unit of storage. It includes key-value objects
    const collectionRef = projectFirestore.collection("images");

    storageRef.put(file).on(
      "state_changed",
      (snap) => {
        let percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
        setProgress(percentage);
      },
      (err) => {
        setError(err);
      },
      async () => {
        const url = await storageRef.getDownloadURL();
        const createdAt = timeStamp();
        collectionRef.add({ url, createdAt });
        setUrl(url);
      }
    );
  }, [file]);
  //useEffect callback will be fired if file dependency is changed
  return { progress, url, error };
};

export default useStorage;

ProgressBar.js 에서 framer-motion 라이브러리를 이용하여 애니메이션을 만든다.

//ProgressBar.js

import React, { useEffect } from "react";
import useStorage from "../hooks/useStorage";
import styled from "styled-components";
import { motion } from "framer-motion";

const ProgressBar = ({ file, setFile }) => {
  const { url, progress } = useStorage(file);
  //useStorage(file) returns { progress, url, error };
  // console.log(progress, url);

  useEffect(() => {
    if (url) {
      setFile(null);
    }
  }, [url, setFile]);

  return (
    <Progress
      percentage={progress}
      initial={{ width: 0 }}
      animate={{ width: progress + "%" }}
    >
      a
    </Progress>
  );
};

const Progress = styled(motion.div)`
  width: ${(props) => props.percentage + "%"};
  background: black;
  color: transparent;
`;

export default ProgressBar;

2) firestore

Firebase database(firestore) 해당 collection의 모든 사진들을 가져올 수 있다.

Collection 에 변화가 생길때마다(즉, 다큐먼트들) images 의 최신 데이터를 불러온다.

//useFirestore.js

import { useState, useEffect } from "react";
import { projectFirestore } from "../firebase/config";

const useFiretore = (collection) => {
  const [docs, setDocs] = useState([]);

  useEffect(() => {
    const unsub = projectFirestore
      .collection(collection) //collection will be 'images'
      .orderBy("createdAt", "desc")
      .onSnapshot((snap) => {
        //snapshot including all of documents in collection of
        //database when there is change in collection.
        let documents = [];
        snap.forEach((doc) => {
          documents.push({ ...doc.data(), id: doc.id });
          //doc.data(): all data of a document
        });
        setDocs(documents);
      });

    return () => unsub();
  }, [collection]);
  return { docs };
};

export default useFiretore;

ImageGrid.js 에서 데이터를 받아와 렌더시킨다.

import React from "react";
import useFiretore from "../hooks/useFirestore";
import styled from "styled-components";
import { motion } from "framer-motion";

const ImageGrid = ({ setSelectedImg }) => {
  const { docs } = useFiretore("images");
  // console.log(docs);
  return (
    <ImageBox>
      {docs &&
        docs.map((doc) => (
          <ImageWrap
            layout
            className="img-wrap"
            key={doc.id}
            onClick={() => setSelectedImg(doc)}
          >
            <Image
              src={doc.url}
              alt="uploaded pic"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              transition={{ delay: 1 }}
            />
          </ImageWrap>
        ))}
    </ImageBox>
  );
};

 

 

이미지

 

Comments