-
Social Media 만들기 - 16) profile post, detail post 구현하기NODE.JS 2021. 7. 19. 22:22
이제 유저프로필 페이지에서 해당 user의 포스트를 가져오자.
유저아이디에 해당하는 포스트를 가져오는 Router를 작성한다.
postRouter.get('/user/:id', auth, async(req, res)=>{ try{ const posts = await Post.find({user: req.params.id}) .sort("-createdAt") .populate("user likes", "avatar username fullname followers following") .populate({ path:"comments", populate:{ path:"user likes", select:"-password" }, sort:'-createdAt' }) res.json({posts}) }catch(err){ return res.status(500).json({message:err.message}) } })
profile.constants.js
export const PROFILE_GETPOST_SUCCESS='PROFILE_GETPOST_SUCCESS'
profileuser를 가져오는 코드에 다음과 같이 post를 가져오는 코드를 추가해준다.
export const getProfileUser = (userId)=>async (dispatch, getState)=>{ dispatch({ type:PROFILE_GETUSER_REQUEST, payload:userId }) const {userLogin:{userInfo}} = getState() try{ const {data} = await axios.get(`/api/users/${userId}`,{headers:{authorization : `Bearer ${userInfo?.token}`} }) const res = await axios.get(`/api/post/user/${userId}`,{headers: {authorization : `Bearer ${userInfo.token}`}}) dispatch({ type:PROFILE_GETUSER_SUCCESS, payload:data }) dispatch({ type:PROFILE_GETPOST_SUCCESS, payload:res.data.posts }) }catch (err){ dispatch({ type:PROFILE_GETUSER_FAIL, payload:{error:err.response && err.response.data.message? err.response.data.message : err.message} }) } }
profileReducer에 다음과 같이 post정보를 추가해준다.
export const getUserProfileReducer = (state={}, action)=>{ switch(action.type){ case PROFILE_GETUSER_REQUEST: return {loading:true}; case PROFILE_GETUSER_SUCCESS: return {loading:false, user:action.payload} case PROFILE_GETUSER_FAIL: return {loading:false, error:action.payload} case PROFILE_GETUSER_RESET: return {loading:true} case USER_FOLLOW_PROFILE: return {...state, user:action.payload} case USER_UNFOLLOW_PROFILE: return {...state, user:action.payload} case PROFILE_GETPOST_SUCCESS: return {...state, posts:action.payload} default: return state; } }
profile.js에서 디스패치 해서 가져온 user 정보의 post를 읽어서 post thumb요소를 이용해서 보여준다.
import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux'; import { getProfileUser } from '../_actions/profileActions'; import Avatar from '../component/Avatar' import { PROFILE_GETUSER_RESET, USER_UPDATE_PROFILE_RESET } from '../_constants/profileConstants'; import Loading from '../component/Loading'; import Alert from '../component/Alert' import EditProfile from '../component/EditProfile' import FollowBtn from '../component/FollowBtn'; import Followers from '../component/Followers'; import Followings from '../component/Followings'; import Post from '../component/Post'; import {Link} from 'react-router-dom' function Profile(props) { const userId = props.match.params.id; const userProfile = useSelector(state => state.userProfile) const {loading, error, user, posts} = userProfile useEffect(() => { if(!user || userId !== user._id || success){ dispatch({ type:PROFILE_GETUSER_RESET }) dispatch({ type:USER_UPDATE_PROFILE_RESET }) dispatch(getProfileUser(userId)) } }, [dispatch, userId, user, userInfo.token,success]) return ( { posts && posts.length ===0 ? <h2 className="text-center text-danger">NO POST</h2> : <> <div className="post_thumb"> { posts?.map(post=>( <Link key={post._id} to={`/post/${post._id}`}> <div className="post_thumb_display"> { post.images[0].config.url.match(/video/i) ? <video controls src={post.images[0].data} alt={post.images[0].data}></video> : <img src={post.images[0].data} alt={post.images[0].data}></img> } <div className="post_thumb_menu"> <i className="far fa-heart"></i> <i className="far fa-comment"></i> </div> </div> </Link> )) } </div> </> } </div> ) } export default Profile
post thumb.css
.post_thumb{ width: 100%; display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); justify-content: center; grid-gap: 10px; overflow: hidden; margin: 15px 0; } .post_thumb_display{ min-width: 300px; height: 300px; width: 100%; position: relative; cursor: pointer; overflow: hidden; } .post_thumb_display img, .post_thumb_display video{ width: 100%; height: 100%; display: block; object-fit: cover; } .post_thumb_display .post_thumb_menu{ position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #0008; display: flex; justify-content: center; align-items: center; opacity: 0; transition: 0.3s; } .post_thumb_display:hover .post_thumb_menu{ opacity: 1; } .post_thumb_display .post_thumb_menu i{ font-size: 1.8rem; color: white; margin: 0 25px; }
GLOBAL에도 추가해준다.
/* -------- PostThumb ---------- */ @import url("./post_thumb.css");
리덕스에도 잘 생겼다.
사진을 누르면 링크를 통해서 이동하도록 했으니,
App.js에 다음과 같이 추가해주자.
<Route exact path="/post/:id" component={Post}/>
pages>Post.js를 만들어준다.
import React from 'react' function Post(props) { const postId = props.match.params.id; return ( <div> Post </div> ) } export default Post
router를 만들어주자.
postRouter.get('/:id', auth, async(req, res)=>{ try{ const post = await Post.findById(req.params.id) .sort('-createdAt') .populate("user likes", "avatar username fullname followers following") .populate({ path:"comments", populate:{ path:"user likes", select:"-password" }, sort:'-createdAt' }) if(!post) return res.status(400).json({msg:"This post does not exist."}) res.json({post}) }catch(err){ return res.status(500).json({message:err.message}) } })
constant
export const GET_POST_REQUEST = 'GET_POST_REQUEST' export const GET_POST_SUCCESS = 'GET_POST_SUCCESS' export const GET_POST_FAIL = 'GET_POST_FAIL'
action
export const getPostDetail =(postId) => async(dispatch, getState)=>{ dispatch({ type:GET_POST_REQUEST, payload:{loading:true} }) const {userLogin: {userInfo}} = getState() try{ const res = await axios.get(`/api/post/${postId}`,{headers: {authorization : `Bearer ${userInfo.token}`}}) dispatch({ type:GET_POST_SUCCESS, payload:res.data.post }) }catch(error){ dispatch({ type:GET_POST_FAIL, payload: error.response && error.response.data.message ? error.response.data.message : error.message }) } }
reducer
export const postDetailReducer = (state={}, action)=>{ switch(action.type){ case GET_POST_REQUEST: return {loading:true, success:false} case GET_POST_SUCCESS: return {loading:false, success:true, postdetail:action.payload} case GET_POST_FAIL: return {loading:false, success:false, error:action.payload} default: return state } }
store.js
detailpost:postDetailReducer,
post.js
업데이트 되면 그대로 가져와서 새로바꿔진 데이터 가져오도록 설정한다.
import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { createPost, getHomePosts } from '../_actions/postActions' import { CREATE_POST_RESET, DELETE_POST_RESET, GET_POSTS_RESET, LIKE_POST_RESET, UNLIKE_POST_RESET, UPDATE_POST_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 const updatepost = useSelector(state => state.updatepost) const{success:updatesuccess} = updatepost const deletepost = useSelector(state => state.deletepost) const {success:deletesuccess} = deletepost const likepost = useSelector(state => state.likepost) const {success:likesuccess} = likepost const unlikepost = useSelector(state => state.unlikepost) const {success:unlikesuccess} = unlikepost useEffect(() => { if(success||updatesuccess ||deletesuccess ||unlikesuccess ||likesuccess){ dispatch({type:GET_POSTS_RESET}) dispatch({type:CREATE_POST_RESET}) dispatch({type:UPDATE_POST_RESET}) dispatch({type:DELETE_POST_RESET}) dispatch({type:LIKE_POST_RESET}) dispatch({type:UNLIKE_POST_RESET}) } dispatch(getHomePosts()) }, [dispatch, success, updatesuccess, deletesuccess, unlikesuccess, likesuccess]) return ( <div className="posts"> { posts && posts.map(post=>( <PostCard key={post._id} post={post}></PostCard> )) } </div> ) } export default Post
'NODE.JS' 카테고리의 다른 글
Social Media 만들기 - 17) share, suggestion 구현하기 (0) 2021.07.31 Social Media 만들기 - 16) Discover - loadmore 버튼 구현하기 (2) 2021.07.30 Social Media 만들기 - 15) create, like, update, delete comment 구현하기 (0) 2021.07.16 Social Media 만들기 - 14) like, unlike post 구현하기 (0) 2021.07.13 Social Media 만들기 - 13) update post , delete post, clipboard에 주소 복사 구현하기(Card Header) (0) 2021.07.12