Youtube 만들기 - 8) 구독기능 만들기
이번에는 동영상의 구독기능을 만들 것이다.
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
완성된 화면을 보면,