CSR로 댓글 기능 구현

김종현·2023년 4월 14일
0

CSR 구현
① 페이지 로드 시 필요한 리소스를 클라이언트에 미리 선언
② 클라이언트에서 필요한 데이터를 비동기 호출 받음
③ 클라이언트가 전달받은 데이터를 가공, 리소스를 사용하여 화면에 표시.

클라이언트에 리소스 선언 -HTML Template
-클라이언트에 리소스 선언 하는 방법은 다양) 리엑트, 뷰
-현재는 HTML 템플릿 기능을 간단하게 사용해 브라우저에 표시되지 않는 HTML 요소를 작성해두고 Js로 이를 화면에 반복적으로 그려냄.

  1. 댓글 화면 작성
    -게시글 상세화면 하단에 댓글 작성, 목록 화면 추가
    -HTMLTemplate 사용하여 한개의 댓글이 표시될 모양을 선언
    -JavaScript 로 조작하기 위해 id, class를 선언하는것이 유용
...
table 
	thead
    tr
    	td(colspan="2")
          input#content(type="text")
        td: button(onclick="writeComment()") 댓글작성
	tbody#comments
template#comment-template
	tr
		td.content
        td.author
        td.createdAt
  1. 데이터 비동기 호출 - API 작성하기
    -SSR(e.g. pug)은 HTTP 응답으로 HTML을 전송하는 방식. CSR을 구현하기 위해서는HTML이 아닌, 데이터만 주고 받을 수 있는 API를 구성해야함(JSON사용)
    -댓글 작성 API와 댓글 목록 API만 구현 예정.
    -간단하게 댓글 작성시 댓글 목록을 다시 불러와(새로고침) 그리는 형식으로 구현

게시글에 댓글 추가하기

1. Post 스키마에 comment를 추가

-comment = 댓글
-mongoose의 sub-schema를 이용하여 Post 스키마에 Comment를 배열로추가
e.g) comments: [CommentSchema]
-populate를 사용할때, ObjectID만 저장 하는 것과는 다르게 Comment의 내용을 게시글이 포함하게 됨
※ sub-schema 내부에서도 populate가능

---PostSchema---
const CommentSchema = new Schema({
	content: String,
  	author: {type: Schema.Types.ObjectId,
             ref: 'User',},
}, {timestamps: true,}
);

const PostSchema= new Schema({
  ...
  // comments에 서브스키마 삽입
  comments: [CommentSchema],
  ...

2. API - 댓글 작성

-api 라우터를 추가하고, RESTful하게 api/posts/{postId}/comment 경로로 댓글작성 기능 구현
-게시글 업데이트시 $push를 사용하여 몽고DB를 통해 comments 배열에 새로 작성된 댓글 추가동시에 들어오는 요청에 대해 정확히 처리
-api는 render 하지않고 json으로 응답

---routes/api.js---
...
//shortId로 작성한 댓글 경로
router.post('/posts/:shortId/comments', ... {
//shortId를 받아와 게시글 찾음
  const { shortId } = req.params;
  const { content } = req.body;	
  const author= await User.findOne({ shortId: req.user.shortId});
//shortid로 게시글 찾아 comments를 넣어 게시글 업데이트
await Post.updateOne({ shortId}, {
// Post내의 comments에 내용/작성자가 들어감
  $push: { comments: { 
    content, 
    author,
  }},
});
// api는 render 하지않고 json으로 응답
res.json({ result: 'success' });});
...

3.API - 댓글 작성 목록

-/api/posts/{postId}/comments로 RESTful 경로 설정
-find에 populate 하지않고 User(model)로 populate를 사용하는 방법도 가능

...
//shortId를 받아 특정 게시글 찾아옴.
router.get('/posts/:shortId/comments', ... {
const { shortId } = req.params;
const post = await Post.findOne({ shortId });
// post.comments는 content, author를 가짐.
// 각 author에 User가 obi로 검색이 돼서 주입됨.
await User.populate(post.comments, {
  path: 'author'
});
//json으로 보내게 되면 각 댓글에 작성자가 주입되어 사용 가능해짐.
res.json(post.comments);
});
...

4. 데이터 비동기 호출 - fetch로 클라이언트에서 api 호출하기

-브라우저는 비동기 HTTP 요청을 fetch 함수를 이용해 제공 : 간단하게 Js로 HTTP 요청 처리.
-jQuery의 ajax와 유사한 기능인데 이를 사용하지 않고 HTTP 요청 구현 가능.

(1) 댓글 작성
-댓글 작성 버튼 클릭시 writeComment() 실행되는 코드임.
-input#content에서 내용을 읽어 fetch로 댓글 작성api 호출
-호출 결과의 성공 여부를 확인하여, 댓글 다시 불러오기 실행

---posts/view.pug
...
script.
	function writeComment() {
      const input = document.querySelector('#content');
      const content = input.value;
      ---fetch 부분: JSON req를 보내기 위한 설정 부분---
      fetch('/api/posts/#{post.shortId}/comments', {
        method: 'post',
        headers: { 'Content-Type': 'application/json'},
        body: JSON.stringify({ content }),
      })
        .then(() => {
        if (res.ok) {
        	input.value= '';
        		loadComments();
        } else {                       
          alert('오류가 발생 했습니다.');
        }
      });

(2) 댓글 목록 불러오기

---posts/view.pug---
// 댓글목록 api 호출
script.
	loadComments(); //페이지 불러왔을때 댓글목록 불러옴. 
	function loadComments() {
      document
        .querySelector('#comments')
        .innerHTML= ''; // 이전 목록 삭제
//작성한 comments api 호출
      fetch('/api/posts/#{post.shortId}/comments')
        .then((res) => {
        if (res.ok) {
// 호출한 comments들을 json화
          return res.json();
        } else {
          throw new Error('댓글을불러오지못했습니다');
        }
      })
//json으로 변환 성공시 댓글들을 순환하며 출력
        .then((comments) => {
        comments.forEach(addComment);
      });
// 오류 발생시 캐치
      	.catch((err) => alert(err.message));
    }

// HTMLTemplate 사용하여 댓글 화면에 출력
function addComment(comment) {
  const template = document.querySelector('#comment-template');
//importNode : 노드를 만드는 함수. 아래는 만든 노드 구성.
  const node = document.importNode(template.content, true);
//댓글 내용
  node.querySelector('.content').textContent= comment.content;
//댓글 작성자
  node.querySelector('.author').textContent= comment.author.name;
//댓글 작성 시간
  node.querySelector('.createdAt').textContent= comment.createdAt;

//위의 코드들을 통해 만든 노드를 comments에 붙임
  document.querySelector('#comments').appendChild(node);
}
profile
나는 나의 섬이다.

0개의 댓글