ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Social Media 만들기 - 11)createPost 작성하기
    NODE.JS 2021. 7. 11. 00:45

    post와 comment 모델을 만들어주자. 

    const mongoose = require('mongoose')
    
    const postSchema = new mongoose.Schema({
        content:String,
        images:{
            type:Array,
            required:true
        },
        likes:{
            type:mongoose.Types.ObjectId,
            ref:'User'
        },
        comments:{
            type:mongoose.Types.ObjectId,
            ref:'comment'
        },
        user:{
            type:mongoose.Types.ObjectId,
            ref:'user'
        }
    },{timestamps:true})
    
    
    const Post = mongoose.model('Post', postSchema)
    module.exports  = {Post}
    const mongoose = require('mongoose')
    
    const commentSchema = new mongoose.Schema({
        content:{
            type:String,
            required:true
        },
        tag:Object,
        reply:mongoose.Types.ObjectId,
        likes:[{
            type:mongoose.Types.ObjectId,
            ref:'User'
        }],
        user:{
            type:mongoose.Types.ObjectId,
            ref:'User'
        },
        postId:mongoose.Types.ObjectId,
        postUserId:mongoose.Types.ObjectId
    },{timestamps:true})
    
    
    const Comment = mongoose.model('Comment', commentSchema)
    module.exports = {Comment}

     

     

    submit 버튼을 클릭하면 사진과 데이터를 보내도록 설정해주자. 

    statusModal.js

     

    import React, { useRef, useState } from 'react'
    import { useDispatch, useSelector } from 'react-redux'
    import { createPost } from '../_actions/postActions'
    import { ALERT, STATUS } from '../_constants/globalConstants'
    import Alert from './Alert'
    
    function StatusModal() {
    
        const userLogin = useSelector(state => state.userLogin)
        const {userInfo} = userLogin
        const theme = useSelector(state => state.theme)
    
        const [content, setcontent] = useState('')
        const dispatch = useDispatch()
    
        const [Images, setImages] = useState([])
        const [stream, setStream] = useState(false)
        const [tracks, setTracks] = useState('')
    
        const videoRef = useRef()
        const canvasRef = useRef()
        
    
        const handleChangeImages=(e)=>{
            const files = [...e.target.files]
            let newImages= []
            let err = ''
    
            files.forEach(file=>{
                if(!file) return err = "File doesn't exist."
                if(file.type !== 'image/jpeg' && file.type !=='image/png'){
                    return err = "Image format is incorrect."
                }
                return newImages.push(file)
            })
    
            if(err) dispatch({type:ALERT, payload:{error:err}})
    
            setImages([...Images, ...newImages])
            
        }
    
        const deleteImage=(index)=>{
            const newArr = [...Images]
            newArr.splice(index, 1)
            setImages(newArr)
        }
    
        const handleStream= () =>{
            setStream(true)
            // navigator.getWebcam = (navigator.getUserMedia || navigator.webKitGetUserMedia || navigator.moxGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
            if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia){
                navigator.mediaDevices.getUserMedia({video:true})
                                        .then(mediastream=>{
                                            videoRef.current.srcObject = mediastream
                                            videoRef.current.play()
                                            const track = mediastream.getTracks()
                                            setTracks(track[0])
                                        }).catch(err=>console.log(err))
            }
            // else{
            //     navigator.getWebcam({video:true},
            //                     function(stream){
            //                         videoRef.current.srcObject = stream
            //                         videoRef.current.play()
            //                         const track = stream.getTracks()
            //                         setTracks(track[0])
            //                     }, function(){
            //                         console.log("Web cam is not accesible.")
            //                     })
            // }
        }
    
        const handleCapture= () =>{
            const width = videoRef.current.clientWidth;
            const height = videoRef.current.clientHeight;
    
            canvasRef.current.setAttribute("width", width)
            canvasRef.current.setAttribute("height", height)
    
            const ctx = canvasRef.current.getContext('2d')
            ctx.drawImage(videoRef.current, 0, 0, width, height)
            let URL = canvasRef.current.toDataURL()
            setImages([...Images, URL])
        }
    
        const handleStopStream= () =>{
            tracks.stop()
            setStream(false)
        }
    
        const handleSubmit= (e) =>{
            e.preventDefault()
            if(Images.length===0){
                dispatch({type:ALERT, payload:{error:"Please add your photo."}})
            }
    
            dispatch(createPost({content, Images}))
            setcontent('')
            setImages([])
            if(tracks) tracks.stop()
            dispatch({type:STATUS, payload:false})
            
        }
    
        return (
            <div className="status_modal">
                <form onSubmit={handleSubmit}>
                    <div className="status_header">
                        <h5 className="m-0">Create Post</h5>
                        <span onClick={()=>dispatch({type:STATUS, payload:false})}>&times;</span>
                    </div>
    
                    <div className="status_body">
                        <textarea name="content" value = {content} placeholder={`${userInfo.user.username}, what are you thinking?`} onChange={(e)=>setcontent(e.target.value)} />
    
                        <div className="show_images">
                            {
                                Images && Images.map((image, index)=>(
                                    <div key={index} id="file_img">
                                        <img src={image.camera? image.camera : URL.createObjectURL(image)} 
                                        alt="images" className="img-thumbnail rounded" style={{filter:theme? 'invert(1)':'inver(0)'}} />
                                        <span onClick={()=>deleteImage(index)}>&times;</span>
                                    </div>
                                ))
                            }
                        </div>
    
                        {
                            stream &&
                            <div className="stream position-relative">
                                <video autoPlay muted  style={{filter:theme? 'invert(1)':'inver(0)'}} ref={videoRef} width='100%' height='100%'></video>
                                <span onClick={handleStopStream}>&times;</span>
                                <canvas ref={canvasRef} style={{display:'none'}}></canvas>
                            </div>
    
                        }
                        <div className="input_images">
                            {
                                stream
                                ? <i className="fas fa-camera" onClick={handleCapture}></i>
                                : <>
                                    <i className="fas fa-camera" onClick={handleStream}></i>
                                    <div className="file_upload">
                                        <i className="fas fa-image"></i>
                                        <input type="file" name="file" id="file_up" multiple accept="image/*" onChange={handleChangeImages}/>
                                    </div>
                                </>
                            }
                        </div>
                    </div>
    
                    <div className="statue_footer">
                    <button className="btn btn-secondary w-100" type="submit" type="submit">Post</button>
                    </div>
    
                </form>
            </div>
        )
    }
    
    export default StatusModal

     

     

    사진을 보내면 postuploads폴더에 사진이 저장되도록 router설정해주자. 

    Router>postUploadRouter.js

    const multer = require('multer')
    const express = require('express');
    const { auth } = require('../middleware/auth');
    
    const postUploadRouter = express.Router();
    
    const storage = multer.diskStorage({
        destination(req,file,cb){
            cb(null, 'postuploads/')
        },
        filename(req,file,cb){
            cb(null, `${Date.now()}.jpg`);
        }
    })
    
    const postupload = multer({storage})
    
    postUploadRouter.post('/',auth, postupload.single('image'), (req,res)=>{
        res.send(`/${req.file.path}`)
    })
    
    module.exports = postUploadRouter

    최상위에 postuploads/폴더를 만들어주고 안에 file.txt를 만들어준다. 

    server.js에 이미지 저장 경로를 설정해주자. 

    const __variableOfChoice = path.resolve();
    app.use('/profileuploads', express.static(path.join(__variableOfChoice, '/profileuploads')))
    app.use('/postuploads', express.static(path.join(__variableOfChoice, '/postuploads')))
    
    
    app.use('/api/postuploads', require('./routes/postUploadRouter'));

     

     

    이제 postcreate하는 라우터를 만들어주자. 

    postRouter.js

    const express = require('express')
    const {Post} = require('../models/Post')
    const {auth} = require('../middleware/auth')
    
    
    const postRouter = express.Router()
    
    postRouter.post('/', auth,  async(req, res)=>{
        // console.log(req.body)
        try{
            const newPost = new Post({'content':req.body.content, 'images':req.body.images,'user':req.body.userId})
    
            await newPost.save()
    
            res.send({newPost})
    
        }catch(err){
            return res.status(500).json({message:err.message})
        }
    })
    
    module.exports = postRouter

     

     

    Constant를 만들어주자.

    postConstants.js

    export const CREATE_POST_REQUEST = 'CREATE_POST_REQUEST'
    export const CREATE_POST_SUCCESS = 'CREATE_POST_SUCCESS'
    export const CREATE_POST_FAIL = 'CREATE_POST_FAIL'
    export const CREATE_POST_RESET = 'CREATE_POST_RESET'

     

    postActions.js

    여기서 각이미지마다 form데이터를 돌아가면서 설정하고 저장하고, 

    이미지를 폴더에 저장하는 라우트를 보내준다. 

    import axios from "axios"
    import { CREATE_POST_FAIL, CREATE_POST_REQUEST, CREATE_POST_SUCCESS } from "../_constants/postConstants"
    
    export const createPost = ({content, Images})=> async(dispatch, getState)=>{
        const {userLogin:{userInfo}} = getState()
        dispatch({
            type:CREATE_POST_REQUEST,
            payload:{loading:true}
        })
        try{
            let imgArr = [];
            if(Images.length>0){
                for(const item of Images){
                    const bodyFormData = new FormData()
                    if(item.camera){
                        bodyFormData.append("image", item.camera)
                    }else{
                        bodyFormData.append("image", item)
                    }
                    const data = await axios.post('/api/postuploads', bodyFormData,{
                        headers:{'Content-Type' : 'multipart/form-data', authorization:`Bearer ${userInfo.token}`}
                    })
                    imgArr.push(data)
                }
            }
            const res = await axios.post('/api/post', {content, images:imgArr, userId:userInfo.user._id,user:userInfo.user},{
                headers:{authorization:`Bearer ${userInfo.token}`}
            })
            dispatch({
                type:CREATE_POST_SUCCESS,
                payload:res.data.newPost
            })
    
        }catch(error){
            dispatch({
                type:CREATE_POST_FAIL,
                payload:                
                error.response && error.response.data.message
                ? error.response.data.message
                : error.message
            })
        }
    
    }

     

    받아온 newpost 데이터를 스테잇에 저장해준다. (reducer 통해서)

    postReducers.js

    import { CREATE_POST_FAIL, CREATE_POST_REQUEST, CREATE_POST_RESET, CREATE_POST_SUCCESS } from "../_constants/postConstants";
    
    
    export const postCreateReducer = (state={}, action)=>{
        switch(action.type){
            case CREATE_POST_REQUEST:
                return {loading:true, success:false}
            case CREATE_POST_SUCCESS:
                return {loading:false, newpost:action.payload, success:true}
            case CREATE_POST_FAIL:
                return {loading:false, error:action.payload,success:false}
            case CREATE_POST_RESET:
                return {}
            default:
                return state
        }
    }

     

    store.js

        createpost:postCreateReducer,

    잘생성된다.

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.