게시판 - 글번호, 조회수 표시하기

소스코드

이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.

이 게시물의 소스코드는 게시판 만들기(고급) / 게시판 - 댓글 기능 만들기 4 (댓글 수 표시)에서 이어집니다.

board.git 을 clone 한 적이 있는 경우: 터미널에서 해당 폴더로 이동 후 아래 명령어들을 붙여넣기합니다. 폴더 내 모든 코드가 이 게시물의 코드로 교체됩니다. 이를 원치 않으시면 이 방법을 선택하지 마세요.

git reset --hard
git pull
git reset --hard 28580df
git reset --soft 39225f3
npm install
atom .

board.git 을 clone 한 적이 없는 경우: 터미널에서 코드를 다운 받을 폴더로 이동한 후 아래 명령어들을 붙여넣기하여 board.git 을 clone 합니다.

git clone https://github.com/a-mean-blogger/board.git
cd board
git reset --hard 28580df
git reset --soft 39225f3
npm install
atom .

- Github에서 소스코드 보기: https://github.com/a-mean-blogger/board/tree/28580dff31e3ebfa5a8efa375294a83fde6c1c69


게시판에 글번호와 조회수를 추가해 봅시다. 이 두가지는 각각 다른 내용이지만 조회수가 너무 간단한 내용이라 한번에 진행하겠습니다.

이전에 작성된 게시물들에는 글번호와 조회수가 존재하지 않으므로 이전까지 작성된 게시물들은 모두 지운 후에 진행해주세요.

우선 조회수를 어떻게 구현할 수 있을지 한번 생각해 봅시다.

게시물이 조회될 때마다(조건) 조회수가 1씩 올라갑니다.(할일)

현재 프로젝트에서 '게시물이 조회된다'는 조건의 행위가 일어나는 부분을 찾아서 조회수를 1씩 올려주면 됩니다. 현재는 '조회수'라는 것이 게시물에 존재하지 않으니 post schema에 추가해 주면 되겠지요.

반면 글번호는 조금 복잡합니다. SQL계열 DB의 경우 데이터의 id를 숫자 1부터 순서대로 줄 수 있어서, 글번호로 이 id를 사용하면 쉽게 구현할 수 있습니다. 하지만 mongo DB의 경우 보안상의 이유로 이것을 허용하지 않습니다. 데이터의 id는 데이터를 특정할 수 있는 중요한 정보인데, 이러한 정보를 쉽게 예측할 수 있게 순서를 따르는 숫자로 할 수 없다는 것이죠.

때문에 게시물의 번호를 저장할 새로운 collection을 사용해야 합니다. 직접 코드를 보면서 자세히 설명하겠습니다.

폴더 구조

코드 - js

// models/Counter.js

var mongoose = require('mongoose');

// schema
var counterSchema = mongoose.Schema({
  name:{type:String, required:true},
  count:{type:Number, default:0},
});

// model & export
var Counter = mongoose.model('counter', counterSchema);
module.exports = Counter;

Counter 모델의 코드입니다. Counter는 '{name:'posts',count:0}'라는 데이터 단 하나만을 가질 예정이며, 새 게시물이 생성될 때마다 게시물은 이 값을 읽어와서 1을 더한 후 그 값을 게시물번호로 사용하고, 'posts' counter의 count값을 1 증가시키게 됩니다.

우리는 현재 'posts'만 사용하지만, 만약 다른 collection의 번호를 저장하고자 한다면 counters collection을 사용하면 되겠습니다.

// models/Post.js

var mongoose = require('mongoose');
var Counter = require('./Counter');

// schema
var postSchema = mongoose.Schema({
  title:{type:String, required:[true,'Title is required!']},
  body:{type:String, required:[true,'Body is required!']},
  author:{type:mongoose.Schema.Types.ObjectId, ref:'user', required:true},
  views:{type:Number, default:0}, // 1
  numId:{type:Number}, // 2
  createdAt:{type:Date, default:Date.now},
  updatedAt:{type:Date},
});

postSchema.pre('save', async function (next){ // 3
  var post = this;
  if(post.isNew){
    counter = await Counter.findOne({name:'posts'}).exec();
    if(!counter) counter = await Counter.create({name:'posts'});
    counter.count++;
    counter.save();
    post.numId = counter.count;
  }
  return next();
});

// model & export
var Post = mongoose.model('post', postSchema);
module.exports = Post;

Post 모델에 조회수, 글번호를 위한 코드를 추가합니다.

1. Counter 모델을 사용하기 위해 require합니다.

2. 조회수를 위한 views항목을 추가했습니다.

3. 글번호를 위한 numId항목을 추가했습니다.

4. 이전 강의에도 설명했듯이 Schema.pre함수는 첫번째 파라미터로 설정된 event가 일어나기 전(pre)에 먼저 callback 함수를 실행시킵니다. "save" event는 Model.create, model.save함수 실행시 발생하는 event입니다.

새 post가 생성되는 경우(post.isNew가 true)에만 'posts' counter를 읽어오고, 숫자를 증가시키고, post의 numId에 counter를 넣어줍니다. 만약 'posts' counter가 존재하지 않는다면 생성합니다.

// routes/post.js

...

// Index
router.get('/', async function(req, res){
      ...
      { $project: {
          title: 1,
          author: {
            username: 1,
          },
          views: 1, // 1
          numId: 1, // 1
          createdAt: 1,
          commentCount: { $size: '$comments'}
      } },
    ...

// show
    ...
    .then(([post, comments]) => {
      post.views++; // 2
      post.save();  // 2
      var commentTrees = util.convertToTrees(comments, '_id','parentComment','childComments');
      res.render('posts/show', { post:post, commentTrees:commentTrees, commentForm:commentForm, commentError:commentError});
    })
    ...

1. post 모델의 schema에 views와 numId가 추가되었으므로 post index route의 aggregation 속 $project에도 해당 항목들이 표시될 수 있도록 수정해 줍니다.

2. 게시물이 조회될 때마다 post.views를 1 증가시키고, 저장해줍니다.

코드 - ejs

// views/posts/index.ejs

      ...

        <thead class="thead-light">
          <tr>
            <th scope="col" class="numId">#</th> <!-- 1 -->
            <th scope="col">Title</th>
            <th scope="col" class="views">Views</th>  <!-- 2 -->
            <th scope="col" class="author">Author</th>
            <th scope="col" class="date">Date</th>
          </tr>
        </thead>

        <tbody>
          <% if(posts == null || posts.length == 0){ %>
            <tr>
              <td colspan=5> There is no data to show :( </td>  <!-- 3 -->
            </tr>
          <% } %>
          <% posts.forEach(function(post) { %>
            <tr>
   <!-- 1 --> <td class="numId"><%= post.numId %></td>
              <td>
                <a href="/posts/<%= post._id %><%= getPostQueryString() %>" class="title-container">
                  ...
                </a>
              </td>
   <!-- 2 --> <td class="views"><%= post.views %></td>
              <td class="author">
                ...

1. post 테이블에 게시물 번호를 추가합니다.

2. post 테이블에 조회수를 추가합니다.

3. 테이블의 column 수가 늘어났으므로 게시물이 없을 때 표시될 문구의 colspan의 수를 column 수와 맞춰줍니다.

// views/posts/show.ejs
    ...

      <nav aria-label="breadcrumb">
        <ol class="breadcrumb p-1 pl-2 pr-2">
          <li class="breadcrumb-item"><a href="/">Home</a></li>
          <li class="breadcrumb-item"><a href="/posts">Board</a></li>
          <li class="breadcrumb-item active" aria-current="page"><%= post.numId %>. <%= post.title %></li> <!-- 1 -->
        </ol>
      </nav>

      <div class="card">
        <h5 class="card-header p-2" data-search-highlight="title"><%= post.numId %>. <%= post.title %></h5> <!-- 1 -->
        <div class="row">

          ...
            <div class="post-info card m-2 p-2">
              <div class="border-bottom pb-1 mb-1">
                <div><span>Author</span> : <span data-search-highlight="author"><%= post.author ? post.author.username : "" %></span></div>
     <!-- 2 --> <div><span>Views</span> : <span><%= post.views %></span></div>
              </div>
              <div><span>Created</span> : <span data-date-time="<%= post.createdAt %>"></span></div>
              ...

1. 게시물 번호를 post-show view에 추가합니다. 

2. views 역시 추가해 줍니다.

코드 - css

/* public/css/master.css */

...

.board-table {
  table-layout: fixed;
}
.board-table .numId, /* 1 */
.board-table .views{
  width: 60px;
  text-align: center;
}
.board-table .author,
.board-table .date {
  width: 100px;
  text-align: center; /* 2 */
}
.board-table .title-container:hover .title-comments,
...

1. 새로 추가된 'numId', 'views'에 대한 class가 추가되었습니다.

2. 기존의 'author', 'date' class에도 가운데 정렬을 추가했습니다.

실행 결과

post index view에 글번호와 조회수가 제대로 표시되는지 확인합시다.


post show view에 글번호와 조회수가 제대로 표시되는지 확인합시다.

마치며...

크게 어려운 부분은 없는 강의였다고 생각합니다. 수고하셨습니다!

댓글

댓글쓰기

이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기

UP