본문 바로가기
REACT

[클론코딩] 따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기 #8. 비디오 디테일 페이지 만들기

by kmyobin 2022. 8. 24.


저번에는 landingpage에 비디오들을 출력했었다

 

1. 비어있는 비디오 디테일 페이지 생성

client-src-components-view에 VideoDetailPage 폴더를 만든다

그리고 그 안에 VideoDetailPage.js 파일을 만든다

 

 

 

2. 비디오 디테일 페이지를 위한 Route 만들기

새로운 페이지를 만들었으므로 App.js에 가서 Route를 해준다

일단 VideoDetailPage를 가져와준다

import VideoDetailPage from './views/VideoDetailPage/VideoDetailPage';

 

Route를 해주는데, 주소는 videoId로 설정한다

또한 로그인하지 않은 사람들도 접속할 수 있게 null로 설정한다

          <Route exact path="/video/:videoId" component={Auth(VideoDetailPage, null)} />

 

 

landingPage에서 동영상을 클릭하면 VideoDetailPage로 넘어가야 하므로 LandingPage.js도 수정해야함!

renderCards에서 <a> 태그를 추가한다

const renderCards = Video.map((video, index)=> {

        // duration은 모두 second로 표시되어있으므로 가공함
        var minutes = Math.floor(video.duration/60)
        var seconds = Math.floor((video.duration - minutes * 60))

        return <Col key={index} lg={6} md={8} xs={24}>
        <a href={`/video/post/${video._id}`}> 
        {/*새롭게 추가된 부분*/}
            <a href={`/video/${video._id}`}>
                <div style={{position: 'relative'}}>
                    <img style={{width:'100%'}} src={`http://localhost:5000/${video.thumbnail}`} alt="thumbnail"/>
                    <div className='duration'>
                        <span>{minutes} : {seconds}</span>
                    </div>
                </div>
            </a>
        </a>
            
        <br />
        <Meta
            avatar={
                <Avatar src={video.writer.image}/>    
            }
            title={video.title}
            description=""
        />

        <span>{video.writer.name}</span> <br />
        <span style={{marginLeft:'3rem'}}> {video.views} views </span> - <span>{moment(video.createdAt).format("MMM Do YY")}</span>

        </Col> 
    })

 

 

 

 

3. 비디오 디테일 페이지 template 만들기

video가 나올 곳의 size는 18. side video는 6으로 설정한다

<VideoDetailPage.js>

import React from 'react'
import {Row, Col, List} from 'antd';

// video는 18, side는 6

function VideoDetailPage() {
  return (
    <Row gutter={[16,16]}>
      <Col lg={18} xs={24}>

        <div style={{width: '100%', padding: '3rem 4rem'}}>
          <video style={{width:'100%'}} src controls />

          <List.Item
            actions
          >
            <List.Item.Meta
              avatar
              title
              description
            /> 
          </List.Item>

          {/*Comments*/}
        </div>
      </Col>
      <Col lg={6} xs={24}>
        Side Videos
      </Col>
    </Row>
  )
}

export default VideoDetailPage

 

 

그래서 landingPage에서 동영상을 클릭하면

전체화면

 

반절 화면

이렇게 확인할 수 있다

아직 data를 넣어주지 않아서 video가 빈 화면으로 보인다

 

 

 

4. mongoDB에서 비디오 데이터 가져오기

VideoDetailPage.js에서 useEffect() 함수를 사용하여 비디오 데이터를 가져올 것이다

video 정보를 가져오는 데에 videoId가 필요하므로 client에서 server로 videoId를 넘겨주는 작업을 해야한다

    const videoId = props.match.params.videoId // App.js에서 설정함
    const variable = {videoId : videoId}

    const [VideoDetail, setVideoDetail] = useState([]) // array
  
  useEffect(() => {    
    Axios.post('/api/video/getVideoDetail', variable)
    .then(response => {
      if(response.data.success){
        setVideoDetail(response.data.videoDetail)
      }else{
        alert('비디오 정보를 가져오는 데에 실패했습니다.')
      }
    })
  },[])

App.js에서 주소 형식이 '/:videoId' 였으므로 props를 이용하여 videoId를 가져왔다

이것을 server에 넘겨서 '이 id를 가진 video 데려와!' 하는 것임

 

 

video.js로 가서 데이터를 가져올 것이다

router.post('/getVideoDetail', (req, res) => {
  Video.findOne({"_id": req.body.videoId}) // client에서 받은 Id로 찾음
  .populate('writer') // writer의 모든 정보를 가져옴
  .exec((err, videoDetail) => {
    if(err) return res.status(400).send(err)
    return res.status(200).json({success: true, videoDetail})
  })
})

populate 안 쓰면 id만 가져올 수 있으므로 적어준다

 

 

 

5. 가져온 데이터 스크린에 출력하기

<VideoDetailPage.js>

import React, { useEffect, useState } from 'react'
import {Row, Col, List, Avatar} from 'antd';
import Axios from 'axios';


// video는 18, side는 6

function VideoDetailPage(props) {
    const videoId = props.match.params.videoId // App.js에서 설정함
    const variable = {videoId : videoId}

    const [VideoDetail, setVideoDetail] = useState([]) // array
  
  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 style={{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}/>}
                title={VideoDetail.writer.name}
                description={VideoDetail.description}
              /> 
            </List.Item>

            {/*Comments*/}
          </div>
        </Col>
        <Col lg={6} xs={24}>
          Side Videos
        </Col>
      </Row>
    )
  } else{
    return (
      <div>... loading</div>
    )
  }


  
}

export default VideoDetailPage

return에서 if else 구문을 쓰지 않으면 렌더링이 되기 전에 writer의 image, name의 정보를 얻어오려 하여 오류가 난다

 

 

다 완료하면

이렇게 잘 뜬다!

쇼츠를 다운받아서 그런지 비율이 이상해서 너무 크다 ㅜㅜ

댓글