Nextjs 파이어베이스에 이미지 업로드 & 다운로드

JangGwon·2023년 4월 9일
3

1. Firebase Storage 연결하기

1-1 Storage를 사용하는 이유.

일단 Firestore는 파어이베이스에서 제공되는 무료 클라우드 데이터베이스로 데이터를 딕셔너리(key, value)형태로만 저장하고 동기화할 수 있다. 그렇기에 이미지나 파일을 저장하기 위해서는 Firestore 가 아닌 사용자의 사진 및 동영상 저장 기능을 제공하는 Firebase Storage를 사용해야한다.

1-2 연결하기

파이어베이스 콘솔페이지에서 firestore / storage 모두 등록해야한다.
이미지를 저장할 때 사용되는건 Storage인데 왜 FireStore까지 연결하냐면, Storage에 저장할 이미지의 다운로드 경로 또는 저장될 파일 이름을 FireStore에 저장하여 관리하기 위함.

그 다음 Next.js Firebase(Firestore)연동하기 포스팅때 처럼 Firebase / Firestore 모두 연결 해주고, 추가로 storage.js를 만들고 아래와 같이 작성해주면 stroge도 연결 끝

firebase/storage.js

import firebasedb from "./firebasedb";
import { getStorage } from "firebase/storage";

const storage = getStorage(firebasedb.firebaseApp);
export default storage;



2. Storage에 이미지 업로드하기

2-1 uploadBytes 메서드

Storage에 이미지나 파일을 업로드 할 때, uploadBytes 메서드를 이용함.

import { ref, uploadBytes, } from "firebase/storage";

    const imageRef = ref(스토리지, `스토리지에 저장할 파일 경로 및 이름`);  
    uploadBytes(imageRef, 저장할 파일);

uploadBytes 메서드를 이용하여 파일을 올릴 수 있지만, 만약 현재 올릴 파일 이름과 스토리지에 있는 파일 이름이 겹친다면, 파일을 업로드 했을시 기존에 있는 파일이 사라지는 문제가 생길 수 있음
이런 문제점을 해결하기 위해 UUID 라이브러리를 사용하여 스토리지에 저장할 파일 이름을 고유한 Key 값을 랜덤하게 만들어서 부여해주면 업로드할 파일 이름 중복 문제를 해결 할 수 있음.

2-2 UUID란?

UUID는 범용 고유 식별자의 약자로 고유한 아이디 값을 생성할때 사용할 수 있는 표준이다.
UUID는 총 5가지 버전이 있지만, 이렇게 랜덤으로 고유한 식별자를 만들 때는 v4버전을 많이 사용한다고 한다(?)

2-3 UUID 적용 예시

npm install uuid 명령어로 uuid 라이브러리를 설치 하고나서

npm install uuid

import { v4 as uuid } from 'uuid';					//v4 버전 사용
import { ref, uploadBytes, } from "firebase/storage";

 const UploadImage =  () => {
    const uploadFileName = uuid() + ".png";			// uuid를 통해 파일 이름 랜덤으로 뽑기

    if (inputimage === null) return;
    const imageRef = ref(storage, `images/${uploadFileName}`);		// images 폴더에 저장
    uploadBytes(imageRef, inputimage);
 }

2-4 storage에 저장된 파일 이름을 Firestore DB에 따로 저장


  const [titlename, setTitleName] = useState("");
  const [name, setName] = useState("");
  const [inputimage, setInputImage] = useState();

  const onClickUploadB = async () => {
    const uploadFileName = uuid() + ".png";

    if (inputimage === null) return;
    const imageRef = ref(storage, `images/${uploadFileName}`);	// 스토리지에 이미지 업로드 
    uploadBytes(imageRef, inputimage);

    await addDoc(collection(firestore, `auth`),					// 파이어스토어에 데이터 추가
      {
        uploadFileName,							// 파일 이름
      })
  }


3. Storage에서 이미지 불러오기

3-1 getDownloadURL 메서드

getDownloadURL 메서드를 통해 stroage에서 파일을 다운로드 할 수 있음

import { ref, getDownloadURL, } from "firebase/storage";

      const reference = ref(스토리지, `스토리지에서 다운 받을 파일 경로 및 이름`);
      await getDownloadURL(reference).then((x) => {
		  x......
      })
      


4. 게시판 만들어보기

꾸미진 않았지만(디자인이 많이 처참하다), 대충 게시판 기능을 하는 예시를 직접 만들어봤다.

코드

// index.js

import firestore from "../firebase/firestore"
import storage from "../firebase/storage"

import { collection, addDoc, onSnapshot } from "firebase/firestore";
import { ref, uploadBytes, } from "firebase/storage";
import { useState, useEffect } from "react";
import Post from "@/compents/post";
import { v4 as uuid } from 'uuid';

export default function Home() {

  const [titlename, setTitleName] = useState("");
  const [name, setName] = useState("");
  const [inputimage, setInputImage] = useState();
  const [list, setList] = useState([]);

  const onClickUploadB = async () => 			// 버튼 클릭시 스토리지에 이미지 업로드 및 파이어스토어에 데이터 등록
  {					
    const uploadFileName = uuid() + ".png";
    console.log(uploadFileName);

    if (inputimage === null) return;
    const imageRef = ref(storage, `images/${uploadFileName}`);
    uploadBytes(imageRef, inputimage);

    await addDoc(collection(firestore, `auth`),
      {
        titlename,
        name,
        uploadFileName,
      })
    setTitleName("");
    setName("");
    setInputImage("");
  }


  useEffect(() => 					// onSnapshot 을 이용하여 실시간으로 게시판 글 정보 가져오기
  {
    const snap = onSnapshot(collection(firestore, "auth"), (querySnapshot) => {
      let tempList = [];
      querySnapshot.forEach((doc) => {
        let data = doc.data();
        tempList.push(data);
      });
      setList(tempList);
    });
  }, []);


  return (
    <div>
      <div>
        <form onSubmit={(event) => {
          event.preventDefault();
        }}>
          <label>제목</label>
          <input onChange={(event) => { setTitleName(event.target.value) }} />
          <label>작성자 이름</label>
          <input onChange={(event) => { setName(event.target.value) }} />
          <label>이미지 파일</label>
          <input type="file" onChange={(event) => { setInputImage(event.target.files[0]) }} />
        </form>
      </div>
      <button onClick={onClickUploadB}> 업로드 </button>

      <div>
        {list.map((item) => (
          <div key={item.id} >
            <div>
              <label>게시글 제목 : </label>
              <h4>{item.titlename} </h4>
              <label>작성자 : </label>
              <h4>{item.name} </h4>
              <Post imagename={item.uploadFileName} />
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

FireStore로부터 여러 데이터들을 가져올 때, getDocs 메서드를 쓰는 방식과 onSnapshot 메서드를 쓰는 방법이 있지만, 그 둘의 차이로는 getDocs는 데이터를 호출할 때 1번만 받아오고 끝이다. 즉 firebase 데이터가 변경되면 변경 사항을 확인하기 위해 다시 호출해야한다. 하지만, onSnapshot 메서드는 데이터베이스에 내용이 변경될 때마다 snapshot을 실시간(자동적)으로 업데이트 하기 때문에 다시 호출할 필요 없음.
그렇기 때문에 데이터가 자주 추가되거나 삭제되는 게시판 특성을 살려 onSnapshot 메서드를 사용하여 글 정보들을 불러왔다.

// Post.js
import storage from "../firebase/storage"
import { useState, useEffect } from "react";
import Image from "next/image"

import { ref, getDownloadURL, } from "firebase/storage";

export default function Post({ imagename }) {

    const [imgurl, setImgurl] = useState();


    useEffect(() => {
        const func = async () => {
            if (imagename !== undefined) {
                const reference = ref(storage, `images/${imagename}`);
                await getDownloadURL(reference).then((x: any) => {
                    setImgurl(x);
                })
            }
        }
        func();
    }, []);

    return (
        <div>
            <Image unoptimized={true} alt="text" width={300} height={150} src={imgurl} />
        </div>
    )
}

0개의 댓글