Twitter_Clone(3)

😁 로그인 기능을 구현해봤으니 Tweet이 가능하게 만들어 봅니다.
그러기 위해서는 Database가 필요.

📌 Tweet window

💻 Home.js

const Home = () =>{
    const [tweet, setTweet] = useState("");
    const onSubmit = (event) =>{
        event.PreventDefault();
    }
    const onChange = (event) =>{
        const{target:{value}}  = event;
        setTweet(value);
    }
    return(
    <div>
        <form>
            <input value = {tweet} onChange= {onChange}type="text" placeholder="What is on your mind?" maxLength={120} />
            <input type="submit" value="Tweet" />
        </form>
    </div>
    );
}

📌 Create Tweet

💻fbase.js
import "firebase/firestore";

export const dbService = firebase.firestore();

💻Home.js

const Home = () =>{
    const [tweet, setTweet] = useState("");
    const onSubmit = async(event) =>{
        event.preventDefault();
        await dbService.collection("tweets").add({
            tweet,
            createdAt:Date.now(),
        });
        setTweet("");
    }
    const onChange = (event) =>{
        const{target:{value}}  = event;
        setTweet(value);
    }
    ...
        </form>
    </div>
    );
}

📌 코드 설명

  1. fbase에서 fireStore 가져와 export
  2. onSubmit은 db에 저장하기에 async, await 사용.
  3. dbService.collection("tweets").add({ tweet, createdAt : Date.now(),
    : tweets라는 이름을 가진 collection안에 넣어줌. tweet 값과 createdAt 값.
  4. setTweet("") : 비워주기.

📌 DB -> Home

💻 Home.js
const Home = () =>{
    ...
    const [tweets, setTweets] = useState([]);
    const getTweets = async() =>{
        const dbtweets =await dbService.collection("tweets").get();
        dbtweets.forEach((document) =>{
            const tweetObject = {
                ...document.data(),
                id:document.id,
            }
            setTweets(prev => [tweetObject, ...prev]);
        });
    }
    useEffect(() =>{
        getTweets();
    },[])
  ...
    return(
    ...
        <div>
            {tweets.map((tweet) => 
            (<div key={tweet.id}>
                <h4>{tweet.tweet}</h4>
                </div>
              ))}
        </div>
    ...
}

📌 코드 설명

  1. const [tweets, setTweets] = useState([]); : firebase에서 가져올 값.
  2. dbService.collection("tweets").get(); : 반환 값이 QuerySnapShat 임. 그러므로 data, id만 forEach로 뽑아주기.
  3. tweetObject에 이전의 data값과, id 넣어주기.
  4. setTweets(prev => [tweetObject, ...prev]); : prev 값을 [tweetObject, ...prev] 값으로 return.
  5. map을 사용하여 key 값 지정 and tweet 값 화면에 보여주기.

📌 RealTime Tweet

😁 새로고침을 하지 않아도 tweet이 자동으로 새로고침 되게 설정.

💻 Home.js
const Home = ({userObj}) =>{
  ...
    useEffect(() =>{
        //getTweets();
        dbService.collection("tweets").onSnapshot((snapshot) =>{
            const tweetArray = snapshot.docs.map(doc=>({id:doc.id, ...doc.data()}));
            setTweets(tweetArray);
        })
    },[])
  ...
}

📌 코드 설명

  1. {userObj}는 App.js로부터 가져온 uid.
  2. getTweets는 오래된 데이터만 가지고 있기에 realtime을 위해 useEffect안에 넣어준다.
  3. onSnapShot을 이용 : db가 read, write, update 될 때 update 됨을 알려주는 App에서의 onAuthStateChanged 와 같은 기능.

🧨 주의 : 무조건 Realtime이 좋은 것은 아님. 채팅이나 실시간 업데이트가 필요한 기능에서는 좋으나 그렇지 않은경우 너무 쓸데없는 realtime일 수 있다.

📌 Delete and Update

💻 Tweet.js

const Tweet = ({tweetObj, isOwner}) => (
    <div>
        <h4>{tweetObj.tweet}</h4>
        {isOwner && (
            <>
            <button>DEL</button>
            <button>EDIT</button>
            </>
        )}
    </div>
)

💻 Home.js

{tweets.map((tweet) => 
            (<Tweet key={tweet.id} tweetObj = {tweet} isOwner={tweet.creatorId===userObj.uid} />
                ))}

📌 코드 설명

  1. Home.js에서 Tweet 부분 너무 커지기에 따로 Tweet.js 나눠줌.
  2. isOwner={tweet.creatorId===userObj.uid} : tweet.creatorId와 userObj의 uid가 같은 경우 true, 아니면 false 반환. 글을 쓴 사람과 로그인되어있는 사람이 같은지.
  3. Tweet에서 {isOwner && ( <> <button>DEL</button> <button>EDIT</button> </> )} : isOwner이 true이면 button이 보여짐.
💻 Tweets.js

const Tweet = ({tweetObj, isOwner}) =>  {
    const [editing, setEditing] = useState(false);
    const [newTweet, setNewTweet] = useState(tweetObj.tweet);
    const onDeleteClick = async() => {
        const ok = window.confirm("Are you sure you want to delete this tweet?");
        if(ok){
            await dbService.doc(`tweets/${tweetObj.id}`).delete();
        }
    };
    const toggleEditing = () =>setEditing((prev) => !prev);
    const onSubmit = async(e) => {
        e.preventDefault();
        await dbService.doc(`tweets/${tweetObj.id}`).update({
            tweet:newTweet,
        });
        setEditing(false);
    }
    const onChange = (e) =>{
        const {target:{value}} = e;
        setNewTweet(value);
    }
    return(
        <div>
            {
                editing ? (
                    <>
                    <form onSubmit={onSubmit}>
                        <input onChange = {onChange}type = "text" placeholder = "Edit" value={newTweet} required />
                        <input type="submit" value="Update Tweet" />
                    </form>
                    <button onClick={toggleEditing}>Cancel</button>
                    </>
                    ) : 
                <>
                    <h4>{tweetObj.tweet}</h4>
                    {isOwner && (
                    <>
                    <button onClick={onDeleteClick}>DEL</button>
                    <button onClick={toggleEditing}>EDIT</button>
                    </>
            )}
            </>
            }
        </div>
    )
}

📌 코드 설명

  1. onDeleteClick : Delete 버튼을 눌렀을 때 실행되는 함수.
    • ok는 window 확인창이 나왔을 때의 답변, "예"는 true
    • true 일때 dbService.doc().delete() 로 해당 id에 맞는 DB 삭제.
  2. toggleEditing : Edit의 상태를 !상태로 반환시켜주는 함수.
  3. onSubmit : Edit form에 넣어주는 함수.
    • dbService.doc().update()로 tweet 내용 업데이트.
    • setEditing : false로 만들어주어 Edit창 닫아주기.
  4. return
    • editing의 bool 값에 따라 Edit창이 나올지, Tweet 창이 나올지를 결정.

📌 Uploading

💻 fbase.js
import "firebase/storage";

export const storageService = firebase.storage();

💻 Home.js
import {v4 as uuidv4} from "uuid"

const onSubmit = async(event) =>{
        event.preventDefault();
        const fileRef = storageService.ref().child(`${userObj.uid}/${uuidv4()}`);
        const response = await fileRef.putString(attachment, "data_url");
        // await dbService.collection("tweets").add({
        //     tweet : tweet,
        //     createdAt : Date.now(),
        //     creatorId : userObj.uid,
        // });
        // setTweet("");
    }

📌 코드 설명

  1. import "firebase/storage"; : 사진 업로드에 필요한 import 및 export

  2. terminal에 npm i uuid : 랜덤으로 만들어주는 식별자.

  3. storageService.ref().child(userObj.uid/{userObj.uid}/{uuidv4()}); :ref(구글 클라우드 스토리지의 객체)의 child(이미지의 path)를 만들어 path 설정.

  4. await fileRef.putString(attachment, "data_url"); : data_url의 data와 data_type(data_url)을 넣어줌.

    📌 File Url and Tweet

    😀 Tweet과 img File 함께 firestore에 올려주기❗❗

💻Home.js
const onSubmit = async(event) =>{
        event.preventDefault();
        let attachmentUrl = "";
        if(attachment != ""){
            const fileRef = storageService.ref().child(`${userObj.uid}/${uuidv4()}`);
            const response = await fileRef.putString(attachment, "data_url");
            attachmentUrl = await response.ref.getDownloadURL();
        }
        const tweetObj = {
            tweet : tweet,
            createdAt : Date.now(),
            creatorId : userObj.uid,
            attachmentUrl,
        }
        await dbService.collection("tweets").add(tweetObj);
        setTweet("");
        setAttachment("");
    }

💻 TWeet.js
{tweetObj.attachmentUrl && <img src={tweetObj.attachmentUrl} width="50px" height="50px"/>}

📌 코드 설명

  1. if(attachment != "") : 사진을 안 올릴 경우를 위한 if문.
    • putSTring의 then으로 반환되는 값중 ref의 getDownloadURL 사용하여 attachmentUrl 변수에 넣어주기.
  2. tweetObj 객체에 attachment도 넣어주기.

📌 Deleteing File

💻Tweet.js

if(ok){
            await dbService.doc(`tweets/${tweetObj.id}`).delete();
            await storageService.refFromURL(tweetObj.attachmentUrl).delete();
        }

📌 코드 설명

  1. delete키를 눌렀을 때, storageService.refFromURL(tweetObj.attachmentUrl).delete();로 storage의 파일 삭제시켜주기. refFromURL을 이용.

<참고 : 노마드코더>

좋은 웹페이지 즐겨찾기