Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Joon's Space

[React] React + Firebase 연동 (Cloud Firestore) (2) 본문

Web/React

[React] React + Firebase 연동 (Cloud Firestore) (2)

Happy Joon 2022. 2. 22. 22:53

Form

게시글을 입력하고 submit 할 Form을 생성한다. 

// Home.js

const Home = () => {
    const [nweet, setNweet] = useState("");
    const onSubmit = (event) => {
        event.preventDefault();
    };
    const onChange = (event) => {
        const { 
            target: {value},
        } = event;
        setNweet(value);
    }
    return (
        <div>
            <form>
                <input value={nweet} onChange={onChange} type="text" placeholder="What's on your mind?" maxLength={120} />
                <input type="submit" value="Nweet" />
            </form>
        </div>
    );
};
export default Home

 

게시글을 입력하고, 제출 버튼을 클릭하여 앞서 입력해서 제출한 정보글들을 저장할 공간이 필요한데, 이때 firebase의 database를 사용한다.

 

Cloud Firestore

firebase의 Cloud Firestore에서 새로운 Database를 생성하는데, 이 프로젝트는 테스트용이기 때문에 다음과 같이 test mode로 생성한다.

 

우리가 사용할 Cloud Firestore는 SQL문이 필요하지 않은 NoSQL 데이터 베이스이므로, 사용 방법이 훨씬 간단하고, 프로그래밍을 하지 않아도 되는 부분이 많아 보다 다른 데이터 베이스보다 유연하게 사용할 수 있다. 

 

SQL 데이터베이스와 달리 테이블이나 행이 없으며, Collection 으로 정리되는 문서에 데이터를 저장한다. 

 

Collection 은 Documents 들을 모으는 곳이므로, 우리가 tweet 할 글들을 모을 tweet Collection 하나를 생성한다.

(사용에 따라, DM을 저장할 DM, 유튜브 같은 웹페이지에는 Videos, Comments라는 Collection을 가질 것이다.)

 

 

CRUD

 

1. 데이터 추가

collection, addDoc함수가 필요한데, 일단 addDoc에는 collection함수와, 추가할 데이터 정보(필드 정보 포함)하여 파라미터로 넣어준다. collection 함수에는 firestore 인스턴스 정보와, 컬렉션 이름을 넣어준다. 

 

// Home.js

import {addDoc, collection} from "firebase/firestore";

const docRef = await addDoc(collection(dbService, "콜렉션 이름"), {
    필드1:필드1에 넣을 값,
    필드2:필드2에 넣을 값,
});

 

2. 실시간 데이터 받아오기

collection를 파라미터로 받는 query를 생성하여, onSnapshot() 함수를 이용하여 데이터를 수신받을 때마다 실행하는 함수를 실행시킨다.  이때 snapshot을 등록된 순서대로 받기 위해서, query에 orderBy("createdAt")를 추가로 파라미터 인자로 넣어준다.

 

// Home.js

useEffect(() => {
    const q = query(collection(dbService, "nweets"), orderBy("createdAt"));
    onSnapshot(q, (snapshot) => {
        const nweetArr = snapshot.docs.map((doc) => ({
            id:doc.id,
            ...doc.data(),
        }));
        setNweets(nweetArr);
    });
}, []);

 

3. 데이터 수정 및 삭제

데이터를 수정 및 삭제하기 위해서, 내가 user인지부터 check 해야 한다.

Nweet 컴포넌트를 생성하여, Home.js의 userObj의 인자로 그 tweet의 작성자인지 확인하여 True/False인자를 받아준다.

 

// Home.js

{nweets.map((nweet) =>(
    <Nweet key={nweet.id} nweetObj={nweet} isOwner={nweet.creatorId === userObj.uid}/>
))}

 

// Nweet.js

import React from "react";

const Nweet = ({nweetObj, isOwner}) => {
    return(
    <div>
        <h4>{nweetObj.text}</h4>
        {isOwner && (
            <>
                <button>Delete Nweet</button>
                <button>Edit Nweet</button>
            </>
        )}
    </div>
    );
}

export default Nweet;

 

 

Owner가 맞으면, Delete/Edit 버튼이 활성화 되는 것을 확인할 수 있다. (맨 밑 uid는 임의로 변경하였음)

데이터 삭제

// Nweet.js

await deleteDoc(doc(dbService, 'nweets', `${nweetObj.id}` ));

데이터 수정

// Nweet.js

const [newNweet, setNewNweet] = useState(nweetObj.text);

await updateDoc(doc(dbService, 'nweets', `${nweetObj.id}`), {
    text:newNweet,
});

 

ImageUpload

WEB API에서 FileReader()를 사용한다. FileReader가 즉시 파일을 읽는 게 아니기 때문에 onloadend 이벤트 핸들러를 붙여서 콜백으로 파일을 다 읽었다는 것을 알려주도록 해야 한다. 

 

그리고 파일을 읽는 방식은 크게 여러 가지가 있는데, 데이터를 읽고 그것을 text로 변환하면 이상한 글자가 나오기 때문에, 

readAsDataURL로  Filed을 읽으면 결괏값이 base64 형식으로 인코딩한 값이 나오는데, 이 값을 브라우저에 입력하면 그 값을 원래 데이터로 표시해준다. 

 

이미지의 경우 이 값을 img 태그의 src로 입력하면 그 이미지가 표시된다.

 

// Home.js

const [attachment, setAttachment] = useState();

const onFileChange = (event) => {
    const {
        target: { files },
    } = event;
    const theFile = files[0];
    const reader = new FileReader();
    reader.onloadend = (finishedEvent) => {
        const {
            currentTarget: { result },
        } = finishedEvent;
        setAttachment(result);
    }
    reader.readAsDataURL(theFile);
}

return (
    <input onChange={onFileChange} type="file" accept="image/*" />
    {attachment && <img src={attachment} width="50px" />}
);

 

Firebase에 이미지 업로드

// Home.js

import { getStorage, ref, uploadString } from "firebase/storage";

const storageService = getStorage();
const fileRef = ref(storageService, `${userObj.uid}/${v4()}`);
const response = uploadString(fileRef, attachment, 'data_url').then((snapshot) => {
    console.log('Uploaded a data_url string!');
});

업로드를 하기 위해서, 파일 reference를 생성해야 하는데, 처음 파일 폴더인 사용자의 uid, 그리고 파일 이름은 랜덤으로 주기 위해 uuid의 v4() 함수를 쓰면 랜덤으로 이름을 지정해준다. 

 

uploadString() 함수를 이용해서, fileRef와 FileReader API로 얻은 data_url값 그리고 데이터 타입을 적어주면 firebase storage에 저장된다.

 

이미지가 업로드 되었음을 확인

response로 받은 값을 getDownloadURL()를 활용하여 받은 attachmentUrl로 업로드한 이미지를 표시할 수 있다.

위에서 언급했듯이 img태그의 src에 attachmentUrl을 붙여 넣는다. 

// Home.js

const onSubmit = async(event) => {
    event.preventDefault();
    let attachmentUrl = "";
    // 사진이 없을 경우
    if (attachment != "") {
        const attachmentRef = ref(storageService, `${userObj.uid}/${v4()}`);
        const response = await uploadString(attachmentRef, attachment, 'data_url');
        attachmentUrl = await getDownloadURL(response.ref);   
    }
    const nweetObj = {
        text:nweet,
        createdAt:Date.now(),
        creatorId: userObj.uid,
        attachmentUrl
    }
    await addDoc(collection(dbService, "nweets"), nweetObj);
    setNweet("");
    setAttachment("");
}

 

위에서 받은 nweetObj 값에 attachmentUrl이 추가되었으므로, Nweet 파일에 표시될 div 사이에 다음을 추가한다. attachmentUrl이 빈 값이 아닐 경우에만 추가한다.

// Nweet.js
{nweetObj.attachmentUrl && <img src={nweetObj.attachmentUrl} width="50px" height="50px" />}

 

업로드한 사진 삭제

onDeleteClick 함수에 deleteObject() 함수만 추가한다. 파라미터로 필요한 인자는 reference 값인데, 우리가 img url로 사용한 값을 이용하면 지우려고 하는 이미지의 reference값을 얻을 수 있다.

 

// Nweet.js

const onDeleteClick = async () => {
    const ok = window.confirm("Are you sure you want to delete this nweet?");
    if(ok) {
        // delete nweet
        await deleteDoc(doc(dbService, 'nweets', `${nweetObj.id}` ));
        await deleteObject(ref(storageService, nweetObj.attachmentUrl));
    }
};
반응형