[TIL] 210706

오늘은 게시물들을 listing 채널페이지에서 listing해주고 해당 게시물을 클릭하면 들어가서 글내용이랑 댓글기능과 조회수를 구현해봤다.

-------🖥️ Frontend-------

📂 채널 게시물들 listing

페이지 로딩이 되면 해당 채널에 있는 포스트만 들고와서 tempHtml로 붙혀줬다. 그리고 글쓰기도 로그인을 해야 쓸수있도록 token을 확인해서 없으면 로그인페이지로 리다이렉트 시켜줬다.

$(document).ready(function () {
        listingPost();
      });

      function listingPost() {
        $('#postList').empty();
        $.ajax({
          type: 'GET',
          url: `posts/list/${channel}`,
          data: {},
          success: function (response) {
            const post = response['post'];

            for (let i = 0; i < post.length; i++) {
              makePost(post[i]);
            }
          },
        });
      }

      function makePost(post) {
        let posthtml = `...`;
        $('#postList').append(posthtml);
      }

function writeCheck() {
        if (!localStorage.getItem('token')) {
          alert('로그인후 사용가능합니다.');
          window.location.replace(`/login`);
        } else {
          window.location.href = `/write?channel=${channel}`;
        }
      }

📂 게시물 읽기

이 페이지는 아마 프론트개발자분들이 보고 경악할수있을정로 정말정말정말 비효율적으로 원시인마냥 코드를 짰다... 일단 들어가면 이 세가지가 실행된다.

$(document).ready(function () {
  channelHead();
  userCheck();
  readComment();
});

channelHead는 어제 write.html에서 쓴거랑 똑같고 2번째부터 설명하자면,
원래 같으면 그냥 게시물 데이터만 가져와서 보여줄것이다. 하지만 이 게시물을 보여주는동시에 로그인한 유저의 id를 가지고 조회수를 올리는 기능을 구현해놨다( 서버쪽에서도 게시물 listing해주는 api안에 같이 해놨다 ). 그래서 로그인한 유저가 아니면 getSelf함수에서 막혀서 해당게시물의 데이터를 못불러온다.
그러므로 로그인한 유저가 아니면 그냥 게시물을 보여주는 readPostUnuser 함수를 실행하고
로그인한 유저면 readPost함수를 실행하게했다. 이 기능 구현을 다하고 생각이 떠올랐는데, 조회수 api를 서버도 프론트도 따로 꺼내서 따로 만들었으면 됐을 문제인거다.. (진짜 멍청이다)

function userCheck() {
  if (localStorage.getItem('token')) {
    readPost();
  } else {
    readPostUnuser();
  }
}

function readPostUnuser() {
  $('#postDetails').empty();
  $.ajax({
    type: 'GET',
    url: `posts/read/${postId}`,
    data: {},
    success: function (response) {
      const post = response['post'][0];
      $('#titlePost').text(post['title']);
      let posthtml = `...`;
      $('#postDetails').append(posthtml);
    },
  });
}

function readPost() {
  getSelf(function (user) {
    $('#postDetails').empty();
    const userId = user.id;
    $.ajax({
      type: 'GET',
      url: `posts/read/${postId}/${userId}`,
      data: {},
      success: function (response) {
        const post = response['post'][0];
        $('#titlePost').text(post['title']);
        let posthtml = ` ...`;
        $('#postDetails').append(posthtml);
      },
    });
  });
}

📂 댓글 달기

댓글달기도 게시물쓰는거랑 비슷하게 또!! getSelf 불러와서 user.id 와 user.nickname을 가져와서 쓴 댓글내용과 같이 서버쪽으로 보내줬다. 댓글도 토큰이 없으면 못쓰게 login.html 로 보내진다.

function writeComment() {
  getSelf(function getCommentData(user) {
    let comment = $('#newComment').val();
    let userId = user.id;
    let nickname = user.nickname;

    $.ajax({
      type: 'POST',
      url: '/comments/write',
      data: {
        comment: comment,
        postId: postId,
        userId: userId,
        nickname: nickname,
      },
      success: function (response) {
        alert('쓰기완료');
        window.location.reload();
      },
      error: function (error) {
        alert(error.responseJSON.errorMessage);
      },
    });
  });
}

function getSelf(callback) {
  $.ajax({
    type: 'GET',
    url: '/users/info',
    headers: {
      authorization: `Bearer ${localStorage.getItem('token')}`,
    },
    success: function (response) {
      callback(response.user);
    },
  });
}

function commentCheck() {
  if (!localStorage.getItem('token')) {
    alert('로그인후 사용가능합니다.');
    window.location.replace(`/login.html`);
  }
}

📂 댓글 listing

대망의 댓글 기능이다. 이것도 로그인한 유저와 로그인을 안한 유저를 나눴다 왜냐하면 댓글의 수정과 댓글 기능을 넣어야하는데 처음부터 그냥 모든 댓글에 수정과삭제 버튼을 달아놓고 그 버튼을 누른 사람과 그 댓글을 쓴 유저랑 비교해서 틀리면 못하게 막으면 되지만!
그럼 깔끔하지 못하고 사용자도 상당히 불편할거라 생각해서 자기가 쓴 댓글에만 수정삭제 버튼이 나오게끔 하려고 한것도 있고
해당 유저아이디랑 일치하는 댓글을 판별하고 보여주려면 지금 보고 있는 유저의 id를 알아야하기때문에 또 로그인 안한 유저가 들어오면 id판별 함수에서 막혀버리고 댓글을 아예 보여줄수가 없게 되버려서 로그인한 유저와 로그인을 안한유저에게 보여주는걸 따로 만들었다. (그냥 봐도 비효율적으로 보인다. 프론트 힘들다..)

function readComment() {
  $('#pastComment').empty();
  $.ajax({
    type: 'GET',
    url: `comments/read/${postId}`,
    data: {},
    success: function (response) {
      const comment = response['comment'];
      if (!localStorage.getItem('token')) {
        for (let i = 0; i < comment.length; i++) {
          makeComment(comment[i]);	//로그인을 안한 유저를 위한 댓글 listing
        }
      } else {
        getUserData(comment);	//로그인을 한 유저를 위한 댓글 listing
      }
    },
  });
}

function getUserData(comment) {
  $.ajax({
    type: 'GET',
    url: '/users/info',
    headers: {
      authorization: `Bearer ${localStorage.getItem('token')}`,
    },
    success: function (response) {
      user = response['user'];
      for (let i = 0; i < comment.length; i++) {
        makeCommentUser(comment[i], user);
      }
    },
  });
}

function makeComment(comment) {
  let commentHtml = `...`;
  $('#pastComment').append(commentHtml);
}

function makeCommentUser(comment, user) {
  if (comment['userId'] === user.id) {
    let commentHtml1 = `...
                      <button style="float:right; margin-right: 10px" onclick="openEdit(${comment['order']})">수정</button>
                      <button style="float:right" onclick="deleteComment(${comment['order']})">삭제</button>
                      ...`;
    $('#pastComment').append(commentHtml1);
  } else {
    let commentHtml2 = `...`;
    $('#pastComment').append(commentHtml2);
  }
}

-------⚙️ Backend-------

서버는 너무 편안하게 그냥 해당 게시물 아이디를 확인해서 해당 게시물 정보를 보내줬다.

router.get('/read/:postId', async (req, res) => {
  const { postId } = req.params;
  const posts = await Post.find({ _id: postId });
  res.send({ post: posts });
});

📂 조회수

그리고 이게 로그인한 유저를 위한 게시물 데이터 읽어오는건데 읽어오는거는 위랑 비슷하니 조회수 올리는 api를 설명해야겠다. 먼저 "밖에" 객체 하나를 만든다. 이거는 중복 조회수를 없애려고 게시물을 조회한 해당 유저의 id를 que 형식의 데이터 구조로 넣어주고 설정해 놓은 시간이 지나면 빼주려고 만들었다. (밑에 주석으로 설명)

const viewObj = new Object();

router.get('/read/:postId/:userId', async (req, res) => {
  const { postId, userId } = req.params;
  const posts = await Post.find({ _id: postId });

  if (!viewObj[postId]) {	//해당 객체에 이 글의 postId가 없다면 넣어준다.
    viewObj[postId] = [];
  }

  if (viewObj[postId].indexOf(userId) == -1) { //만약 이글을 보는 유저의 id가 없다면
    viewObj[postId].push(userId); // 해당 postId에 넣어준다.
    posts[0].view++;	// 그리고 조회수 1 증가

    setTimeout(() => {	// 보고 나가거나 새로고침을 했을때 바로 빼주는게 아니라 시간을 걸어준다.
      viewObj[postId].splice(viewObj[postId].indexOf(userId), 1);
    }, 600000); // 60만 밀리초 = 10분이 지나면 해당 postId에서 유저 id를 뺀다.
				// 해당 게시물 조회수를 1증가한 값으로 update 해준다.
    await Post.updateOne({ _id: postId }, { $set: { view: posts[0].view } });
  }

  res.send({ post: posts });
});

📂 댓글

서버쪽의 댓글 api이다. 프론트는 지금 전쟁터인데 백에서는 지금 평화롭게 그냥 댓글 적으면 해당 유저 id가지고 와서 같이 저장해주면되고 댓글 listing 해주는것도 그 게시물 id가 가지고 있는 comments만 들고 와서 보여주면 된다 끝.

router.post('/write', async (req, res) => {
  const { comment, userId, postId, nickname } = req.body;

  if (comment === '') {
    res.status(400).send({
      errorMessage: '댓글을 작성해주세요.',
    });
    return;
  }
  const maxOrder = await Comment.findOne({ postId }).sort('-order').exec();
  let order = 1;

  if (maxOrder) {
    order = maxOrder.order + 1;
  }
  const date = moment().format('YYYY-MM-DD HH:mm:ss');
  const commentContext = new Comment({
    comment,
    userId,
    postId,
    nickname,
    order,
    date,
  });
  await commentContext.save();
  res.status(201).send();
});


router.get('/read/:postId', async (req, res) => {
  const { postId } = req.params;
  const comments = await Comment.find({ postId: postId });

  res.send({ comment: comments });
});

좋은 웹페이지 즐겨찾기