코멘트만들기
모든 것은 db부터 시작
Comment Model 만들기
import mongoose from "mongoose";
const commentSchema = new mongoose.Schema({
text:{type:String, required:true},
owner:{type: mongoose.Schema.Types.ObjectId , required:true, ref:"User"},
video:{type: mongoose.Schema.Types.ObjectId , required:true, ref:"Video"}//어느 비디오에 달려있는 댓글인지 ,
createdAt: { type: Date, required: true, default: Date.now, },
});
const Comment = mongoose.model("Comment", commentSchema);
export default Comment;
User,Video Model에 comments 속성을 추가
const userSchema = new mongoose.Schema({
comment:[
{type:mongoose.Types.ObjectId, ref:"Comment"}
], //array로 comment 속성을 추가함
});
init.js
import " ./models/Comment "
----
이제 프론트부분을 만들어보자
client/js에 commentSection.js 파일을 만들고
이 상태로는 인식하지 못하니 webpack.config.js에 알려준다.
const BASE_JS ="./src/client/js/";
module.exports ={
entry: {
main: BASE_JS + "main.js",
videoPlayer: BASE_JS + "videoPlayer.js",
recorder: BASE_JS + "recorder.js",
commentSection: BASE_JS + "commentSection.js"
},
watch.pug 템플릿 아래에
script로 commentSection.js 불러오기
btn의 click을 감지x form의 submit을 감지
어떤 비디오에 댓글을 달았는지 알아야함
watch.pug에 있는 영상 시청후 조회수 올릴 때 사용한 data-id 이용
const videoId = videoContainer.dataset.id;
이제 백엔드에 보낼 준비가 되었음
commentModel의 속성인
text, video, createAt,는 자동으로 생성 됨
owner은?
backend에 request 보내면
owner은 누가 request 보냈는지 session에서 알면 됨
프론트엔드에서 누가 오너인지 알려주면 보안성 좋지 않음
((에러발생))
! querySelector을 찾을 수 없다는 에러가 나오는데
textarea와 btn을 찾을 수 없다는 말
로그아웃 한 상태에서
if loggedIn
div.video__add-comments
form.video__comments-form#commentForm
textarea(cols="30", rows="10", placeholder="Write a nice comment...")
button Add Comment
js에서 해당 HTML 찾을 수 없음
1.첫번 째 방법
js불러 올 때
block scripts
script(src="/static/js/videoPlayer.js")
if loggedIn //로그인 되어있을 때 불러오기
script(src="/static/js/commentSection.js")
2. 두번 째 방법
js코드를 개선하기
const handleSubmit = (event) => {
event.preventDefault();
const textarea = form.querySelector("textarea"); //form이 있을 때 실행되는 해당 함수에서 찾기
const text= textarea.value;
const videoId = videoContainer.dataset.id;
if(text === ""){
return;
}
fetch(`/api/videos/${videoId}/comment`,{
method:"POST",
headers:{
"Content-Type":"application/json",
},
body:JSON.stringify({text}),
});
textarea.value="";
}
if(form){ //form이 있으면 이벤트리스너를 실행시키고
form.addEventListener("submit",handleSubmit);
}
backend에 request 보내기
/fetch는 js로 request 요청을 보낼 수있다.
apiRouter.js
apiRouter.post("/videos/:id([0-9a-f]{24})/comment", createComment);
fetch() request에 포함할 수 있는 또 다른 선택적 속성은 body입니다. body 속성은 HTTP(또는 API) request의 일부로, 보내려는 모든 데이터를 포함할 수 있습니다. API request를 할 때, 데이터가 포함된 헤더와 함께 전송됩니다. fetch()를 사용하여 데이터를 보낼 때 보낸 데이터가 JSON인지 쿼리 문자열인지 API에 알려주는 Content-type을 지정해야 합니다. https://gomakethings.com/how-to-send-data-to-an-api-with-the-vanilla-js-fetch-method/
req.body.title, req.body.description처럼 form을 이해할 수 있는 이유?
app.use(express.urlencoded({extended:true})) 이 미들웨어 덕분에 form으로부터 오는 data를 이해 할 수있음
2. fetch로부터 오는 데이터 이해시키기
주로 fetch를 통해서 보내는 데이터는 JSON데이터
js객체를 보내게되면const obj = {text:"hello"} 우리의 브라우저는 문자화하게되고 [object object]같은 형식으로 나타나게됨 텍스트를 보내고 싶은데 안보내짐
const text = textarea.value
fetch(`/api/videos/${videoId}/comment`,{
method:"POST",
body:text
}); 이렇게 backend에 object를 보낼 때
브라우저의 nextwork에는 text를 request하는 걸 볼 수있지만
backend에는 여전히 req.body에 접근 하지못한다.
middleware가 웹사이트에 들어오는 form을 이해하도록 만들어 주듯이
다른 middleware로 웹사이트에 reqeust로 들어오는 text를 이해하도록 하기
app.use(express(express.text())); //backend는 누군가 text를 보내면 이해하고 req.body로 보내줌
front 에서 backend로 데이터를 보낼 수 있게 되었음
그러나 문자말고 평점이나 댓글 같이 object로 보내야할 때는?
[object object]로 됨
JSON.Stringify(object) 객체를 문자로 바꿔줌 {"text":"hello", "rating":"5"}
각각에 접근 가능한가 ? req.body.text reqbody.rating -->undifined 객체가아니라서
따라서 express.text();는 단일 문자로 바꿀 때만 사용가능하고
express.json()으로 string을 받아서 json으로 바꿔주는 middleware를 사용하자
JSON.Stringify()--> object를 문자열로 프론트엔드로가기전에 사용
JSON.parse() -->문자를 object로 바꿔줌 백엔드로 가기전에 사용
EXPRESS에게 TEXT가 아닌 JSON파일 을 보내고있다고 fetch의 컨텐트 타입을 통해서 알려줘야함 json middleware를 통해서 처리해줘야해 !
headers: { //request에 추가 할 수있는 정보 ehaders
"Content-Type" :"application/json",
}
if(text === ""){
return;
} textarea에 아무것도 없으면 보내지 않을 것임
fetch(`/api/videos/${videoId}/comment`,{
method:"POST",
headers:{
"Content-Type":"application/json",
},
body:JSON.stringify({text}),
});
(BIG 중요 포인트) 요약: 1. fetch로 백엔드에 요청을 보낼 때에 / method: "POST", headers: {}, body: {}, URL과 더불어 이 세 가지를 덧붙여야 한다. 2. headers에는 이 요청의 세부 사항을 명시하며, body에는 실질적인 컨텐츠가 포함된다. 3. 따로 명시하지 않을 시에 모든 body의 컨텐츠는 Text File로서 전송되고 받아 인식된다. 4. 특히 body: { ... }, 이런 식으로 자바스크립트 오브젝트를 넘겨줄 시 외부에서 이 오브젝트는 [object Object]라는 의미 없는 문자열로 변환된다. 5. 오브젝트와 그 안의 세부 변수 목록들을 넘겨주고 싶을 시, JSON이라는 규약에 의거한 오브젝트 내의 모든 기록을 텍스트화하여 넘겨주어야 하는데, 이때 JSON.stringify({ ... }) 라는 편리한 자체 표준 함수를 사용하면 된다. 6. 덧붙여 headers 안에 "Content-Type": "application/json"이라는 명시를 해 주어 전송된 텍스트가 JSON파일임을 백엔드에 인식시켜 준다. 7. 백엔드에 (이를테면 express를 사용 중이라면) app.use(express.json()); 미들웨어를 추가해주어 자체 내에서 JSON.parse("..."); JSON파일을 다시 자바스크립트 오브젝트로 변환해주는 표준 함수로 요청 body 내의 컨텐츠를 디코딩하는 작업을 한다.
express.text([options]) Express에 내장된 미들웨어 기능입니다. body-parser를 기반으로 request payload로 전달한 문자열을 파싱합니다. https://expressjs.com/ko/api.html#express.text express.json([options]) Express에 내장된 미들웨어 기능입니다. body-parser를 기반으로 request payload로 전달한 JSON을 파싱합니다. https://expressjs.com/ko/api.html#express.json
Express 4.x - API 참조
Express 4.x API express() Creates an Express application. The express() function is a top-level function exported by the express module. var express = require('express') var app = express() Methods express.json([options]) This middleware is available in Ex
expressjs.com
----------------------------
이제는 우리가 달 비디오의 아이디와 텍스트를 가지고있다.
남은것은 어떤 사용자가 보내냐
backend로 보내는 모든 request는 쿠키와 같이 옴
우리가 backend로보내는 모든 request는 쿠키와 함께 온다 쿠키를 보내면 backend는 세션을 찾아본다.
fetch --> 쿠키가 자동으로 브라우저에 저장되고 --> videoController에서 req.session.user사용 할 수있다.
export const createComment = async (req, res) => {
const {
session: { user },
body: { text },
params: { id },
} = req;
const video = await Video.findById(id);
if (!video) {
return res.sendStatus(404);
}
const comment = await Comment.create({
text,
owner: user._id,
video: id,
});
video.comments.push(comment._id); //코멘트를 비디오 저장해야함
video.save();
return res.sendStatus(201);
};
HTTP 상태 코드 200 OK 201 Created 400 Bad Request 404 Not Found https://developer.mozilla.org/ko/docs/Web/HTTP/Status
HTTP 상태 코드 - HTTP | MDN
HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고
developer.mozilla.org
_id is created by Mongo, the one coming from the DB. 'id' is what HTML element have.
if loggedIn
div.video__add-comments
form.video__comment-form#commentForm
textarea(cols="30", rows="10", placeholder="Write a nice comment...")
button Add Comment
div.video__comments
ul
each comment in video.comments.reverse() //퍼그에서 자바스크립트 됨
li.video__comment
i.fas.fa-comment
| #{comment.text}
const handleSubmit = async (event) => {
event.preventDefault();
const textarea = form.querySelector("textarea");
const text= textarea.value;
const videoId = videoContainer.dataset.id;
if(text === ""){
return;
}
await fetch(`/api/videos/${videoId}/comment`,{
method:"POST",
headers:{
"Content-Type":"application/json",
},
body:JSON.stringify({text}),
});
textarea.value="";
window.loaction.reload();
//실시간으로 댓글이 달리는 것처럼 보이지만 사실 refresh되는 것이고 refresh 할 때마다 db에서 video를 찾기 때문에 과부화가 걸릴 수 있다.
}
const handleSubmit = async(event) => {
event.preventDefault();
const textarea = form.querySelector("textarea");
const text= textarea.value;
const videoId = videoContainer.dataset.id;
if(text === ""){
return;
}
const {status} =await fetch(`/api/videos/${videoId}/comment`,{
method:"POST",
headers:{
"Content-Type":"application/json",
},
body:JSON.stringify({text}),
});
textarea.value="";
if(status === 201){
console.log("create fake comment");
}
addComment(text);
}
if(form){
form.addEventListener("submit",handleSubmit);
}
const addComment = (text)=>{
const videoComments = document.querySelector(".video__comments ul")
const newComment = document.createElement("li");
newComment.className= "video__comment";
const icon = document.createElement("i");
icon.className= "fas fa-comment";
const span = document.createElement("span");
span.innerText = ` ${text}`
newComment.appendChild(icon);
newComment.appendChild(span);
videoComments.prepend(newComment); //prepend 는 제일 위에 달린다.
}
TTP Method 종류와 의미들 올려둡니다. 주요 메소드 5가지 GET : 리소스 조회 POST : 요청 데이터 처리, 주로 데이터 등록에 사용 PUT : 리소스를 대체, 해당 리소스가 없으면 생성 PATCH : 리소스를 일부만 변경 DELETE : 리소스 삭제
res.json([body]) JSON response를 보냅니다. 이 메서드는 JSON.stringify()를 사용하여 JSON 문자열로 변환된 매개변수인 response를 보냅니다. 사용 예시 ``` res.json(null) res.json({ user: 'tobi' }) res.status(500).json({ error: 'message' }) res.status(201).send({ hello: 'message' }) // res.send()로도 가능 ``` https://expressjs.com/ko/api.html#res.json Response.json() Response 인터페이스의 json() 메서드는 Response 스트림을 가져와 읽습니다. (백엔드의 res.json() 또는 res.send()를 통해 보낸 JSON데이터를 가져온다.) https://developer.mozilla.org/en-US/docs/Web/API/Response/json
Response.json() - Web APIs | MDN
The json() method of the Response interface takes a Response stream and reads it to completion. It returns a promise which resolves with the result of parsing the body text as JSON.
developer.mozilla.org
1. We send json data from backend(videoController) to frontend by "return res.status(201).json({newCommentId: comment._id})" --- .json() is how we send response from backend to frontend 2. We can check the response the backend sent by looking at the Network of F12. 3. On the frontend(commentSection.js) we get the json data by await response.json()
3we can console.log it 4. As we can put data on the html using 'data-~~' to use it in JS. We can reversely set the data from js side. ex) newComment.dataset.id = id(this is variable)
1. pug에서 댓글owner의 _id와 loggedInUser의 id를 비교하여 댓글 삭제 버튼 보이게 , 퍼그에서 data-id=comment._id
2.새로 생성한 가짜 실시간 댓글 생성 시에도 삭제 버튼 보이게
3.실시간 댓글은 data-id가 없음
delete request보내려면 댓글의 id를 알아야함
4.videoController에 comment 접근 할 수있는데 코멘트 생성하고나서 상태코드만 보내는게아니라 프론트에 json으로 메세지를 보내
export const createComment = async (req, res) => {
const {
session: { user },
body: { text },
params: { id },
} = req;
const video = await Video.findById(id);
if (!video) {
return res.sendStatus(404);
}
const comment = await Comment.create({
text,
owner: user._id,
video: id,
});
video.comments.push(comment._id);
video.save();
return res.sendStatus(201); res.status(201).json({newCommentId: comment._id});
};
/브라우저에 network 탭을보면 response에 도착 함
어떻게 추출 할까 ?
fetch는 response를 받고
const response =await fetch(`/api/videos/${videoId}/comment`,{
method:"POST",
headers:{
"Content-Type":"application/json",
},
body:JSON.stringify({text}),
}); //console.log(response)해도 id를 찾을 수 없다.
if ( response . status === 201 ) {
textarea . value = "" ;
const {newCommentId}= await response.json(); / fetch에서 response반응을 기다렸다가 response안에서 json를 추출하면 새로운댓글의 id를 얻을 수dlT음 이 모든건 댓글이 create될 때 코드 (status ==201)
addComment(text, newCommentId );
}
//const json = awaot response.json()를 console.log(json)하면{newCommentId: comment._id}
const addComment = (text,id )=>{
const videoComments = document.querySelector(".video__comments ul")
const newComment = document.createElement("li");
newComment.dataset.id =id ;
newComment.className= "video__comment";
const icon = document.createElement("i");
icon.className= "fas fa-comment";
const span = document.createElement("span");
span.innerText = ` ${text}`;
const span2 = document.createElement("span");
span2.innerText=`❌`;
newComment.appendChild(icon);
newComment.appendChild(span);
newComment.appendChild(span2);
videoComments.prepend(newComment);
}
이제 실시간 댓글 생성될 때도 id가 생김 --> 삭제가능
https://github.com/nomadcoders/wetube-reloaded/commit/ebd4970cca32f23c653684756b34629066f602f2
15.9 Comment Ids · nomadcoders/wetube-reloaded@ebd4970
Permalink This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. Browse files 15.9 Comment Ids Loading branch information Showing 3 changed files with 13 additions and 7 deletions. +10 −5 src/cli
github.com
+삭제기능 , 프론트, 백 모두
댓글 작성자인지 둘 다 체크