본문 바로가기

Projects/React + Firebase 링크드인 클론

#7. 저장한 글들을 피드에 띄워주기 | Post.js

Post.js 부분을 완성한다

Feed 컴포넌트를 만들고, 그 안에 글을 작성할 수 있는 Input 부분을 완성하였다. 이번에는 저장된 글들을 DB에서 불러와 이를 보여주도록 한다. 이를 위해선 컴포넌트 실행시 글을 보여줄 수 있도록 useEffect를 사용하고, 저장된 글을 불러오기 위해 Firebase의 DB를 이용해야 하고, 저장된 내용들을 하나하나 띄워줄 수 있도록 반복문을 사용해야 한다. 이 때 JSX부분에서 반복기능을 구현하기 위해 Map함수를 사용하도록 한다.

 

먼저 Feed.js를 작성한다

 

import React, { useState, useEffect } from 'react'
import './Feed.css'
import CreateIcon from '@material-ui/icons/Create'
import InputOption from './InputOption'
import ImageIcon from '@material-ui/icons/Image'
import SubscriptionsIcon from '@material-ui/icons/Subscriptions'
import EventNoteIcon from '@material-ui/icons/EventNote'
import CalendarViewDayIcon from '@material-ui/icons/CalendarViewDay'
import Post from './Post'
import { db } from './firebase'
import firebase from 'firebase'
import { useSelector } from 'react-redux'
import { selectUser } from './features/userSlice'
import FlipMove from "react-flip-move"

function Feed() {

    const user = useSelector(selectUser);
    const [input, setInput] = useState('');
    const [posts, setPosts] = useState([]);

	//메인화면이 보이고, Feed컴포넌트가 실행되면,
    //저장되어 있던 글들을 불러와야 한다
    useEffect(()=>{
    	//posts collection에 저장되어 있던 글들을 시간순으로 캡처한다
        db.collection("posts").orderBy('timestamp', 'desc').onSnapshot((snapshot)=>
            setPosts(
            	//DB에 저장된 글들을 map 함수를 이용해 하나하나씩 꺼내서 Posts State에 담아준다
                //이렇게 Posts State에 담으면, 나중에 State에 담긴 글들을 하나씩 꺼내면된다.
                snapshot.docs.map((doc)=>({
                    id: doc.id,
                    data: doc.data()
                }))
            )
        );
    },[]);

    const sendPost = e => {
        e.preventDefault();

        db.collection("posts").add({
            name: user.displayName,
            description: user.email,
            message: input,
            photoUrl: user.photoUrl || "",
            timestamp: firebase.firestore.FieldValue.serverTimestamp()
        });

        setInput("");
    };


    return (
        <div className="feed">
        	//글을 작성할 수 있는 부분
            <div className="feed__inputContainer">
                <div className="feed__input">
                    <CreateIcon />
                    <form>
                        <input type="text" value={input} onChange={e=>setInput(e.target.value)} />
                        <button type="submit" onClick={sendPost}>Send</button>
                    </form>
                </div>
                <div className="feed__inputOptions">
                    <InputOption Icon={ImageIcon} title="Photo" color="#70B5F9" />
                    <InputOption Icon={SubscriptionsIcon} title="Video" color="#E7A33E" />
                    <InputOption Icon={EventNoteIcon} title="Event" color="#C0CBCD" />
                    <InputOption Icon={CalendarViewDayIcon} title="Write article" color="#7FC15E" />
                </div>
            </div>
            
            //작성된 글들을 보여줄 때 애니메이션 효과를 넣는다
            //React-flip-move 라이브러리 사용
            <FlipMove>
                {
                	//앞서 DB에서 꺼내서 posts state에 담긴 글들을 다시 하나 하나 꺼내준다
                    //글을 꺼낼 때, id, 작성자, 이메일, 글내용의 정보를 꺼내 Post 컴포넌트에 전달
                    posts.map(({id, data:{name, description, message, photoUrl}})=>(
                    <Post
                        key={id}
                        name={name} 
                        description={description}
                        message={message}
                        photoUrl={photoUrl}
                    />
                    ))
                }
            </FlipMove>
        </div>
    )
}

export default Feed

 

저장된 글들을 보여주기 위해선 3가지 단계를 거쳐야 한다.

1) DB에 저장된 글들을 하나하나 꺼내서 posts state에 담아둔다.

2) posts state에 담긴 글들을 다시 하나하나 꺼내서 그 안에 담긴 정보를 Post 컴포넌트에 전달한다

3) Post 컴포넌트는 props로 전달받은 정보들을 UI로 띄워준다

 

이번에는 Post 컴포넌트를 완성해본다. 이를 위해 Post.js를 작성한다.

 

import { Avatar } from '@material-ui/core'
import { ChatOutlined, SendOutlined, ShareOutlined, ThumbUpAltOutlined } from '@material-ui/icons'
import React, { forwardRef } from 'react'
import InputOption from './InputOption'
import './Post.css'

//FlipMove로 애니메이션 효과를 주기 위해 rfce 형식을 수정하였다
const Post = forwardRef(({ name, description, message, photoUrl }, ref) => {
    return (
        <div ref={ref} className="post">
        
        	//앞서서 posts state에 담긴 정보를 Post 컴포넌트로 전달하였다
            //이 정보들을 데이터 바인딩 해준다
            <div className="post__header">
                <Avatar src={photoUrl}>{name[0]}</Avatar>
                <div className="post__info">
                    <h2>{name}</h2>
                    <p>{description}</p>
                </div>
            </div>

            <div className="post__body">
                <p>{message}</p>
            </div>

			//각 글들 마다 필요한 상세 기능들을 띄워준다.
            <div className="post__buttons">
                <InputOption Icon={ThumbUpAltOutlined} title="Like" color="gray" />
                <InputOption Icon={ChatOutlined} title="Comment" color="gray" />
                <InputOption Icon={ShareOutlined} title="Share" color="gray" />
                <InputOption Icon={SendOutlined} title="Send" color="gray" />
            </div>

        </div>
    )
})

export default Post