-
Social Media 만들기 - 12) get posts 가져오기NODE.JS 2021. 7. 12. 01:33
이제 홈페이지에 포스트를 가지고 와서 보여주도록 하자.
포스트를 가지고 오는 라우터를 먼저 작성해준다.
(auth 미들웨어에서 req.user = user로 등록해줬다)이를 이용한다.
postRouter.get('/', auth, async(req, res)=>{ try{ const user = await User.findOne({_id:req.user.id}) const posts = await Post.find({user:[...user.following, user._id]}) .sort('-createdAt') .populate("user likes", "avatar username fullname followers following") // .populate({ // path:"comments", // populate:{ // path:"user likes", // select:"-password" // } // }) res.json({ msg:'Get posts successfully', result:posts.length, posts }) }catch(err){ return res.status(500).json({message:err.message}) } })
postConstants
export const GET_HOME_POSTS_REQUEST = 'GET_HOME_POSTS_REQUEST' export const GET_HOME_POSTS_SUCCESS = 'GET_HOME_POSTS_SUCCESS' export const GET_HOME_POSTS_FAIL = 'GET_HOME_POSTS_FAIL' export const GET_POSTS_RESET = 'GET_POSTS_RESET'
postActions을 만들어준다.
export const getHomePosts =() => async(dispatch, getState)=>{ dispatch({ type:GET_HOME_POSTS_REQUEST, payload:{loading:true} }) const {userLogin: {userInfo}} = getState() try{ const res = await axios.get('/api/post',{headers: {authorization : `Bearer ${userInfo.token}`}}) console.log(res.data.posts) dispatch({ type:GET_HOME_POSTS_SUCCESS, payload:res.data.posts }) }catch(error){ dispatch({ type:GET_HOME_POSTS_FAIL, payload: error.response && error.response.data.message ? error.response.data.message : error.message }) } }
post Reducers
export const postGetReducer = (state={}, action)=>{ switch(action.type){ case GET_HOME_POSTS_REQUEST: return {loading:true} case GET_HOME_POSTS_SUCCESS: return {loading:false, posts:action.payload, success:true} case GET_HOME_POSTS_FAIL: return {loading:false, error:action.payload, success:false} case GET_POSTS_RESET: return {} default: return state } }
store.js
getposts:postGetReducer,
이제 포스트를 가지고 오도록하자.
Post 컴포넌트에 다음과 같이 입력해준다.
포스트를 생성완료하면 생성된 포스트 가지고 오도록 설정해준다.
import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { createPost, getHomePosts } from '../_actions/postActions' import { GET_POSTS_RESET } from '../_constants/postConstants' import PostCard from './PostCard' function Post() { const userLogin = useSelector(state => state.userLogin) const {userInfo} = userLogin const dispatch = useDispatch() const getposts = useSelector(state => state.getposts) const {posts} = getposts const createpost = useSelector(state => state.createpost) const {success} = createpost useEffect(() => { if(success){ dispatch({type:GET_POSTS_RESET}) dispatch({type:CREATE_POST_RESET}) } dispatch(getHomePosts()) }, [dispatch, success]) return ( <div className="posts"> { posts && posts.map(post=>( <PostCard key={post._id} post={post}></PostCard> )) } </div> ) } export default Post
이제 포스트 카드 부분을 짜줄 것이다.
포스트카드는 카드헤더, 바디, 풋터, 커맨트 , 인풋 부분으로 나눠줄 것이다.
import CardHeader from './post_card/CardHeader' import CardBody from './post_card/CardBody' import CardFooter from './post_card/CardFooter' import Comments from './post_card/Comments' import InputComment from './post_card/InputComment' import React from 'react' function PostCard({post}) { return ( <div className="card my-3"> <CardHeader post = {post}/> <CardBody post = {post} /> <CardFooter post = {post} /> <Comments post = {post} /> <InputComment Comment = {post} /> </div> ) } export default PostCard
CardHeader.js
여기서 moment를 사용하기 위해서 npm i moment 해준다. (시간 사용한다)
import React from 'react' import Avatar from '../Avatar' import {Link} from 'react-router-dom' import moment from 'moment' import { useDispatch, useSelector } from 'react-redux' function CardHeader({post}) { const userLogin = useSelector(state => state.userLogin) const {userInfo} = userLogin const dispatch = useDispatch() const handleEditPost = () =>{ } return ( <div className="card_header"> <div className="d-flex"> <Avatar src={post.user.avatar} size="big-avatar"/> <div className="card_name"> <h6 className="m-0"> <Link to={`/profile/${post.user._id}`} className="text-dark"> {post.user.username} </Link> </h6> <small className="text-muted"> {moment(post.createdAt).fromNow()} </small> </div> </div> <div className="nav-item dropdown"> <span className="material-icons" id="moreLink" data-toggle="dropdown"> more_horiz </span> <div className="dropdown-menu"> { userInfo.user._id ===post.user._id && <> <div className="dropdown-item"> <span className="material-icons" onClick={handleEditPost}>create</span> Edit Post </div> <div className="dropdown-item"> <span className="material-icons">delete_outline</span> Remove Post </div> </> } <div className="dropdown-item"> <span className="material-icons">content_copy</span> Copy Link </div> </div> </div> </div> ) } export default CardHeader
CardBody.js
import React, { useState } from 'react' import Carousel from '../Carousel' function CardBody({post}) { const [readMore, setReadMore] = useState(false) return ( <div className="card_body"> <div className="card_body-content"> <span> { post.content.length<60 ? post.content : readMore? post.content + ' ' : post.content.slice(0,60) + '.....' } </span> { post.content.length >60 && <span className="readMore" onClick={()=>setReadMore(!readMore)}> {readMore? 'Hide content' : 'Read more'} </span> } </div> { post.images.length>0 && <Carousel images={post.images} id={post._id}/> } </div> ) } export default CardBody
이미지를 보여주는 Carousel을 작성하도록 하자.
부트스트랩의 Carousel을 이용한다.
import React from 'react' import { useSelector } from 'react-redux' function Carousel({images, id}) { const isActive = index=>{ if(index===0) return "active" } const theme = useSelector(state => state.theme) return ( <div id={`image${id}`} className="carousel slide" data-ride="carousel"> <ol className="carousel-indicators" style={{zIndex: 1}}> { images.map((img, index) => ( <li key={index} data-target={`#image${id}`} data-slide-to={index} className={isActive(index)} /> )) } </ol> <div className="carousel-inner"> { images.map((img, index) => ( <div key={index} className={`carousel-item ${isActive(index)}`}> { img.data.match(/video/i) ? <video controls src={img.data} className="d-block w-100" alt={img.data} style={{filter: theme ? 'invert(1)' : 'invert(0)'}} /> : <img src={img.data} className="d-block w-100" alt={img.data} style={{filter: theme ? 'invert(1)' : 'invert(0)'}} /> } </div> )) } </div> { images.length > 1 && <> <a className="carousel-control-prev" href={`#image${id}`} role="button" data-slide="prev" style={{width: '5%'}}> <span className="carousel-control-prev-icon" aria-hidden="true"></span> <span className="sr-only">Previous</span> </a> <a className="carousel-control-next" href={`#image${id}`} role="button" data-slide="next" style={{width: '5%'}}> <span className="carousel-control-next-icon" aria-hidden="true"></span> <span className="sr-only">Next</span> </a> </> } </div> ) } export default Carousel
CardFooter.js
import React from 'react' import {Link} from 'react-router-dom' import Send from '../../images/send.svg' function CardFooter({post}) { return ( <div className="card_footer"> <div className="card_icon_menu"> <div> <i className="far fa-heart"></i> <Link to={`/post/${post._id}`} className="text-dark"> <i className="far fa-comment"></i> </Link> <img src={Send} alt="Send" /> </div> <i className="far fa-bookmark"></i> </div> <div className="d-flex justify-content-between"> <h6 style={{padding: '0 25px', cursor:'pointer'}}>{post.likes.length}</h6> <h6 style={{padding: '0 25px', cursor:'pointer'}}>{post.comments.length} comments</h6> </div> </div> ) } export default CardFooter
잘 작동한다.
'NODE.JS' 카테고리의 다른 글
Social Media 만들기 - 14) like, unlike post 구현하기 (0) 2021.07.13 Social Media 만들기 - 13) update post , delete post, clipboard에 주소 복사 구현하기(Card Header) (0) 2021.07.12 req.body 가 undefined으로 뜰 때 (2) 2021.07.11 Social Media 만들기 - 11)createPost 작성하기 (0) 2021.07.11 Social Media 만들기 - 10)Status, create post 창 만들기 (0) 2021.07.10