Search
Duplicate

유튜브 페이지 클론

Boiler Plate (1~3강)

설치

1.
더 빠르게 개발을 완성하기 위해서
3.
npm install로 dependencies 다운받기
Visual Studio 설치 필요

MongoDB

1.
몽고 db 회원가입
2.
몽고 db 공홈에서 클러스터 만들기
클라우드 상에서 만드는것
싱가포르 프리 티어로 할것
3.
유저 만들기
CONNECT 눌러서 유저 생성
connect your application
연결 코드 복사
4.
몽고 db 연결 코드 작성
dev.js 만들고 연결 코드를 붙여넣는다.
passwrod랑 dbname 부분 잘 치환해서 작성

실행

npm run dev 하면 실행된다!
안되는 경우 5000 또는 3000번 포트로 다른 서버가 열려있는지 확인해볼것

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

1.
Upload Page 만들기
VideoUploadPage.js 파일 생성
2.
Upload Page Route 만들기
true : 로그인한 사람만 들어갈수있는 페이지
false : 로그인안한 사람만 들어갈수있는 페이지
null : 아무나 들어갈수있는 페이지
<Route exact path="/video/upload" component={Auth(VideoUploadPage, true)} />
JavaScript
복사
3.
Upload Page Header Tab 만들기
로그인이 되어있을 경우에만 뜨도록 변경
(user.userData && !user.userData.isAuth) 사용
<Menu.Item key="upload"> <a href="/video/upload">video</a> </Menu.Item>
JavaScript
복사
4.
Form Template 만들기
영상에서 주어지는 UI 그대로 구현
<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>
JavaScript
복사
5.
파일을 올리는 Template 만들기 위해 Drop-zone 다운받기
클라이언트 폴더에 다운받아야하는거 주의!
npm install react-dropzone —save
<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>
JavaScript
복사
6.
onChange func 만들기
state 에 value를 넣어두고 서버에 한꺼번에 보낸다.
useState 로 초기값 정함
const [VideoTitle, setVideioTitle] = useState(""); const [Description, setDescription] = useState(""); const [private, setPrivate] = useState(0) const [Categories, setCategories] = useState("Film & Animation")
JavaScript
복사
카테고리 및 private 설정은 옵션태그 여러개를 만들어야한다. map으로 깔끔하게 해결
const PrivateOption = [ { value: 0, label: 'Private' }, { value: 1, label: 'Public' } ]
JavaScript
복사
{PrivateOption .map((item, index) => ( <option key={index} value={item.value}>{item.label}</option> ))}
JavaScript
복사
위와 같은 방법으로 카테고리도 옵션 태그 여러개를 깔끔하게 쓸수있다.
입력칸은 onChange를 통해서 실시간으로 값을 업데이트해주지 않으면 작동하지 않는다.
const onTitleChange = (event) => { setTitle(event.currentTarget.value) }
JavaScript
복사
onChange={handleChangeTitle} value={title}
JavaScript
복사
다른 입력태그에 대해서도 onChange일 경우에 대한 함수를 만들고 속성값으로 추가해준다.

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

a.
Side Video 부분 Layout template 만들기
SideVideo라는 컴포넌트를 따로 만든다.
b.
한개의 카드 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>
JavaScript
복사
index를 key로 넣어줘야 에러가 나지 않는다.
c.
DB에서 모든 비디오 데이터를 불러오기
랜딩 페이지에서 썼던것 그대로 활용
d.
불러온 데이터 화면에 출력하기
const renderSideVideo = sideVideos.map((video, index) => { //여기에 template을 넣어서 출력 }
JavaScript
복사

구독기능

a.
Subscriber Model 만들기
userTo, userFrom 로 구독자수를 센다.
const subscriberSchema = mongoose.Schema({ userTo: { type: Schema.Types.ObjectId, ref: 'User' }, userFrom: { type: Schema.Types.ObjectId, ref: 'User' } }, { timestamps: true })
JavaScript
복사
b.
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>
JavaScript
복사
c.
데이터베이스에서 얼마나 많은 사람이 비디오 업로드 한 유저를 구독하는지 정보 가져오기
디테일 페이지에서 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('구독자 수 정보를 받아오지 못했습니다.') } })
JavaScript
복사
서버에서 /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 }) }) })
JavaScript
복사
length로 구독자수를 센다.
d.
내가 이 비디오 업로드 한 유저를 구독하는지 정보 가져오기
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('정보를 받아오지 못했습니다.') } }) }, [])
JavaScript
복사
서버에서 /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 }) }) })
JavaScript
복사
e.
가져온 정보들 화면에 출력
{SubscribeNumber} {Subscribed ? 'Subscribed' : 'Subscribe'}
JavaScript
복사
f.
구독하기/구독취소 기능 만들기
클라이언트에서 구독 여부에 따라 구독또는 취소 요청을 한다.
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('구독 실패') } }) } }
JavaScript
복사
서버에서 해당 라우터들을 만든다.
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}) }) })
JavaScript
복사
구독취소는 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}) }) })
JavaScript
복사
구독은 save를 통해서 db에 값을 넣는다.
영상 업로더는 자기자신을 구독 못하게 해야한다.
const subscribeButton = videoDetail.writer._id !== localStorage.getItem('usetId') && <Subscribe userTo={videoDetail.writer._id} userFrom={localStorage.getItem('usetId')} />
JavaScript
복사

구독 비디오 페이지

a.
빈 Subscription 페이지 생성
b.
Subscription Page를 위한 Route 만들기
app.js 수정해서 route 만든다.
<Route exact path="/subscription" component={Auth(SubscriptionPage, null)} />
JavaScript
복사
c.
Template 만들기
랜딩페이지를 거의 그대로 가져온다.
subcription 페이지를 탭에 올린다.
<Menu.Item key="subscription"> <a href="/subscription">Subscription</a> </Menu.Item>
JavaScript
복사
d.
내가 구독한 유저의 비디오들만 서버에서 가져오기
/api/video/getSubscriptionVideos 로 구독한 영상만 요청 (userFrom 사용)
let variable = { userFrom: localStorage.getItem('userId') } useEffect(() => { axios.post('/api/video/getSubscriptionVideos', variable) .then(response => { if (response.data.success) { setVideos(response.data.videos) } else { alert('Failed to get subscription videos') } }) }, [])
JavaScript
복사
서버에서 라우터 생성
자신의 아이디를 가지고 구독하는 사람들을 찾는다.
Subscriber.find({ 'userFrom': req.body.userFrom }) .exec((err, subscribers) => { if (err) return res.status(400).send(err); let subscribedUser = []; subscribers.map((subscriber, i) => { subscribedUser.push(subscriber.userTo) }) //subscribedUser에 구독하는 사람들이 있다.
JavaScript
복사
찾은 사람들의 비디오를 가지고 온다.
구독한 사람이 여러명이기때문에 in이라는 메소드를 사용한다.
몽고db 내부함수인 in을 통해서 한번에 여러명의 결과를 가져올수있다.
Video.find({ writer: { $in: subscribedUser } }) .populate('writer') .exec((err, videos) => { if (err) return res.status(400).send(err); res.status(200).json({ success: true, videos }) })
JavaScript
복사
e.
가져온 비디오 데이터들을 화면에 출력하기

댓글 기능 생성 - 구조 설명

a.
댓글 부분 구조 설명
댓글을 달수 있다.
댓글에 답글을 달 수 있다.
답글에도 무한하게 답글을 달 수 있다.
b.
Commet model 생성
writer, videoid, content, replayTo 4가지 가 필요하다
const commentSchema = mongoose.Schema({ writer: { type: Schema.Types.ObjectId, ref: 'User' }, postId: { type: Schema.Types.ObjectId, ref: 'Video' }, responseTo: { type: Schema.Types.ObjectId, ref: 'User' }, content: { type: String } }, { timestamps: true })
JavaScript
복사
c.
디테일 비디오 페이지에 Commet Component 만들기

댓글 기능 생성 - Comment.js

a.
template 만들기
b.
handleChange func 만들기
const handleClick = (e) => { setComment(e.currentTarget.value) }
JavaScript
복사
c.
OnSubmit func 만들기
기존 동작 안하게 prevent 해준다.
props로 postId를 받아온다.
writer를 위해서 redux hook를 사용한다.
const user = useSelector(state => state.user)
JavaScript
복사
/api/comment/saveComment 로 요청한다.
서버에서 라우터를 만든다. index.js에서 comment 라우터 연결 해준다.
router.post("/saveComment", (req, res) => { const comment = new Comment(req.body) comment.save((err, comment) => { if (err) return res.json({ success: false, err }) Comment.find({ '_id': comment._id }) .populate('writer') .exec((err, result) => { if (err) return res.json({ success: false, err }) return res.status(200).json({ success: true, result }) }) }) })
JavaScript
복사
axios.post('/api/comment/saveComment', variables) .then(response => { if (response.data.success) { console.log(response.data.result) } else { alert('코맨트를 저장하지 못했습니다.') } })
JavaScript
복사
서버에서 받아온 Comment내용을 콘솔에서 확인
d.
이때 추가하고 나면 comment state를 업데이트 해줘야함