무료
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
videoDetail 을 가져올 때 GET 이 아닌 POST를 사용하는 이유가 궁금합니다.
안녕하세요 강의 정말 감사히 잘 보고 있습니다. 현재 Detail 화면을 작성하는 코드까지 마무리 하였는데 잘 작동은 하나 궁금한 부분이 있어 질문드립니다 ! 아래 코드에서 video의 detail 정보를 가져오는데 왜 GET 이 아닌 POST를 사용하는지 궁금합니다. 일반적으로 API공부를 할 때 GET Method 를 통해 Data 를 가져오고 POST Method를 통해 Insert 와 같은 작업을 진행하는걸로 알고 있는데 혹 다른 이유가 있으신가요 ?? 강의 정말 잘 듣고 있습니다. 감사합니다. router.post('/getVideoDetail', (req, res) => { // get videos then send to client Video.findOne( {"_id" : req.body.videoId }) .populate('writer') .exec((err, videoDetail) => { if(err) return res.status(400).send(err) return res.status(200).json({success : true, videoDetail}); }) })
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
&& 연산자를 두번 사용하려면 어떻게 해야하나요?
VideoDetailPage / Sections / ReplyComment.js에서 props.commentLists를 console.log해보니 responseTo가 있는 것이 있고 없는 것이 있는데요, 그래서 responseTo가 있는 것들만 출력하고 싶은데, 이미 &&연산자가 comment.responseTo === parentCommentId && 여기서 쓰여서, 그 안에 또 중첩해서 &&연산자를 쓸 수 있는지 시도해봤는데 안되네요. ㅠㅠ 혹시 &&연산자를 두번 사용하려면 어떻게 해야하나요? console.log(props.commentLists); let renderReplyComment = (parentCommentId) => { return props.commentLists.map((comment, index) => ( <React.Fragment key={index}> {comment.responseTo === parentCommentId && ( <div style={{ width: "80%", marginLeft: "40px" }}> <SingleComment refreshFunction={props.refreshFunction} comment={comment} postId={props.postId} /> <ReplyComment commentLists={props.commentLists} refreshFunction={props.refreshFunction} postId={props.postId} parentCommentId={comment._id} /> </div> )} </React.Fragment> )); };
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
파일 업로드 파일 필터 재문의드려요~
안녕하세요 답변주셔서 감사합니다 서버쪽에서 video.js 에서 var storage = multer.diskStorage ({ destination: (req,file, cb) => { cb(null, "uploads/"); }, filename: (req,file,cb) => { cb(null, `${Date.now()}_${file.originalname}`) }, fileFilter: (req,file,cb) => { const ext = path.extname(file.originalname) alert(ext); if(ext !== '.mp4'){ return cb(res.status(400).end('only jpg, png, mp4 is allowed'), false); } cb(null, true) } }); 해당코드에서 fileFilter가 적용이 안되는거 같아요 동영상 업로드에서 동영상 파일이 아닌 것을 못올리게 막고싶은데 이상태로 동영상파일이 아닌것을 올리면 서버를 재시작해야하는 사태가 일어납니다. 어떻게 코드를 바꿔야할까요??
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
err 를 리턴하는 경우
안녕하세요. err가 생겼을 때, 어떤 때는 res.status(400).json({success: false, err}) 를 하는 경우가 있고, 어떤 때는 status(400)을 떼고 쓰는 경우가 있는 것 같은데요, 둘의 차이점이 무엇인지 알 수 있을까요? 감사합니다! :)
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
안녕하세요 강사님! 파일 업로드에서 filefilter 옵션이 제대로 작동을 안하는것같습니다
안녕하세요 강사님! 파일 업로드에서 filefilter 옵션이 제대로 작동을 안하는것같습니다. filefilter 를 하려면 어떻게 코드를 바꿔야 할까요???
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
로그인 에러
저도 밑에 분들과 마찬가지로 vs코드 터미널엔 ] [HPM] Error occurred while trying to proxy request 이런 오류가 떠요.
- 따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
504 Gate way Time out 발생 문의
삭제된 글입니다
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
boilerplate 강의를 듣고 navbar내용만 깃허브에서 다운받아서
해보려고했는데 잘 안되네요 ㅠㅠ.. navbar 강의도 있으면 좋겠어요 !
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
파일 다운 및 clone 404 error
안녕하세요. 강의를 듣고 react수업을 듣는 수강생입니다. 다름이 아니라 파일을 다운이나 클론해야하는 데 해당 페이지가 존재하지 않는 이유에서 인지 404 error가 git 주소로 가면 보이네요 답변 부탁드립니다
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
npm run dev 후 회원가입이 되질 않습니다
회원가입 폼에서 정보 입력 후 제출 눌러도 아무런 답이 없고 터미널에 해당 에러만 가득찹니다 bcrypt 버전을 3.0.6 -> 5.0.0으로 올려도 안되네요..버전 업 후에는 자동 로그인 상태입니다 어떻게 해야할까요
- 따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
왜 무한로딩이 될까요...
삭제된 글입니다
- 따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
썸네일 경로가 잘못됐는지, 불러와지지가 않습니다..
삭제된 글입니다
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
privacy와 category를 default로 설정하면 실패합니다.
두개 설정을 바꾸면 잘 되는데 말이죠..
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
Like 취소 부분에 질문이 있습니다.
강사님은 Like.findByIdAndDelete를 사용하셔서 해당 Like 도큐먼트를 지워주셨습니다. 그런데 이 경우 다수의 사람이 좋아요를 눌렀을 경우 한 사람만이라도 좋아요를 다시 눌러 취소할 경우 조건에 해당하는 도큐먼트 전체가 삭제되는 것이 아닌가요? 아래는 강사님이 작성하신 스키마와 백엔드 로직입니다. const likeSchema = mongoose.Schema({ userId: { type: Schema.Types.ObjectId, ref: 'User' }, commentId: { type: Schema.Types.ObjectId, ref: 'Comment' }, videoId: { type: Schema.Types.ObjectId, ref: 'Video' } }, { timestamps: true }) router.post("/unLike", (req, res) => { let variable = {} if (req.body.videoId) { variable = { videoId: req.body.videoId, userId: req.body.userId } } else { variable = { commentId: req.body.commentId , userId: req.body.userId } } Like.findOneAndDelete(variable) .exec((err, result) => { if (err) return res.status(400).json({ success: false, err }) res.status(200).json({ success: true }) }) }) 제 생각에는 해당 Like의 _id를 찾아내거나, userId와 commentId가 동시에 일치하도록 조건을 걸어주어야 하는 게 아닌가 싶습니다. 혹시 제가 잘못 알고 있는 부분이라면 지적 부탁드립니다.
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
강사님 DetailVideoPage 질문 드릴게요!
안녕하세요 강사님 어느덧 강의 후반부를 달리고 있습니다. 우선 좋은 강의 정말 감사합니다. DetailVideoPage에서 궁금한 부분 질문드릴게요. LikeDislikes와 Subscriber 를 가져오실때 {[<LikeDislikes/>, <Subscriber/>]} 의 형태로 쓰셨는데 <LikeDislikes/>, <Subscriber/> 와의 차이가 무엇인지 궁금합니다. 감사합니다.
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
업로드 과정 오류
강의를 듣는도중 업로드 과정에서 서버와 클라이언트 똑같은 코드를 사용했는데도 fail to the save video in server alert를 띄웁니다. 도저히... 코드를 봐도 모르겠습니다. 한번 봐주시면 감사하겠습니다. https://github.com/AnGwangsu/YoutubeClone
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
비디오 업로드 폼 질문드려요
강의 잘 보고 있습니다. 비디오 업로드 폼에서 콘솔에 파일 이름과 경로는 나오는데 왜 영화/애니, 비공개/공개 설정하고 submit 버튼 누르면 'Please first fill all the fields' 팝업창이 왜 뜨는지 궁금합니다. 썸네일 504 에러도 궁금하구요 ㅜㅜ UploadVideoPage.js 코드 첨부합니다. import React, { useState, useEffect } from 'react' import { Typography, Button, Form, Input, Icon } from 'antd'; import Dropzone from 'react-dropzone'; import axios from 'axios'; import { useSelector } from "react-redux"; const { Title } = Typography; const { TextArea } = Input; const Private = [ { value: 0, label: '비공개' }, { value: 1, label: '공개' } ] const Catogory = [ { value: 0, label: "영화/애니메이션" }, { value: 0, label: "자동차" }, { value: 0, label: "음악" }, { value: 0, label: "동물" }, { value: 0, label: "스포츠" }, ] function UploadVideoPage(props) { const user = useSelector(state => state.user); const [title, setTitle] = useState(""); const [Description, setDescription] = useState(""); const [privacy, setPrivacy] = useState(0) const [Categories, setCategories] = useState("영화/애니메이션") const [FilePath, setFilePath] = useState("") const [Duration, setDuration] = useState("") const [Thumbnail, setThumbnail] = useState("") const handleChangeTitle = (event) => { setTitle(event.currentTarget.value) } const handleChangeDecsription = (event) => { console.log(event.currentTarget.value) setDescription(event.currentTarget.value) } const handleChangeOne = (event) => { setPrivacy(event.currentTarget.value) } const handleChangeTwo = (event) => { setCategories(event.currentTarget.value) } const onSubmit = (event) => { event.preventDefault(); if (user.userData && !user.userData.isAuth) { return alert('Please Log in First') } if (title === "" || Description === "" || Categories === "" || FilePath === "" || Duration === "" || Thumbnail === "") { return alert('Please first fill all the fields') } const variables = { writer: user.userData._id, title: title, description: Description, privacy: privacy, filePath: FilePath, category: Categories, duration: Duration, thumbnail: Thumbnail } axios.post('/api/video/uploadVideo', variables) .then(response => { if (response.data.success) { alert('video Uploaded Successfully') props.history.push('/') } else { alert('Failed to upload video') } }) } const onDrop = (files) => { let formData = new FormData(); const config = { header: { 'content-type': 'multipart/form-data' } } console.log(files) formData.append("file", files[0]) axios.post('/api/video/uploadfiles', formData, config) .then(response => { if (response.data.success) { console.log(response.data); let variable = { filePath: response.data.filePath, fileName: response.data.fileName } setFilePath(response.data.filePath) //gerenate thumbnail with this filepath ! axios.post('/api/video/thumbnail', variable) .then(response => { if (response.data.success) { setDuration(response.data.fileDuration) setThumbnail(response.data.thumbsFilePath) } else { alert('Failed to make the thumbnails'); } }) } else { alert('failed to save the video in server') } }) } return ( <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' }}> <Dropzone onDrop={onDrop} multiple={false} maxSize={800000000}> {({ getRootProps, getInputProps }) => ( <div style={{ width: '300px', height: '240px', border: '1px solid lightgray', display: 'flex', alignItems: 'center', justifyContent: 'center' }} {...getRootProps()} > <input {...getInputProps()} /> <Icon type="plus" style={{ fontSize: '3rem' }} /> </div> )} </Dropzone> {Thumbnail !== "" && <div> <img src={`http://localhost:5000/${Thumbnail}`} alt="haha" /> </div> } </div> <br /><br /> <label>Title</label> <Input onChange={handleChangeTitle} value={title} /> <br /><br /> <label>Description</label> <TextArea onChange={handleChangeDecsription} value={Description} /> <br /><br /> <select onChange={handleChangeOne}> {Private.map((item, index) => ( <option key={index} value={item.value}>{item.label}</option> ))} </select> <br /><br /> <select onChange={handleChangeTwo}> {Catogory.map((item, index) => ( <option key={index} value={item.label}>{item.label}</option> ))} </select> <br /><br /> <Button type="primary" size="large" onClick={onSubmit}> Submit </Button> </Form> </div> ) } export default UploadVideoPage Video.js 코드도 첨부합니다. const express = require('express'); const router = express.Router(); const multer = require('multer'); var ffmpeg = require('fluent-ffmpeg'); const { Video } = require("../models/Video"); const { Subscriber } = require("../models/Subscriber"); const { auth } = require("../middleware/auth"); var storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'uploads/') }, filename: (req, file, cb) => { cb(null, `${Date.now()}_${file.originalname}`) }, fileFilter: (req, file, cb) => { const ext = path.extname(file.originalname) if (ext !== '.mp4') { return cb(res.status(400).end('only jpg, png, mp4 is allowed'), false); } cb(null, true) } }) const upload = multer({ storage: storage }).single("file") //클라이언트가 올린 비디오를 서버에 전달한다 router.post("/uploadfiles", (req, res) => { upload(req, res, err => { if (err) { return res.json({ success: false, err }) } return res.json({ success: true, filePath: res.req.file.path, fileName: res.req.file.filename }) }) }); router.post("/thumbnail", (req, res) => { let thumbsFilePath =""; let fileDuration =""; ffmpeg.ffprobe(req.body.filePath, function(err, metadata){ console.dir(metadata); console.log(metadata.format.duration); fileDuration = metadata.format.duration; }) ffmpeg(req.body.filePath) .on('filenames', function (filenames) { console.log('Will generate ' + filenames.join(', ')) thumbsFilePath = "uploads/thumbnails/" + filenames[0]; }) .on('end', function () { console.log('Screenshots taken'); return res.json({ success: true, thumbsFilePath: thumbsFilePath, fileDuration: fileDuration}) }) .screenshots({ // Will take screens at 20%, 40%, 60% and 80% of the video count: 3, folder: 'uploads/thumbnails', size:'320x240', // %b input basename ( filename w/o extension ) filename:'thumbnail-%b.png' }); }); router.get("/getVideos", (req, res) => { Video.find() .populate('writer') .exec((err, videos) => { if(err) return res.status(400).send(err); res.status(200).json({ success: true, videos }) }) }); router.post("/uploadVideo", (req, res) => { const video = new Video(req.body) video.save((err, video) => { if(err) return res.status(400).json({ success: false, err }) return res.status(200).json({ success: true }) }) }); router.post("/getVideo", (req, res) => { Video.findOne({ "_id" : req.body.videoId }) .populate('writer') .exec((err, video) => { if(err) return res.status(400).send(err); res.status(200).json({ success: true, video }) }) }); router.post("/getSubscriptionVideos", (req, res) => { //Need to find all of the Users that I am subscribing to From Subscriber Collection 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) }) //Need to Fetch all of the Videos that belong to the Users that I found in previous step. 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 }) }) }) }); module.exports = router; 뭐가 문제일까요? 도와주세요 ㅜㅜ
- 따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
기존boilerplate-mern-stack 이용 install
삭제된 글입니다
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
ffmpeg 관련
안녕하세요 강사님, 리액트 강의 잘 듣고 있습니다! 다름이 아니라 윈도우용 ffmpeg를 다운로드 하려고 홈페이지에 접속하니 9월 18일에 종료된다는 안내문구가 써있더라구요 ffmpeg가 종료되어도 수업을 계속 진행하기에는 지장이 없는걸까요? 9월 18일 안에 이 수업을 다 수강하지 못할 것 같아서요ㅎㅎ
- 미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
안녕하세요.
응용해서 비디오의 수정이나 삭제는 가능하게 만들었는데.. 댓글같은 경우에는 삭제시에 refreshfunction 기능을 어떻게 적용시켜야 할까요..ㅠ