Boiler Plate (1~3강)

설치

  1. 더 빠르게 개발을 완성하기 위해서
  2. 다운로드 (https://github.com/jaewonhimnae/ boilerplate-mern-stack)
  3. npm install로 dependencies 다운받기

MongoDB

  1. 몽고 db 회원가입
  2. 몽고 db 공홈에서 클러스터 만들기
  3. 유저 만들기
  4. 몽고 db 연결 코드 작성

실행

npm run dev 하면 실행된다!

안되는 경우 5000 또는 3000번 포트로 다른 서버가 열려있는지 확인해볼것

비디오 업로드 폼 만들기 (4~5강)

  1. Upload Page 만들기

  2. Upload Page Route 만들기

    <Route exact path="/video/upload" component={Auth(VideoUploadPage, true)} />
    
  3. Upload Page Header Tab 만들기

    <Menu.Item key="upload">
    	<a href="/video/upload">video</a>
    </Menu.Item>
    
  4. Form Template 만들기

    <div style={{ maxWidth: '700px', margin: '2rem auto' }}>
        <div style={{ textAlign: 'center', marginBottom: '2rem' }}>
            <Title level={2} > Upload Video</Title>
        </div>
    
        <Form onSubmit={onSubmit}>
    		    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
    						{/* Drop zone */}
    
    						{/* Thumbnail */}
    		        <div>
    		            <img src alt/>
                </div>
            </div>
    
            <br />
            <br />
            <label>Title</label>
            <Input
                onChange
                value
            />
            <br />
            <br />
            <label>Description</label>
            <TextArea
                onChange
                value
            />
            <br />
            <br />
    
            <select onChange>
    		        <option key value></option>
            </select>
            <br />
            <br />
    
            <select onChange>
    		        <option key value></option>
            </select>
            <br /><br />
    
            <Button type="primary" size="large" onClick>
                Submit
            </Button>
    
        </Form>
    </div>
    
  5. 파일을 올리는 Template 만들기 위해 Drop-zone 다운받기

    <Dropzone
    onDrop
    multiple
    maxSize
    >
    {({ getRootProps, getInputProps }) => (
      <div style={{ width: '300px', height: '240px', border: '1px solid lightgray', alignItems: 'center', justifyContent: 'center' }} {...getRootProps()}>
          <input {...getInputProps()} />
          <Icon type="plus" style={{ fontSize: '3rem' }} />
      </div>
    )}
    </Dropzone>
    
  6. onChange func 만들기

    const [VideoTitle, setVideioTitle] = useState("");
    const [Description, setDescription] = useState("");
    const [private, setPrivate] = useState(0)
    const [Categories, setCategories] = useState("Film & Animation")
    
    const PrivateOption = [
        { value: 0, label: 'Private' },
        { value: 1, label: 'Public' }
    ]
    
    {PrivateOption .map((item, index) => (
        <option key={index} value={item.value}>{item.label}</option>
    ))}
    
    const onTitleChange = (event) => {
    		setTitle(event.currentTarget.value)
    }
    
    onChange={handleChangeTitle}
    value={title}
    

    디테일 비디오 페이지에 Side 비디오 생성

    1. Side Video 부분 Layout template 만들기

      • SideVideo라는 컴포넌트를 따로 만든다.
    2. 한개의 카드 template 만들기

      <div key={index} style={{ display: 'flex', marginTop: '1rem', padding: '0 2rem' }}>
      	<div style={{ width: '40%', marginRight: '1rem' }}>
      		<a href={`/video/${video._id}`} style={{ color: 'gray' }}>
      			<img style={{ width: '100%', height: '100%' }} src={`http://localhost:5000/${video.thumbnail}`} alt="thumbnail" />
      		</a>
      	</div>
      
      	<div style={{ width: '50%' }}>
      		<a href={`/video/${video._id}`} style={{ color: 'gray' }}>
      			<span style={{ fontSize: '1rem', color: 'black' }}>{video.title}  </span><br />
      			<span>{video.writer.name}</span><br />
      			<span>{video.views}</span><br />
      			<span>{minutes} : {seconds}</span><br />
      		</a>
      	</div>
      </div>
      
      • index를 key로 넣어줘야 에러가 나지 않는다.
    3. DB에서 모든 비디오 데이터를 불러오기

      • 랜딩 페이지에서 썼던것 그대로 활용
    4. 불러온 데이터 화면에 출력하기

      const renderSideVideo = sideVideos.map((video, index) => {
      	//여기에 template을 넣어서 출력	
      }
      

    구독기능

    1. Subscriber Model 만들기

      • userTo, userFrom 로 구독자수를 센다.
      const subscriberSchema = mongoose.Schema({
      	userTo: {
      		type: Schema.Types.ObjectId,
      		ref: 'User'
      	},
      	userFrom: {
      		type: Schema.Types.ObjectId,
      		ref: 'User'
      	}
      
      }, { timestamps: true })
      
    2. Subscribe Button UI 만들기

      • 구독 컴포넌트를 따로 만든다.
      <div>
      			<button
      				style={{
      					backgroundColor: '#CC0000',
      					borderRadius: '4px', color: 'white',
      					padding: '10px 16px', fontWeight: '500', fontSize: '1rem', textTransform: 'uppercase'
      				}}
      				onClick>
      				0 Subscribe
      			</button>
      		</div>
      
    3. 데이터베이스에서 얼마나 많은 사람이 비디오 업로드 한 유저를 구독하는지 정보 가져오기

      • 디테일 페이지에서 actions={[<Subscribe userTo={video.writer._id} />]} 이렇게 넘겨주면
      • props.userTo로 정보를 받아올수있다. 아래와 같이 구독자수를 요청한다.
      let variable = { userTo: props.userTo }
      
      Axios.post('/api/subscribe/subscribeNumber', variable)
      	.then( response => {
      		if (response.data.success) {
      
      		} else {
      			alert('구독자 수 정보를 받아오지 못했습니다.')
      		}
      	})
      
      • 서버에서 /api/subscribe/subscribeNumber 라우터를 만들어야한다
      • index.js에서 /api/subscribe 라우터를 연결해준다.
      router.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, subscribeNumber: subscribe.length })
      	})
      })
      
      • length로 구독자수를 센다.
    4. 내가 이 비디오 업로드 한 유저를 구독하는지 정보 가져오기

      • userFrom은 localstorage에 있다. 아래와 같이 구독 여부를 요청한다.
      let subscribedVariable = { userTo: props.userTo, userFrom : localStorage.getItem('usetId')}
      		
      Axios.post('/api/subscribe/subscribed', subscribedVariable)
      	.then(response => {
      		if (response.data.success) {
      			setSubscribed(response.data.Subscribed)
      		} else {
      			alert('정보를 받아오지 못했습니다.')
      		}
      	})
      }, [])
      
      • 서버에서 /api/subscribe/subscribed 라우터를 만든다.
      router.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
      		res.status(200).json({ success: true, subscribed: result  })
      	})
      })
      
    5. 가져온 정보들 화면에 출력

      {SubscribeNumber} {Subscribed ? 'Subscribed' : 'Subscribe'}
      
    6. 구독하기/구독취소 기능 만들기

      • 클라이언트에서 구독 여부에 따라 구독또는 취소 요청을 한다.
      const onSubscribe = () => {
      
      	let subscribedVariable = { userTo: props.userTo, userFrom: props.userFrom }
      
      	if (Subscribed) {
      		Axios.post('/api/subscribe/unSubscribe', 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('구독 실패')
      				}
      			})
      	}
      }
      
      • 서버에서 해당 라우터들을 만든다.
      router.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})
      		})
      })
      
      • 구독취소는 findOneAndDelete를 이용해서 db에서 삭제한다.
      router.post('/Subscribe', (req, res) => {
      
      	const subscribe = new Subscriber(req.body)
      
      	subscribe.save((err, doc) => {
      			if (err) return res.status(400).json({ success: false, err })
      			return res.status(200).json({ success: true})
      		})
      })
      
      • 구독은 save를 통해서 db에 값을 넣는다.
      • 영상 업로더는 자기자신을 구독 못하게 해야한다.
      const subscribeButton = videoDetail.writer._id !== localStorage.getItem('usetId') && <Subscribe userTo={videoDetail.writer._id} userFrom={localStorage.getItem('usetId')} />
      

    구독 비디오 페이지

    1. 빈 Subscription 페이지 생성