node.js

node.js와 mongodb를 이용해 간단한 게시판 만들기

RYUHA/ editor

  • 16 comments
  • 13,214 views
  • 2018년 4월 16일

node.js와 mongodb를 이용해서 간단한 게시판 기능을 구현해봅니다.

1. 시작하기

 
node.js + mongodb 연습 겸 간단한 게시판을 만들어 보려고 합니다.
구현하고자 하는 기능은,
– 글 등록/수정/삭제
– 페이징
– 댓글
– 검색
입니다.  먼저 db 구조를 짜볼까요?
 
글에 필요한 정보는 -> 작성자, 글제목, 쓴 날짜, 수정 날짜, 삭제 날짜, 글 내용, 댓글, 조회수 입니다.
mongodb는 nosql의 데이터 베이스로 collection을 통해 데이터를 한 덩어리로 묶습니다. 그러니까 글에 필요한 정보를 한 덩어리로 묶어 json 방식으로 저장하게 할 것입니다.

 

그럼 db모델을 짜봅시다.
먼저 mongodb와 node.js를 연결 시켜줍니다.
(mongodb 연결 부분은 -> node.js에서 mongoDB사용하기(mongoose)를 참조하세요)
 
//connect db
mongoose.connect('mongodb://localhost/boards');

var db =mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log("connected");
});
models 디렉토리를 만들어 이 안에 db 스키마들을 저장하도록 할게요.
boardSchema.js 라는 파일을 만들고 아래의 내용을 넣습니다.
 
var mongoose = require('mongoose');

var boardSchema = mongoose.Schema({
    writer: String,
    password: String,
    title: String,
    contents: String,
    comments: [{
        name: String,
        memo: String,
        date: {type: Date, default: Date.now}
    }],
    count: {type:Number, default: 0},
    date: {type: Date, default: Date.now},
    updated: [{contents: String, date:{type: Date, default: Date.now}}],
    deleted: {type: Boolean, default: false} // true면 삭제 된 경우임
});

module.exports =  mongoose.model('BoardContents', boardSchema);
그러면 이제 스키마가 생겼고 db 구조가 생성되었습니다.
그 다음은 라우팅을 잡아보겠습니다.
기본 라우팅은 index.js을 통해 잡게 됩니다. 우리는 게시판만을 위한 js파일을 따로 생성해서 게시판 관련된 라우팅을 한곳에서 처리하도록 합니다.

 

routes 디렉토리 밑에 contents.js 파일을 만듭니다.
그리고 app.js에 라우트를 정의해줍니다.
var boards = require('./routes/contents’);
를 선언해서 boards라는 변수에 라우트 정보를 넣고, app.use(‘/boards’, boards);라고 정의해줍니다.
이제 /boards 경로를 통해 들어올 경우,  contents.js에서 일이 진행됩니다.

 

그러면 contents.js로 넘어가볼까요??
먼저 가장 위에,
var express = require('express');
var router = express.Router();
var BoardContents = require('../models/boardsSchema’);
를 선언해줍니다.
 
여기서 BoardContents는 db에 저장시키고 저장되어있는 것을 불러주는데 사용됩니다.
그리고 경로를 타고 들어오면 바로 보여줄 화면을 렌더링 해줍시다.
db에 저장되어있는 글들을 불러오고, 그것을 뷰단에 뿌려줄 것입니다.
 
router.get('/', function(req,res){
    // 처음 index로 접속 했을시 나오는 부분
    // db에서 게시글 리스트 가져와서 출력

    BoardContents.find({}).sort({date:-1}).exec(function(err, rawContents){
       // db에서 날짜 순으로 데이터들을 가져옴
        if(err) throw err;

        res.render('board', {title: "Board", contents: rawContents}); 
        // board.ejs의 title변수엔 “Board”를, contents변수엔 db 검색 결과 json 데이터를 저장해줌.
    });
});

그리고 하단에

module.exports = router;

를 추가해줍니다.

여기까지 하면 코어가 잡혔으니 이제 겉모습을 꾸며봅시다!

이런 모양으로 table 태그를 이용해 간단하게 디자인 해주었습니다.
저장된 글이 없을 경우는 글이 없다고 알려주고, 있을 경우는 시간 순으로 출력해줍니다.
 
<%if(contents.length>0){%>
        <%var i = 0;%>
        <%contents.forEach(function(item){%>
        <%i++;%>
        <tr>
            <td class="number"><%=i%></td>
            <td class="title"><a href="/boards/view?id=<%=item._id%>"><%=item.title%></a></td>
            <td class="writer"><%=item.writer%></td>
            <td class="date"><%=dateFormatChange(item.date)%></td>
            <td class="cnt"><%=item.count%></td>
        </tr>
        <%})%>
        <%} else {%>
        <tr>
            <td colspan="5">게시물이 없습니다.</td>
        </tr>
<%}%>

 

이렇게 게시판 구성까진 나왔고, 새 글을 작성해 봅시다.

 

2. 글 등록하기

 

<a onclick="displayWriteForm();"><div class="write_btn">새 글</div></a>

 

새글을 누르면, 새글을 작성할 수 있는 폼을 만들어 보여줍니다.

폼은 간단하게 아래처럼 만들었습니다.

<div class="write_form">
        <form id="writeAction" action="/boards" method="post">
            <input type="text" class="inputSubject" id="addContentSubject" name="addContentSubject" placeholder="제목">
            <input type="text" class="inputWriter" id="addContentWriter" name="addContentWriter" placeholder="작성자">
            <input type="password" class="inputPassword" id="addContentPassword" name=addContentPassword" placeholder="비밀번호">
            <textarea class="textContents" id="addContents" name="addContents" rows="20" cols="80"></textarea>
        </form>
        <div id = "new" class="addBtngroup">
            <a onclick="submitContents('add');"><div>SUBMIT</div></a>
            <a onclick="cancelWriteForm('cancel');"><div>CANCEL</div></a>
        </div>
    </div>

간단하게 제목, 작성자, 수정/삭제 용 비밀번호, 글을 입력받고, submit 버튼과 cancel버튼을 만듭니다. 그리고 submit하게 되면 빈칸을 체크하고 submit 하는 submitContents 함수를 만듭니다.

function submitContents(option) {
            var title = $('#addContentSubject').val();
            var content = $('#addContents').val();
            var writer = $('#addContentWriter').val();
            var password = $('#addContentPassword').val();

            if(option == 'add') {
                // 새 글 등록 시
                if(title == '' || content == '' || writer == '' || password == '') {
                    alert("제목과 내용, 작성자 비밀번호 모두 있어야합니다.");
                    return;
                } else {
                    $('#writeAction').submit();
                }

            }
        }
form의 경로대로 /boards로 가지만 이번엔 post 방식으로 데이터를 전송해 저장시키도록 합니다.
라우팅 부분으로 넘어가서 완성 시켜 봅시다.
 
router.post('/', function(req, res){
    // 글 작성하고 submit하게 되면 저장이 되는 부분
    var addNewTitle = req.body.addContentSubject;
    var addNewWriter = req.body.addContentWriter;
    var addNewContent = req.body.addContents;
    var addNewPasword = req.body.addContentPassword;

    addBoard(addNewTitle, addNewWriter, addNewContent, addNewPasword);
    res.redirect('/boards');
});

 

form에 입력된 정보를 req.body를 통해 받아오고 데이터베이스에 저장하는 함수 addBoard를 사용해 저장시킵니다. 그리고 다시 /boards로 경로를 보냅니다.
그러면 db에 저장이 완료된 후 게시판의 메인으로 돌아가서 다시 리스트를 볼 수 있게 되는 것이죠.
저장시키는 addBoard() 함수를 봅시다.

 

function addBoard(title, writer, content, password){
    var newBoardContents = new BoardContents;
    
    newBoardContents.writer = writer;
    newBoardContents.title = title;
    newBoardContents.contents = content;
    newBoardContents.password = password;

    newBoardContents.save(function (err) {
        if (err) throw err;
    });
}
db에 저장시키기 위해 BoardContents를 호출하여 newBoardContents를 선언해줍니다.
json 방식이므로 각각의 요소에 사용자가 입력한 데이터를 저장시키고, save 시키면 끝입니다!

 

3. 글 보기

 
 
게시판 리스트에서 타이틀 부분에 <a> 태그를 걸어 제목을 클릭하면 그 글을 출력하게 해줍니다.

간단하게 뷰단 레이아웃을 잡아주고 db로 넘어갑니다.
글을 보는 경로는 /view로 잡아줍니다. 어떤 글인지 알아야 하므로 넘어갈 때 글의 id도 함께 가져갑니다.
<a href="/boards/view?id=<%=item._id%>"><%=item.title%></a>
게시글을 볼때 우리에게 필요한 데이터는, 글 제목, 작성자, 작성날짜, 글 내용, 조회수가 필요합니다.
(댓글 기능은 기본 기능 완성 후 추가할 예정!)
그리고 조회수의 경우는, 글을 클릭할 때마다 1씩 증가 시켜줘야 하지요.

 

위의 필요한 정보들을 바탕으로 간단하게 게시글을 보는 페이지를 ‘boardDetail.ejs’ 파일로 만들어 주었습니다. 간단하게 레이아웃을 잡고, db에서 가져오는 데이터 값들의 위치를 정해주었습니다.
 
<div class="content_box">
        <div class="content-title"><%=content.title%></div>
        <div class="content-info">
            <%=dateFormatChange(content.date)%> / <%=content.writer%>
        </div>
        <div class="content-text">
            <%=content.contents%>
        </div>
        <a href="/boards"><div class="content-Btn">확인</div></a>
    </div>

 

여기서, 날짜 형식을 정리해주는 함수를 하나 지정해주었습니다.

<%
function dateFormatChange(date) {
    var options = {
        weekday: "short", year: "numeric", month: "short",
        day: "numeric", hour: "2-digit", minute: "2-digit"
    };

    return date.toLocaleTimeString("ko-KR", options);
}
%>

날짜 형식에 관한 내용은, 다음 글을 참조해주세요!
(날짜형식 및 정규식 예제들)

다음은 /view의 라우팅 부분입니다.
router.get('/view', function(req, res){
   // 글 보는 부분. 글 내용을 출력하고 조회수를 늘려줘야함
    var contentId = req.param('id');

    BoardContents.findOne({_id:contentId}, function(err, rawContent){
        if(err) throw err;
        rawContent.count += 1; // 조회수를 늘려줍니다.

        rawContent.save(function(err){ // 변화된 조횟수 저장
            if(err) throw err;

            res.render('boardDetail',{title: "Board", content:rawContent}); // db에서 가져온 내용을 뷰로 렌더링
        });
    })
});

앞서 게시판 리스트를 보여줄 때와 마찬가지로,  boardDetail.ejs 파일의 title 부분엔 “Board”가, content에는 “rawContent”의 값이 들어가게 됩니다.

 

여기까지 view도 완성했습니다!

다음은 글 수정/삭제로 들어가도록 할게요.
 
 

(node.js 게시판 만들기 마지막 여기에 최종 코드가 첨부되어 있습니다.)

RYUHA

만들어 보면서 하나씩 글 씁니당:)

16
Leave a Reply

avatar
11 Comment threads
5 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
12 Comment authors
seo배우는 초심자AM JWsonsoryEdgar Recent comment authors
newest oldest most voted
great
Guest
great

많은 도움이 되서 감사합니다.^^

Son빈
Member

강의 잘 보고 있는 유저입니다~
“글 등록하기”부터 갑자기 Jquery로 넘어가네요ㅎ Form은 Express내의 Public폴더에 있는 html로 연결시켜서 Jquery랑 연동시켰나요?

새글작성을 눌렀을 때, displayWriteForm() 함수설명이 빠진 것 같습니다.

새글 작성에서 “boards/new”라는 라우트를 하나 만들고, new.ejs파일을 생성하지 않고, html과 Jquery로 간 이유가 있나요? bodyparser랑 비교해서 프론트에서 함수를 사용해서 폼을 처리하는게 더 빠른지 궁금하네요/

어느순간부터 갑자기 강의 내용이 크고 한번에 넘어가버리는 것 같아서 저같은 초보자들은 당황할 것 같아요;

내용이 한번에 넘어갈 땐, Git에 소스를 올려주시면 따라가는 데에 많은 도움이 될 것 같습니다~.

전반적으로 좋은 내용 올려주셔서 감사합니다~

Son빈
Member

두번째 글입니다~

board.ejs파일에서, date를 체인지하는
dateFormatChange 함수에 관련된 소스가 안 보이는 것 같습니다!~

개발자
Guest
개발자

정말 좋은 예제군요!

html5around
Admin

부족한 글에 관심 가져 주셔서 감사합니다 🙂

박범준
Guest
박범준

contents.js 에 module.exports = router; 안넣으면 에러 나는거 같은데..

김홍철
Guest
김홍철

안녕하세요.
포스팅하신 글의 업데이트가 필요해보이네요…

위의 뎃글에서 반영해야할 부분도 안되어있구(예를 들어 contents.js에 module.exports = router;를 추가하는 내용)

node 초보인 저에겐 많은 에러가 발생하여 당혹스럽습니다.(예를 들어 app.js에 app.use(‘/boards’, boards);를 추가한다던가 boardDetail.ejs파일 명이 board.ejs로 바꿔야 된다는점)

여차저차해서 수정하였지만 board.ejs에서 dateFormatChange()함수 내의 return date.toLocaleTimeString(“ko-KR”, options);에서 에러가 나는데 ‘Cannot read property ‘toLocaleTimeString’ of undefined’라는 내용입니다.

어떻게 수정해야할까요…?

Edgar
Member

안녕하세요? 혹시 게시판의 css좀 받아볼수 없을까요? 게시판 css가 너무이뻐서요 ㅠㅠ

sonsory
Guest
sonsory

잘보고 있습니다.

에서

name= 다음에 ” 이 빠져있네요 🙂 감사합니다.

AM JW
Guest
AM JW

ㅇㅇ

배우는 초심자
Guest
배우는 초심자

맨위 링크가 안되요.

seo
Guest
seo

view 라우팅에서 var contentId = req.param(‘id’); 이 var contentid = req.query.id 로 바꾸셔야 할 것 같네요.