MongoDB와 Express.js로 블로그 만들기 – 완성까지 한번에!

MongoDB와 Express.js로 블로그 만들기 – 완성까지 한번에!

프로젝트 소개 및 목표

MongoDB와 Express.js로 블로그 만들기 프로젝트는 백엔드 개발의 핵심 개념을 실습할 수 있는 최고의 포트폴리오 프로젝트입니다. 이 가이드에서는 RESTful API 설계, NoSQL 데이터베이스 연동, 인증/인가 시스템 구현까지 실무에서 필요한 모든 기술을 단계별로 배웁니다. 완성된 블로그는 게시글 CRUD, 댓글 시스템, 사용자 인증 기능을 포함하며, 실제 서비스 가능한 수준의 애플리케이션이 됩니다. 이 프로젝트를 통해 Node.js 생태계의 핵심 기술을 마스터하고, 취업이나 사이드 프로젝트에 바로 활용할 수 있는 실력을 갖추게 됩니다.

필요한 기술 스택

이 프로젝트를 진행하기 위해 필요한 기술 스택은 다음과 같습니다:

  • Node.js & Express.js: 서버 프레임워크 및 라우팅
  • MongoDB & Mongoose: NoSQL 데이터베이스 및 ODM
  • JWT (jsonwebtoken): 사용자 인증/인가
  • bcrypt: 비밀번호 암호화
  • dotenv: 환경변수 관리
  • express-validator: 입력값 검증
  • Postman: API 테스트 도구

프로젝트 셋업

먼저 프로젝트 디렉토리를 생성하고 필요한 패키지를 설치합니다. MongoDB는 MongoDB Atlas를 사용하여 클라우드 데이터베이스를 구성하거나 로컬에 설치할 수 있습니다.

// 1. 프로젝트 초기화
mkdir blog-backend && cd blog-backend
npm init -y

// 2. 필요한 패키지 설치
npm install express mongoose dotenv bcryptjs jsonwebtoken cors
npm install --save-dev nodemon

// 3. 프로젝트 구조 생성
mkdir models routes controllers middleware config
touch server.js .env

.env 파일에 환경변수를 설정합니다:

PORT=5000
MONGO_URI=mongodb+srv://username:[email protected]/blog
JWT_SECRET=your_secret_key_here
NODE_ENV=development

단계별 구현 과정

1단계: 서버 및 데이터베이스 연결 설정

Express 서버를 설정하고 MongoDB에 연결합니다.

// server.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();

const app = express();

// 미들웨어
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// MongoDB 연결
mongoose.connect(process.env.MONGO_URI)
  .then(() => console.log('MongoDB 연결 성공'))
  .catch(err => console.error('MongoDB 연결 실패:', err));

// 라우트
app.use('/api/auth', require('./routes/auth'));
app.use('/api/posts', require('./routes/posts'));
app.use('/api/comments', require('./routes/comments'));

const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`서버 실행 중: ${PORT}`));

2단계: Mongoose 스키마 및 모델 정의

사용자, 게시글, 댓글 모델을 정의합니다.

// models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    minlength: 3
  },
  email: {
    type: String,
    required: true,
    unique: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true,
    minlength: 6
  },
  role: {
    type: String,
    enum: ['user', 'admin'],
    default: 'user'
  }
}, { timestamps: true });

// 비밀번호 해싱
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next();
  this.password = await bcrypt.hash(this.password, 12);
  next();
});

// 비밀번호 검증 메서드
userSchema.methods.comparePassword = async function(candidatePassword) {
  return await bcrypt.compare(candidatePassword, this.password);
};

module.exports = mongoose.model('User', userSchema);
// models/Post.js
const mongoose = require('mongoose');

const postSchema = new mongoose.Schema({
  title: {
    type: String,
    required: true,
    trim: true,
    maxlength: 200
  },
  content: {
    type: String,
    required: true
  },
  author: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
    required: true
  },
  category: {
    type: String,
    enum: ['기술', '일상', '프로젝트', '튜토리얼'],
    default: '일상'
  },
  tags: [String],
  views: {
    type: Number,
    default: 0
  },
  published: {
    type: Boolean,
    default: false
  }
}, { timestamps: true });

module.exports = mongoose.model('Post', postSchema);

3단계: 인증 시스템 구현

회원가입, 로그인, JWT 토큰 발급을 구현합니다.

// controllers/authController.js
const User = require('../models/User');
const jwt = require('jsonwebtoken');

// JWT 토큰 생성
const generateToken = (userId) => {
  return jwt.sign({ id: userId }, process.env.JWT_SECRET, {
    expiresIn: '7d'
  });
};

// 회원가입
exports.register = async (req, res) => {
  try {
    const { username, email, password } = req.body;

    // 중복 체크
    const existingUser = await User.findOne({ $or: [{ email }, { username }] });
    if (existingUser) {
      return res.status(400).json({ message: '이미 존재하는 사용자입니다.' });
    }

    // 사용자 생성
    const user = await User.create({ username, email, password });
    const token = generateToken(user._id);

    res.status(201).json({
      message: '회원가입 성공',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });
  } catch (error) {
    res.status(500).json({ message: '서버 오류', error: error.message });
  }
};

// 로그인
exports.login = async (req, res) => {
  try {
    const { email, password } = req.body;

    const user = await User.findOne({ email });
    if (!user || !(await user.comparePassword(password))) {
      return res.status(401).json({ message: '이메일 또는 비밀번호가 잘못되었습니다.' });
    }

    const token = generateToken(user._id);
    res.json({
      message: '로그인 성공',
      token,
      user: {
        id: user._id,
        username: user.username,
        email: user.email
      }
    });
  } catch (error) {
    res.status(500).json({ message: '서버 오류', error: error.message });
  }
};

4단계: 게시글 CRUD API 구현

MongoDB와 Express.js로 블로그 만들기의 핵심인 게시글 관리 기능을 구현합니다.

// controllers/postController.js
const Post = require('../models/Post');

// 게시글 목록 조회 (페이지네이션)
exports.getPosts = async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;
    const skip = (page - 1) * limit;

    const posts = await Post.find({ published: true })
      .populate('author', 'username')
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit);

    const total = await Post.countDocuments({ published: true });

    res.json({
      posts,
      currentPage: page,
      totalPages: Math.ceil(total / limit),
      totalPosts: total
    });
  } catch (error) {
    res.status(500).json({ message: '서버 오류', error: error.message });
  }
};

// 게시글 생성
exports.createPost = async (req, res) => {
  try {
    const { title, content, category, tags, published } = req.body;

    const post = await Post.create({
      title,
      content,
      category,
      tags,
      published,
      author: req.user.id
    });

    res.status(201).json({ message: '게시글 작성 성공', post });
  } catch (error) {
    res.status(500).json({ message: '서버 오류', error: error.message });
  }
};

// 게시글 수정
exports.updatePost = async (req, res) => {
  try {
    const post = await Post.findById(req.params.id);

    if (!post) {
      return res.status(404).json({ message: '게시글을 찾을 수 없습니다.' });
    }

    if (post.author.toString() !== req.user.id) {
      return res.status(403).json({ message: '권한이 없습니다.' });
    }

    const updatedPost = await Post.findByIdAndUpdate(
      req.params.id,
      req.body,
      { new: true, runValidators: true }
    );

    res.json({ message: '수정 완료', post: updatedPost });
  } catch (error) {
    res.status(500).json({ message: '서버 오류', error: error.message });
  }
};

// 게시글 삭제
exports.deletePost = async (req, res) => {
  try {
    const post = await Post.findById(req.params.id);

    if (!post) {
      return res.status(404).json({ message: '게시글을 찾을 수 없습니다.' });
    }

    if (post.author.toString() !== req.user.id) {
      return res.status(403).json({ message: '권한이 없습니다.' });
    }

    await post.deleteOne();
    res.json({ message: '게시글 삭제 완료' });
  } catch (error) {
    res.status(500).json({ message: '서버 오류', error: error.message });
  }
};

5단계: 인증 미들웨어 구현

JWT 토큰을 검증하여 보호된 라우트에 접근을 제어합니다.

// middleware/auth.js
const jwt = require('jsonwebtoken');
const User = require('../models/User');

exports.protect = async (req, res, next) => {
  try {
    let token;

    if (req.headers.authorization && req.headers.authorization.startsWith('Bearer')) {
      token = req.headers.authorization.split(' ')[1];
    }

    if (!token) {
      return res.status(401).json({ message: '인증 토큰이 없습니다.' });
    }

    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = await User.findById(decoded.id).select('-password');

    if (!req.user) {
      return res.status(401).json({ message: '사용자를 찾을 수 없습니다.' });
    }

    next();
  } catch (error) {
    res.status(401).json({ message: '유효하지 않은 토큰입니다.' });
  }
};

6단계: 라우트 설정

API 엔드포인트를 정의하고 컨트롤러와 연결합니다.

// routes/posts.js
const express = require('express');
const router = express.Router();
const { protect } = require('../middleware/auth');
const {
  getPosts,
  getPost,
  createPost,
  updatePost,
  deletePost
} = require('../controllers/postController');

router.get('/', getPosts);
router.get('/:id', getPost);
router.post('/', protect, createPost);
router.put('/:id', protect, updatePost);
router.delete('/:id', protect, deletePost);

module.exports = router;

테스트 및 배포

API 테스트

Postman이나 Thunder Client를 사용하여 각 엔드포인트를 테스트합니다:

// 회원가입 테스트
POST http://localhost:5000/api/auth/register
Content-Type: application/json

{
  "username": "testuser",
  "email": "[email protected]",
  "password": "password123"
}

// 게시글 작성 테스트
POST http://localhost:5000/api/posts
Authorization: Bearer YOUR_JWT_TOKEN
Content-Type: application/json

{
  "title": "첫 번째 게시글",
  "content": "안녕하세요! 블로그 첫 글입니다.",
  "category": "일상",
  "tags": ["인사", "첫글"],
  "published": true
}

배포 준비

Heroku, Railway, 또는 Vercel에 배포할 수 있습니다. package.json에 시작 스크립트를 추가합니다:

// package.json
{
  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  }
}

환경변수를 배포 플랫폼에 설정하고, MongoDB Atlas 연결 문자열을 확인합니다. CORS 설정도 프로덕션 도메인에 맞게 조정합니다.

마무리 및 확장 아이디어

이제 MongoDB와 Express.js로 블로그 만들기 프로젝트의 기본 구조가 완성되었습니다. 추가로 구현할 수 있는 기능들:

  • 댓글 시스템: 게시글에 댓글 및 대댓글 기능 추가
  • 이미지 업로드: Multer와 Cloudinary를 사용한 파일 업로드
  • 검색 기능: 제목, 내용, 태그 기반 검색 구현
  • 좋아요/북마크: 사용자 인터랙션 기능 추가
  • 관리자 대시보드: 통계 및 관리 기능
  • 이메일 인증: Nodemailer를 활용한 회원가입 인증

이 프로젝트를 GitHub에 올리고 README를 작성하여 포트폴리오로 활용하세요!

📚 함께 읽으면 좋은 글

1

React + Node.js 풀스택 앱 배포하기 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 2.
🎯 React + Node.js 풀스택 앱 배포하기

2

REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 1.
🎯 REST API 서버 구축 단계별 튜토리얼

3

실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 1.
🎯 실시간 채팅 앱 만들기 with Socket.io

4

REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 1.
🎯 REST API 서버 구축 단계별 튜토리얼

5

REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!

📂 프로젝트 아이디어
📅 2025. 10. 1.
🎯 REST API 서버 구축 단계별 튜토리얼

💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!

📢 이 글이 도움되셨나요? 공유해주세요!

여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨

🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏

💬 여러분의 소중한 의견을 들려주세요!

이 글을 읽고 새롭게 알게 된 정보가 있다면 공유해주세요!

💡
유용한 정보 공유

궁금한 점 질문

🤝
경험담 나누기

👍
의견 표현하기

⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨

🔔 블로그 구독하고 최신 글을 받아보세요!

📚
다양한 주제
17개 카테고리

정기 업데이트
하루 3회 발행

🎯
실용적 정보
바로 적용 가능

💡
최신 트렌드
2025년 기준

🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨

📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!

답글 남기기