본문 바로가기

코딩/Today I Learn

02.15화

728x90

코멘트만들기  

 

모든 것은 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()

3
we 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

 

+삭제기능 , 프론트, 백 모두 

댓글 작성자인지 둘 다 체크 

 

'코딩 > Today I Learn' 카테고리의 다른 글

Zoom 클론코딩 #1 환경설정  (0) 2022.02.26
2.19토  (0) 2022.02.26
에러로깅  (0) 2022.01.11
01.05공부  (0) 2022.01.05
01/04 공부2  (0) 2022.01.04