ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Youtube 만들기 - 7) 비디오 디테일 페이지 만들기
    NODE.JS 2021. 5. 14. 18:43

     

    랜딩페이지에서 비디오 하나를 클릭하면 

    하나의 비디오에 해당하는 디테일 페이지를 생성하도록 만든다. 

    순서

     

    제일먼저 비디오 디테일 페이지를 만들어준다. 

    src>components>views>VideoDetailPage>VideoDetailPage.js를 생성해준다. 

     

     

    그리고 만든 페이지를 app.js에서 추가해준다. 

    링크는 개별의 videoId를 이용해서 들어오도록 설정하고 아무나 볼 수 있도록 null로 넣어준다. 

    import React, { Suspense } from 'react';
    import { Route, Switch } from "react-router-dom";
    import Auth from "../hoc/auth";
    // pages for this product
    import LandingPage from "./views/LandingPage/LandingPage.js";
    import LoginPage from "./views/LoginPage/LoginPage.js";
    import RegisterPage from "./views/RegisterPage/RegisterPage.js";
    import NavBar from "./views/NavBar/NavBar";
    import Footer from "./views/Footer/Footer"
    import VideoUploadPage from "./views/VideoUploadPage/VideoUploadPage";
    import VideoDetailPage from "./views/VideoDatailPage/VideoDetailPage";
    
    //null   Anyone Can go inside
    //true   only logged in user can go inside
    //false  logged in user can't go inside
    
    function App() {
      return (
        <Suspense fallback={(<div>Loading...</div>)}>
          <NavBar />
          <div style={{ paddingTop: '69px', minHeight: 'calc(100vh - 80px)' }}>
            <Switch>
              <Route exact path="/" component={Auth(LandingPage, null)} />
              <Route exact path="/login" component={Auth(LoginPage, false)} />
              <Route exact path="/register" component={Auth(RegisterPage, false)} />
              <Route exact path="/video/upload" component={Auth(VideoUploadPage, true)} />
              <Route exact path="/video/:videoId" component={Auth(VideoDetailPage, null)} />
            </Switch>
          </div>
          <Footer />
        </Suspense>
      );
    }
    
    export default App;
    

     

     

    비디오 디테일 페이지를 만드는데, 

    이 디테일 페이지는 어느정도 화면크기가 줄어들면 사이드의 동영상 리스트들이 화면 아래로 이동하는 모습을 나타낸다. 

     

    Row, Col을 이용해서 반응형으로 만든다. 

    (antd 이용)

    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) {
    
    
        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//좋아요와 싫어요 버튼 
                        >
                            <List.Item.Meta
                                avatar = {<Avatar src={VideoDetail.writer.image}/>}//유저의 이미지(populate해서 가능하다)
                                title = {VideoDetail.writer.name}
                                description = {VideoDetail.description}
                            />
    
    
                        </List.Item>
    
                        {/* Comments */}
    
                    </div>
                </Col>
                <Col lg = {6} xs={24}>
                    <SideVideo />
                </Col>
    
            </Row>
        )
    
    export default VideoDetailPage
    

     

     

    비디오 랜딩페이지에가서 랜딩페이지에서 동영상을 누르면 링크로 들어오도록 설정했었다. 

                    {/* 하나의 동영상에 해당하는 페이지로 이동 */}
                <a href={`/video/post/${video._id}`}>

     

     

    다시 디테일 페이지로 돌아와서 

    비디오에 대한 정보를 요청하는 axios를 만들어주자. 

    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/getDetailVideo', variable)
            .then(response =>{
                if(response.data.success){
                    setVideoDetail(response.data.videoDetail)
    
    
                }else{
                    alert('비디오 정보를 가져오길 실패했습니다.')
                }
            })
        }, [])

     

     

    video.js에서 라우트를 생성해준다. 

    아이디를 이용해서 맞는 비디오 디테일을 가져온다. 

    router.post('/getDetailVideo', (req, res)=>{
        Video.findOne({"_id":req.body.videoId}) //비디오를 찾아온다.
        .populate('writer')//populate를 해줘야 user관련 데이터를 가져올수 있다. 안하면 id만 가져온다. 
        .exec((err, videoDetail)=>{
            if(err)return res.status(400).send(err);
            res.status(200).json({success:true,videoDetail})
        })
    })

     

    여기서 비디오 writer가 있어야지만 코드르 렌더링 할 수 있도록 바꿔준다.

    (가져오지 않은 상태에서 먼저 실행될 수 있으므로)

     

    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){
        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 ={[<Subscribe userTo = {VideoDetail.writer._id} userFrom= {localStorage.getItem('userId')}/>]}//여기서 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
    

     

     

    이번에는 옆에 보이는 side 비디오를 생성해보자. 

    순서

     

     

    views>VideoDetailPage>Sections>SideVideo.js를 만들어준다. 

    왼쪽에는 이미지 오른쪽에는 정보가 올 수 있도록 만들 것이다. 

     

     

    우선, 하나의 카드에 적용되는 return을 만들어보자. 

    import React ,{useEffect, useState} from 'react'
    import Axios from 'axios';
    
    function SideVideo() {
    
        return (
    
    
                <div key = {index} style={{display: 'flex', marginBottom:'1rem', padding:'0 2rem'}}>
                	//왼쪽 이미지 가져오는 부분
            		<div style ={{width:'40%', marginRight:'1rem'}}>
                    <a href>
                        <img style = {{ width:'100%', height:'100%'}}src={`http://localhost:5000/${video.thumbnail}`} alt="thumbnail"/>
                    </a>
            		</div>
                    //오른쪽 동영상 정보 가져오는 부분
                    <div style = {{width:'50%'}}>
                        <a href style={{color:'gray'}}>
                            <span style={{fontSize:'1rem', color:'black'}}>{videoTitle}</span>
                            <span>{video.writer.name}</span><br />
                            <span>{video.views} views </span><br />		
                            <span>{minutes} : {seconds}</span><br />
                        </a>
                    </div>
            </React.Fragment>
    
            
        )
    }
    
    export default SideVideo
    

     

    db에서 데이터 베이스를 가져오자 .

     

    useEffect 를 사용할 것이다. 

    랜딩페이지에서 동영상을 불러올때 모든 동영상을 불러오는 api를 이미 작성해뒀다. 

    이를 이용해서 동영상을 나타낼 것이다. 

    function SideVideo() {
    
    
        const [sideVideos, setsideVideos] = useState([])//state이름만 바꿔서 가져와서 저장한다.
        useEffect(() => {
            Axios.get('/api/video/getVideos')
            .then(response =>{
                if(response.data.success){
                    setsideVideos(response.data.videos)
    
                }
                else{ 
                    alert('비디오 가져오기를 실패 했습니다.')
                }
            })
    
        }, [])

     

     

    우리는 하나의 카드가 아닌 여러개의 동영상에 대해서 특징을 적용하므로 이를 map을 이용해서 나타나도록 지정해보자. 

    import React ,{useEffect, useState} from 'react'
    import Axios from 'axios';
    
    function SideVideo() {
    
    
        const [sideVideos, setsideVideos] = useState([])
        useEffect(() => {
            Axios.get('/api/video/getVideos')
            .then(response =>{
                if(response.data.success){
                    setsideVideos(response.data.videos)
    
                }
                else{ 
                    alert('비디오 가져오기를 실패 했습니다.')
                }
            })
    
        }, [])
        const renderSideVideo = sideVideos.map((video, index)=>{
            var minutes = Math.floor(video.duration/60);
            var seconds = Math.floor((video.duration - minutes*60));
            
    		//map은 index를 키로 넣어줘야 에러가 생기지 않는다. 
            return <div key = {index} style={{display: 'flex', marginBottom:'1rem', padding:'0 2rem'}}>
            <div style ={{width:'40%', marginRight:'1rem'}}>
                <a href>
                    <img style = {{ width:'100%', height:'100%'}}src={`http://localhost:5000/${video.thumbnail}`} alt="thumbnail"/>
                
                </a>
    
            </div>
            <div style = {{width:'50%'}}>
                <a href style={{color:'gray'}}>
                    <span style={{fontSize:'1rem', color:'black'}}>{videoTitle}</span>
                    <span>{video.writer.name}</span><br />
                    <span>{video.views} views </span><br />
                    <span>{minutes} : {seconds}</span><br />
                </a>
            </div>
        </div>
    
        })
        return (
    
    
            <React.Fragment>
                <div style={{marginTop:'3rem'}}></div>
                {renderSideVideo}
            </React.Fragment>
    
            
        )
    }
    
    export default SideVideo

     

     

     

    완성된 화면

     

     

     

     

Designed by Tistory.