글 작성자: 망고좋아
반응형

🎯 CSR로 댓글 기능 구현하기

📝 CSR을 구현하는 방법

  1. 페이지 로드 시 필요한 리소스를 클라이언트에 선언
  2. 클라이언트에서 필요한 데이터를 비동기 호출
  3. 클라이언트가 전달받은 데이터를 가공, 리소스를 사용하여 화면에 표시

 

📝 클라이언트에 리소스 선언 - HTML Template

  • 클라이언트에 리소스를 선언하기 위한 다양한 방법이 존재. (React.js, Vue.js 등)
  • 본 강의에서는 간단하게 HTML Template 기능을 사용
  • HTML Template 은 브라우저에 표시되지 않는 HTML Element를 작성 해 두고, JavaScript로 이를 화면에 반복적으로 그릴 수 있게 하는 기술

 

📝 댓글 화면 작성하기

🛠 posts/view.pug

...
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
  • 게시글 상세 화면 하단에 댓글 작성, 목록 화면 추가
  • HTML Template 사용하여 한 개의 댓글이 표시될 모양을 선언
  • JavaScript로 조작하기 위해 id, class를 선언하는 것이 유용

 

📝 데이터 비동기 호출 - API 작성하기

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

 

📝 게시글에 댓글 추가하기

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

// -----------------------
const PostSchema = new Schema({
    ...
    comments: [CommehtSchema],
    ...
  • mongoose의 sub-schema를 이용하여 Post 스키마에 Comment를 배열로 추가
  • populate를 사용할 때, ObjectID만 저장하는 것 과는 다르게 Comment의 내용을 게시글이 포함하게 됨
  • sub-schema 내부에서도 populate 가능

 

📝 API 작성하기 - 댓글 작성

...
router.post('/posts/:shortId/comments', ... {
    const { shortId } = req.params;
    const { content } = req.body;
    const author = await User
        .findOne({ shortId: req.user.shortId });

    await Post.updateOne({ shortId }, {
        $push: { comments: {
            content,
            author,
        }},
    });

    res.json({ result: 'success' });
});
...
  • api 라우터를 추가하고, RESTful 하게 api/posts/{postId}/comments 경로로 댓글 작성 기능 구현
  • 게시글 업데이트 시 $push를 사용하여 comments 배열에 새로 작성된 댓글 추가 →동시에 들어오는 요청에 대해 정확하게 처리하는 부분
  • api는 render 하지 않고 json으로 응답

 

📝 API 작성하기 - 댓글 목록

...
router.get('/posts/:shortId/comments', ... {
    const { shortId } = req.params;
    const post = await Post
        .findOne({ shortId });

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

    res.json(post.comments);
});
...
  • /api/posts/{postId}/comments로 RESTful 경로 설정
  • find에 populate 하지 않고 User (model)의 populate를 사용하는 방법도 가능하다.

 

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

  • 브라우저는 비동기 HTTP 요청을 fetch 함수를 이용해 제공한다.
  • jQuery의 ajax와 유사한 기능, jQuery를 사용하지 않고도 HTTP 요청 구현 가능
  • fetch를 이용하면 간단하게 JavaScript로 HTTP 요청을 비동기 처리할 수 있다.

 

📝 fetch로 API 호출하고 처리하기 - 댓글 작성하기

...
script.
    function writeComment() {
        const input = document.querySelector('#content')
        const content = input.value;
        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('오류가 발생했습니다.');
        }
    });
  • 댓글 작성 버튼 클릭 시 writeComment() 실행
  • input#content에서 내용을 읽어 fetch로 댓글 작성 api 호출
  • 호출 결과의 성공 여부를 확인하여, 댓글 다시 불러오기 실행

 

📝 fetch로 API 호출하고 처리하기 - 댓글 목록 불러오기

// 댓글 목록 api 호출하기
script.
    loadComments();
    function loadComments() {
        document.querySelector('#comments')    .innerHTML = ''; // 이전 목록 삭제
        fetch('/api/posts/#{post.shortId}/comments')
            .then((res) => {
                if (res.ok) {
                    return res.json();
                } else {
                    throw new Error('댓글을 불러오지 못했습니다');
                }
            })
            .then((comments) => {
                comments.forEach(addComment);
            });
            .catch((err) => alert(err.message));
}
// HTML Template 사용하여 댓글 화면에 표시하기
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.author.name;
    node.querySelector('.createdAt').textContent = comment.createdAt;
    document.querySelector('#comments').appendChild(node);
}
반응형

'프로그래밍 > Node.js' 카테고리의 다른 글

[Node.js] JWT + Cookie 사용하기  (0) 2021.12.15
[Node.js] JWT란?  (0) 2021.12.15
[Node.js] 회원과 게시글의 연동  (0) 2021.12.13
[Node.js] Session Store 사용하기  (0) 2021.12.13
[Node.js] Passport.js와 로그인  (0) 2021.12.13