ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Youtube 만들기 - 8) 구독기능 만들기
    NODE.JS 2021. 5. 14. 19:15

    이번에는 동영상의 구독기능을 만들 것이다. 

     

    순서

    model은 구독 받는 사람과 구독하는 사람으로 나누어 나타낸다. 

    구독하고 있으면 subscribed (회색)

    구독 하지 않고 있으면 subscribe(빨간색)

    버튼으로 나오도록 지정할 것이다. 

     

     

    먼저 server>models>Subscribers.js파일을 만들어 준다. 

    const mongoose = require('mongoose');
    const Schema = mongoose.Schema;
    
    const subscriberSchema = mongoose.Schema({
        userTo: {
            type:Schema.Types.ObjectId,
            ref:'User'
        },
        userFrom:{
            type:Schema.Types.ObjectId,
            ref:'User'
        }
    
    }, {timestamps:true})//업데이트 시간 표시
    
    const Subscriber = mongoose.model('Subscriber', subscriberSchema)
    module.exports = {Subscriber }

     

     

    이번엔 subscribe 버튼을 만들자. 

    videodetail페이지의 actions에 들어가야 하는데, 

    여기에 넣으면 디테일페이지의 양이 너무 길어지니까 subscribe기능을 구현하는 새로운 페이지를 이용해보자. 

    views>videoDetailPage>Sections>Subscribe.js파일을 새로 하나 만들어 준다. 

     

     

    우선 videoDetailPage안에 다음과 같이 Subscribe.js파일을 넣어준다. 

    import Subscribe from './Sections/Subscribe';
    <List.Item
                            actions ={[<Subscribe/>]}
                            <List.Item.Meta
                                avatar = {<Avatar src={VideoDetail.writer.image}/>}
                                title = {VideoDetail.writer.name}
                                description = {VideoDetail.description}
                            />
    
    
                        </List.Item>

     

    Subscribe.js

    import Axios from 'axios'
    import { response } from 'express'
    import React, {useEffect, useState} from 'react'
    
    function Subscribe(props) {
    
       
        return (
    
            <div>
                <button
                    style={{backgroundColor:#CC0000, borderRadius:'4px', color:'white', padding:'10px 16px', fontWeight:'500', fontSize:'1rem', textTransform:'uppercase'}}   
                    onClick={onSubscritbe}
                >
                0 Subscribe
                </button>
            </div>
        )
    }
    
    export default Subscribe
    

     

     

    db에서 구독자 수 정보를 가져와서 화면에 나타내도록 해보자. 

     

    function Subscribe(props) {
    
        const [SubscribeNumber, setSubscribeNumber] = useState(0)
        const [Subscribed, setSubscribed] = useState(false)
        useEffect(() => {
            
            let variable = {userTo: props.userTo}
            Axios.post('/api/subscribe/subscribeNumber', variable)
            .then(response=>{
                if(response.data.success){
                    setSubscribeNumber(response.data.SubscribeNumber)
                }else{
                    alert('구독자 수 정보를 가져오는데 실패했습니다.')
                }
            })
           
    
        }, [])

     

     

    여기서 props.userTo는 비디오 디테일 페이지에서 subscribe에서 보내줌으로서 설정할 수 있다. 

    actions ={[<Subscribe userTo = {VideoDetail.writer._id} userFrom= {localStorage.getItem('userId')}/>]}//여기서 userTO 보내주면 Subscribe에서 prop으로 받아서 사용가능하다. 

     

    axios를 작성했으니 router를 구현해줘야 한다. 

     

    server의 index.js에 다음과 같은 구문을 추가해주고,

    app.use('/api/subscribe', require('./routes/subscribe'));
    

     

    routes>subscribe.js파일을 새로 생성해서 작성해준다. 

    const express = require('express');
    const router = express.Router();
    const {Subscriber } = require("../models/Subscriber");
    const { route } = require('./users');
    
    
    //=================================
    //             Subscribe
    //=================================
    
    route.post("/subscribeNumber", (req, res)=>{
        Subscriber.find({'userTo': req.body.userTo})
        .exec((err, subscribe)=>{
            if(err){
                return res.status(400).send(err);
            }
            return res.status(200).json({success: true, subscribeNumger :subscribe.length})//몇명이 구독하고 있는지 길이를 센다.
        }) 
    
    });
    
    
    
    module.exports = router;
    

     

     

    이번엔 사용자가 구독했는지 구독여부 정보를 가져오자. 

    userFrom은 로그인할때 유저의 아이디만 localStorage에 저장해두므로 그것을 가져와서 조회한다. 

    import Axios from 'axios'
    import { response } from 'express'
    import React, {useEffect, useState} from 'react'
    
    function Subscribe(props) {
    
        const [SubscribeNumber, setSubscribeNumber] = useState(0)
        const [Subscribed, setSubscribed] = useState(false)
        useEffect(() => {
    
            let subscribedVariable = {userTo: props.userTo, userFrom:localStorage.getItem('userId')}
    
            Axios.post('/api/subscribe/subscribed',subscribedVariable)
            .then(response=>{
                if(response.data.success){
                    setSubscribed(response.data.subscribed)
                }else{
                    alert('정보를 가져오지 못했습니다.')
                }
            })
    
        }, [])
    

    route에 가서 다시 구독여부 정보를 가져오는 라우터를 작성해준다. 

    subscriber.find의 결과가 1만 되어도 이미 구독하고 있다는 뜻이 된다. 

    route.post("/subscribed", (req, res)=>{
        Subscriber.find({'userTo': req.body.userTo, 'userFrom': req.body.userFrom})
        .exec((err, subscribe)=>{
            if(err){
                return res.status(400).send(err);
            }
            let result = false
            if(subscribe.length!==0){
                result = true
            }
            return res.status(200).json({success: true, subscribed : result})
        }) 
    
    });

     

     

    구독 여부에 따라서 나타나는 버튼의 모양도 바뀌도록 설정해준다. (Subscribe.js)

        return (
    
            <div>
                <button
                    style={{backgroundColor:`${Subscribed? '#AAAAAA' : '#CC0000'}`, borderRadius:'4px', color:'white', padding:'10px 16px', fontWeight:'500', fontSize:'1rem', textTransform:'uppercase'}}   
                    onClick={onSubscritbe}
                >
                    {SubscribeNumber} {Subscribed ? 'Subscribed' : 'Subscribe'}
                </button>
            </div>
        )
    }

     

     

    완성된 화면

     

    이번에는 버튼을 클릭하므로서 생기는 구독기능과 구독취소기능을 만들 것이다. 

     

    순서

    우선 Subscribe.js에 onSubscribe 함수를 만들어 준다. 

    userTo와 userFrom은 비디오 디테일 페이지에서 props로 보내준 것을 가지고 온 것이다. 

    actions ={[<Subscribe userTo = {VideoDetail.writer._id} userFrom= {localStorage.getItem('userId')}/>]}//여기서 userTO 보내주면 Subscribe에서 prop으로 받아서 사용가능하다. 
    
    const onSubscribe = ()=>{
    
            let subscribedVariable = {
                userTo: props.userTo,
                userFrom:props.userFrom
            }
            // 이미 구독중이라면
            if(Subscribed){
                    Axios.post('/api/subscribe/unssubcribe', subscribedVariable)
                    .then(response =>{
                        if(response.data.success){
                            setSubscribeNumber(SubscribeNumber-1)
                            setSubscribed(!Subscribed)
    
                        }else{
                            alert('구독을 취소하는데에 실패했습니다.')
                        }
    
                    })
                
            }
    
            // 아직 구독중이 아니라면
            else{
                Axios.post('/api/subscribe/subscribe', subscribedVariable)
                .then(response =>{
                    if(response.data.success){
                        setSubscribeNumber(SubscribeNumber+1)
                        setSubscribed(!Subscribed)
    
                    }else{
                        alert('구독하는데에 실패했습니다.')
                    }
    
                })
    
            }
        }

     

     

    axios를 작성해 주었으니 router도 작성해준다. 

    //구독취소 기능
    route.post("/unsubscribe", (req, res)=>{ 
        Subscriber.findOneAndDelete({'userTo': req.body.userTo, 'userFrom': req.body.userFrom})
        .exec((err, doc)=>{
            if(err){
                return res.status(400).json({success:false, err});
            }
    
            return res.status(200).json({success: true, doc})
        }) 
    
    });
    
    //구독기능
    route.post("/subscribe", (req, res)=>{
    
    		//새로운 구독자를 생성(userTo, userFrom 저장)
        const subscribe = new Subscriber(req.body)
        	//db에 저장
        subscribe.save((err, doc)=>{
            if(err){
                return res.status(400).json({success:false, err});
            }
            return res.status(200).json({success: true})
    
    
        })
    
    
    });

     

     

    추가로 만약 동영상 올린사람이 지금 로그인 된 사람과 같은 사람이라면 구독버튼이 나타나면 안되므로, 

    비디오 디테일 페이지를 다음과 같이 수정해준다. 

    import React, {useEffect, useState}from 'react'
    import {Row, Col, List, Avatar} from 'antd';
    import Axios from 'axios';
    import {response} from 'express';
    import SideVideo from './Sections/SideVideo';
    import Subscribe from './Sections/Subscribe';
    
    function VideoDetailPage(props) {
    
        const videoId =props.match.params.videoId//app.js에서 :videoId 보냈기 때문에 가져올 수 있다. 
        const variable = {videoId: videoId}
    
        const [VideoDetail, setVideoDetail] = useState([])
        useEffect(() => {
            Axios.post('/api/video/getVideoDetail', variable)
            .then(response =>{
                if(response.data.success){
                    setVideoDetail(response.data.videoDetail)
    
    
                }else{
                    alert('비디오 정보를 가져오길 실패했습니다.')
                }
            })
        }, [])
    
        if(VideoDetail.writer){
    
    		//영상주인과 구독하려는 사람이 다를때만 subscribeButton 설정 
            const subscribeButton = VideoDetail.writer._id !==localStorage.getItem('userId') &&<Subscribe userTo = {VideoDetail.writer._id} userFrom= {localStorage.getItem('userId')}/>
        return (
            <Row gutter={[16, 16]}>
                <Col lg = {18} xs = {24}>
    
                    <div tyle = {{ width:'100%', padding = '3rem 4rem' }}>
                        <video style = {{width:'100%' }}src={`http:/localhost:5000/${VideoDetail.filePath}`} controls />
                        <List.Item
                            actions ={[subscribeButton]}//여기서 userTO 보내주면 Subscribe에서 prop으로 받아서 사용가능하다. 
                        >
                            <List.Item.Meta
                                avatar = {<Avatar src={VideoDetail.writer.image}/>}
                                title = {VideoDetail.writer.name}
                                description = {VideoDetail.description}
                            />
    
    
                        </List.Item>
    
                        {/* Comments */}
    
                    </div>
                </Col>
                <Col lg = {6} xs={24}>
                    <SideVideo />
                </Col>
    
            </Row>
        )
        }
        else {
            return (
                <div>...loading</div>
            )
        }
    }
    
    export default VideoDetailPage
    

     

     

    완성된 화면을 보면, 

     

    구독하지 않은 상태

     

    구독한 상태

     

    구독을 다시 시작한 상태

     

Designed by Tistory.