Almon Dev

게시판 만들기 #4 (게시글 읽기) 본문

모의해킹/웹 개발

게시판 만들기 #4 (게시글 읽기)

Almon 2024. 11. 12. 13:51

게시판 만들기

 

게시글 읽기

 

read_post.js

function readPost() {
  document.querySelectorAll('.td-title').forEach((tdTitle) => {
    tdTitle.addEventListener('click', (e) => {
      const post_id = e.target
        .closest('tr')
        .querySelector('.td-id').textContent;
      console.log(post_id);
      const pageNum = e.target.dataset.page_num;
      console.log(pageNum);

      const categoryId = e.target.dataset.category_id;
      const categoryName = e.target.dataset.category_name;
      const url = '/forum/db/read_post.php';

      fetch(url, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
        },
        body: JSON.stringify({
          post_id: post_id,
        }),
      })
        // .then((res) => res.text())
        // .then((res) => console.log(res));
        .then((res) => res.json())
        .then((res) => {
          const postContainer = document.querySelector('.post-container');
          const postContent = document.querySelector('.post-content');
          const post = res.post;
          const imgPath =
            '../upload/profile_img/' +
            (post.profile_img_path ?? 'almond-profile.jpg');

          const articleHeader = document.createElement('div');
          articleHeader.classList.add('article-header');

          const articleTitle = document.createElement('h3');
          articleTitle.classList.add('article-title');
          articleTitle.textContent = post.title;

          const writerInfo = document.createElement('div');
          writerInfo.classList.add('writer-info');

          const writerImgDiv = document.createElement('div');
          writerImgDiv.classList.add('writer-img-wrapper');
          const writerImg = document.createElement('img');
          writerImg.classList.add('writer-img');
          writerImg.setAttribute('src', imgPath);

          const profileWrapper = document.createElement('div');
          profileWrapper.classList.add('profile-wrapper');

          const writerNameDiv = document.createElement('div');
          writerNameDiv.classList.add('writer-name-wrapper');
          const writerName = document.createElement('button');
          writerName.classList.add('writer-name');
          writerName.textContent = post.nickname;

          const articleInfo = document.createElement('div');
          articleInfo.classList.add('article-info');
          const date = document.createElement('span');
          date.classList.add('article-date');
          date.textContent = post.created_at.slice(0, -3);
          const views = document.createElement('span');
          views.classList.add('article-views');
          views.textContent = '조회 ' + post.views;

          const btnArea = document.createElement('div');
          btnArea.classList.add('action-btn-area');

          if (res.isWriter) {
            const updateBtn = document.createElement('a');
            updateBtn.textContent = '수정';
            updateBtn.setAttribute(
              'onclick',
              `updatePost(${categoryId}, '${post.title}', ${JSON.stringify(
                post.content
              )}, ${post.post_id})`
            );
            updateBtn.classList.add('update-btn');

            const deleteBtn = document.createElement('a');
            deleteBtn.textContent = '삭제';
            deleteBtn.setAttribute(
              'onclick',
              `deletePost(event, ${post_id}, ${categoryId}, '${categoryName}')`
            );
            deleteBtn.classList.add('delete-btn');

            btnArea.appendChild(updateBtn);
            btnArea.appendChild(deleteBtn);
          }

          const listBtn = document.createElement('a');
          listBtn.textContent = '목록';
          listBtn.classList.add('list-btn');
          listBtn.setAttribute(
            'onclick',
            `postList(${categoryId}, '${categoryName}', ${pageNum})`
          );
          btnArea.appendChild(listBtn);

          // 댓글 추가 나중에
          const comment = document.createElement('a');
          comment.classList.add('comment-btn');

          const contentWrapper = document.createElement('div');
          contentWrapper.classList.add('content-wrapper');
          post.content.split('<br />').forEach((text) => {
            const p = document.createElement('p');
            p.classList.add('content-text');
            p.textContent = text;
            contentWrapper.appendChild(p);
          });

          writerImgDiv.appendChild(writerImg);

          writerNameDiv.appendChild(writerName);

          articleInfo.appendChild(date);
          articleInfo.appendChild(views);

          profileWrapper.appendChild(writerNameDiv);
          profileWrapper.appendChild(articleInfo);

          writerInfo.appendChild(writerImgDiv);
          writerInfo.appendChild(profileWrapper);

          articleHeader.appendChild(articleTitle);
          articleHeader.appendChild(writerInfo);
          articleHeader.appendChild(btnArea);

          closePost();
          postContent.appendChild(articleHeader);
          postContent.appendChild(contentWrapper);

          postContainer.classList.add('on');
        });
    });
  });
}

 

      const post_id = e.target
        .closest('tr')
        .querySelector('.td-id').textContent;

클릭한 태그(.td-title)의 부모태그인 tr을 찾아서 게시글 번호를 불러와 post_id에 저장합니다.

post_id를 post로 read_post.php에 요청을 보냅니다.

응답을 받으면 태그를 생성하고 closePost()를 이용해 생성돼 있던 태그를 삭제한 뒤에 다시 추가해 줍니다.

 

          if (res.isWriter) {
            const updateBtn = document.createElement('a');
            updateBtn.textContent = '수정';
            updateBtn.setAttribute(
              'onclick',
              `updatePost(${categoryId}, '${post.title}', ${JSON.stringify(
                post.content
              )}, ${post.post_id})`
            );
            updateBtn.classList.add('update-btn');

            const deleteBtn = document.createElement('a');
            deleteBtn.textContent = '삭제';
            deleteBtn.setAttribute(
              'onclick',
              `deletePost(event, ${post_id}, ${categoryId}, '${categoryName}')`
            );
            deleteBtn.classList.add('delete-btn');

            btnArea.appendChild(updateBtn);
            btnArea.appendChild(deleteBtn);
          }

작성자와 사용자가 같으면 수정과 삭제버튼을 추가합니다.

 

          const listBtn = document.createElement('a');
          listBtn.textContent = '목록';
          listBtn.classList.add('list-btn');
          listBtn.setAttribute(
            'onclick',
            `postList(${categoryId}, '${categoryName}', ${pageNum})`
          );

목록 버튼을 누르면 postList()함수를 실행해 다시 게시글 목록 페이지로 넘어갑니다.

 

read_post.php

<?php
require_once("../../jwt_auth.php");
require_once("../../mysql.php");

if($token = Jwt_auth::auth()) {
    $post_id = json_decode(file_get_contents("php://input"), true)['post_id'];
    $user_id = $token->sub;
    $isWriter = false;
    if($post_id) {
        $sql = "select posts.post_id, posts.title, posts.content, posts.created_at, posts.views, posts.writer_id, users.profile_img_path, users.nickname, users.user_id
        from posts 
        join users on posts.writer_id = users.id
        where post_id=$post_id";
        $post = runSQL($sql)->fetch_array();
        if($post['user_id'] == $user_id) {
            $isWriter = true;
        }

        $updateSql = "update posts set views = views + 1 where post_id=$post_id";
        runSQL($updateSql);

        echo json_encode( ["post" => $post , "isWriter"=> $isWriter]);
    }
}else {
    echo json_encode(["token" => false]);
}
        $sql = "select posts.post_id, posts.title, posts.content, posts.created_at, posts.views, posts.writer_id, users.profile_img_path, users.nickname, users.user_id
        from posts 
        join users on posts.writer_id = users.id
        where post_id=$post_id";
        $post = runSQL($sql)->fetch_array();

select join을 이용해서 posts테이블의 writer_id와 users테이블의 id가 같은 경우의 users테이블 정보 역시 가져옵니다.

 

        $updateSql = "update posts set views = views + 1 where post_id=$post_id";
        runSQL($updateSql);

게시글의 조회수를 1 증가시킵니다.

 

        echo json_encode( ["post" => $post , "isWriter"=> $isWriter]);

db에서 가져온 포스트와 작성자에 대한 정보를 json으로 인코딩해서 응답합니다.

 

article.css

.post-content {
  padding: 20px 29px 0;

  border: 1px solid rgba(200, 200, 200, 0.3);
  border-radius: 6px;
}

.article-header {
  margin-bottom: 20px;
  padding-bottom: 20px;
  height: 128px;

  border-bottom: 1px solid rgba(200, 200, 200, 0.3);

  position: relative;
}

.article-title {
  height: 58px;
}

.writer-info {
  height: 38px;

  display: flex;
  flex-direction: row;
}

.writer-img-wrapper {
  margin-right: 10px;
  height: 36px;
}

.writer-img {
  width: 36px;

  border: 1px solid #d2b48c;
  border-radius: 50%;
}

.writer-name-wrapper {
  height: 17px;

  margin-bottom: 6px;
}

.writer-name {
  height: 17px;

  font-size: 13px;
  font-weight: 700;
}

.action-btn-area {
  height: 36px;

  position: absolute;
  right: 0;
  bottom: 30px;

  font-size: 13px;
  line-height: 36px;
}

.action-btn-area a {
  margin-left: 10px;
  padding: 0 12px;
  min-width: 46px;
  height: 36px;

  display: inline-block;

  color: #3e2c1c;
  font-weight: 700;
  background-color: rgba(200, 176, 141, 0.5);

  border: 1px solid rgba(200, 200, 200, 0.3);
  border-radius: 6px;
}

.action-btn-area a:hover {
  cursor: pointer;
  /* text-decoration: underline; */
}

.content-wrapper {
  height: 680px;
  overflow: auto;
}

.content-text {
  margin-bottom: 7px;

  color: #212121;
  height: 21px;
}

 


마무리