Node.js - CSR로 댓글 작성/수정 구현하기

ryan·2022년 5월 20일
0

구현 순서

  1. 페이지 로드 시 필요한 리소스를 클라이언트에 선언
  2. 클라이언트에서 필요한 데이터를 서버에 비동기 호출
  3. 클라이언트가 전달받은 데이터를 가공, 리소스를 사용하여 화면에 표시
    화면에 표시할 때 클라이언트가 대부분의 리소스를 가지고 데이터만 붙여서 표시하는 방식

HTML-Template

  • 클라이언트 리소스를 선언하기 위한 다양한 방법이 존재(react.js 등)
  • html template은 브라우저에 표시되지 않는 html element를 작성해두고 js로 이를 화면에 반복적으로 그릴 수 있게 하는 기술
view.pug
댓글 작성, 목록화면 추가
html template 사용하여 한개의 댓글이 표시될 모양을 선언
table 
    thead 
        tr 
            td(colspan='2')
                input#content(type="text")
            td:button(onclick="writeComment()")
                댓글 작성
    tbody#commnets
template#comment-template
    tr
        td.content
        td.author
        td.createdA

데이터 비동기 호출

  • API 작성하기
  • 지금까지의 구현은 HTTP 응답으로 HTML을 전송하는 방식
  • CSR을 구현하기 위해서는 HTML이 아닌, 데이터만 주고 받을 수 있는 API를 구성해야 함(JSON 사용)
  • 댓글 작성 시 댓글 목록을 다시 불러와 그리는 형식으로 구현

fetch로 클라이언트에서 api호출하기

  • fetch를 이용하면 간단하게 js로 http요청을 비동기 처리할 수 있음

fetch로 api 호출하기

script 
    fucntion writeComment(){  // 댓글 작성 버튼 클릭시 writeComment 실행
        const input = document.querySelector('#content')
        const content = input.value;
        fetch('/api/posts/#{post.shortId}/comments',{ 
          // input#content에서 내용을 읽어 fetch로 댓글 작성 api 호출
            method:'post',
            header:{'Content-Type':'application/json'},
            body:JSON.stringify({content}),
        })
        .then(()=>{ 
          // 호출 결과의 성공 여부를 확인하여, 댓글 불러오기 진행
            if(res.ok){
                input.value=''; // 댓글 입력칸 초기화
                    loadComments(); // 댓글 불러오가
            } else{
                alert('error')
            }
            })
        }

댓글 작성 api

  • 게시글 업데이트 시 $push를 사용하여 comments 배열에 새로 작성된 댓글 추가 > 동시에 들어오는 요청에 대해 정확하게 처리
  • api는 render하지 않고 json으로 응답
router.post('/posts/:shortId/commnets',(req,res,next)=>{
    const {shortId} = req.params;
    const {content} = req.body;
    const author = await User.findOne({shortId:req.user.shortId})
    await Post.updateOne({shortId},{
        $push:{ //push operator를 사용하여 하나씩 db에 추가함
            comments:{
                content,author,
            }
        }
    })
    res.json({result:'success'}) // api는 render하지 않고 json으로 응답
})

댓글 목록 불러오기

script 
    loadComments();
    fucntion loadComments(){ 
        document
            .querySelector('#commnets')
            .innerHTML = '';
        fetch('/api/posts/#{post.shortId}/comments')
            .then((res)=>{
                if(res.ok){
                    return res.json()  // 받는 데이터 형식은 json으로 통일
                } else {
                    throw new Error('error')
                }
            })
            .then((comments)=>{
                comments.forEach(addComment)
            });
            .catch((err)=> alert(err.meesage))
        }

댓글 목록 api

  • // find에 populate하지 않고 User(model)의 populate를 사용하는 방법도 가능
router.get('/posts/:shortId/commnets',(req,res,next)=>{
    const {shortId} = req.params;
    const post = await Post.findOne({shortId});

    await User.populate(post.comments,{
        path:'author'
    });

    res.json(post.commnets);
})

댓글 화면에 표시

function addComment(comment){
    const template = document.querySelector('#comment-template');
    const node = document.importNode(template.content,true);
    node.querySelector('.content').textContent = comment.content
    node.querySelector('.author').textContent = comment.name 
    node.querySelector('.createdAt').textContent = comment.createdAt
    document.querySelector('#comments').appendChild(node)
}

댓글 스키마

  • mongoose의 sub-schema를 이용하여 Post 스키마에 comment를 배열로 추가
  • populate를 사용할 때 oid만 저장하는 것과는 다르게 comment의 내용을 게시글이 포함하게 됨.
const CommentSchema = new Schema(
  {
    content: String,
    author: {
      type: Schema.Types.ObjectId,
      ref: 'User',
    },
  },
  {timestamps: true}
);

const PostSchema = new Schema({
  comments: [CommentSchema],
});
profile
프론트엔드 개발자

0개의 댓글