0-1) MongoDB (feat. mongoose)

terlinko·2023년 8월 3일
1

쇼핑몰 프로젝트

목록 보기
2/6
post-thumbnail

쇼핑몰 제작에 앞서, MongoDB 사용에 익숙해지고 MVC 패턴을 Express 에 적용하는 작은 프로젝트를 하나 만들어봤다.
mongooseMVC - GitHub

mongoose 를 사용하는, 사용해야 하는 이유는 다른 곳에서 찾길 바란다.
과정만 설명하겠다.

  1. 프로젝트 폴더 생성과 초기 설정
> mkdir mongoose
> npm init -y

Express Generator 를 사용하다가, 부가적인 기능이 많아서 그냥 생닭 프로젝트를 좋아하게 되었다.

{
  "name": "mongooseMVC",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

나오면 성공.
이번 프로젝트에서 사용할 모듈은 총 세개.
express, dotenv, mongoose, nodemon 이다.

> npm i express dotenv mongoose
> npm i -D nodemon

설치가 됐으면 package.json 에 nodemon으로 구동하는 한줄을 추가해준다.
"dev": "nodemon index.js"

...
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
  	"dev": "nodemon index.js"
  }, ...

서버를 시작할 땐 > npm run dev 로 시작하면 된다.

물론 지금은 안될 것이다. index.js 파일도 없잖아.

만들어준다.
> touch index.js

/** index.js */
const express = require('express');
const app = express();
require('dotenv').config();

app.use(express.json());
app.use(express.urlencoded({extended: false}));

app.get('/', (req, res)=>{
  res.send('Hello World');
});

app.listen(process.env.PORT, ()=>{
  console.log(`PORT : ${process.env.PORT}');
});

이러면 Hello World 정도는 3초컷이다.

> touch .env

PORT = 3000
MONGO_URI = mongodb://localhost:27017/mydatabase
  1. Model, View, Controller 분리

사실 View 부분은 만들지 않을 거다.
models 폴더와 routes, controller 폴더를 만들어준다.

├── controller
│   └── userController.js
├── index.js
├── model
│   └── users.js
├── node_modules
├── package-lock.json
├── package.json
└── routes
    └── userRouter.js

지금은 하나의 모델, 하나의 라우트, 하나의 컨트롤러라서 오히려 파일을 나누는게 더 복잡해졌지만,
규칙을 갖고 설계를 하는 것은 중요한 것 같다.
사실 이렇게 하는게 맞는지 잘 모르겠는데 우당탕탕 분리했다.

2-1. Schema

/** /model/users.js */
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
    name : {
        type: String,
        required: true,
    },
    phone : {
        type: String,
        required: true,
        unique: true,
    },
    seq : {
        type: Number
    }
},{timestamps: true, versionKey: false});

module.exports = mongoose.model('User', userSchema);

모델의 복수형(user->users)이 collection 이름으로 생성된다.
(물론 collection 이름을 지정해줄수있다.)
파일 이름은 collection 이름으로 했다.
module.exports = 모델;을 하면 Mongoose Model에 있는 것들을 모듈처럼 사용할 수 있다.
seq 을 추가한 이유는 유저를 식별하기엔 번호만큼 좋은 게 없는 것 같아 추가했다.
mongoose-sequence를 사용해서 자동으로 증가하게 만드려했지만, 실패했다.

2-2. Controller

/** /controller/userController.js */
const User = require('../model/users')

const controller = {
    get: (req, res)=>{
        User.find({}).then((users)=>{
            if(!users.length) return res.status(404).json({ err: 'user not found' });
            res.json(users);
        })
        .catch(err => res.status(500).json(err))
    },
    getSeq: (req, res)=>{
        const userSeq = req.params.userSeq;
        User.findOne({ seq: userSeq }).then((user)=>{
            if(!user) return res.status(404).json({ err: 'user not found' });
            res.json(user);
        })
        .catch(err => res.status(500).json(err));
    },
    post: (req, res)=>{
        const new_user = new User(req.body);
        new_user.save().then((result)=>{
            res.json(result);
        })
        .catch(err => res.status(500).json(err));
    },
    deleteSeq: (req, res)=>{
        const userSeq = req.params.userSeq;
        User.deleteOne({ seq: userSeq }).then((result)=>{
            if(!result.deletedCount) return res.status(404).json({ err: 'user not found'});
            res.json(result)
        })
        .catch(err => res.status(500).json(err));
    },
    updateSeq: (req,res)=>{
        const userSeq = req.params.userSeq;
        const update = req.body;
        User.updateOne({ seq: userSeq }, update).then((result)=>{
            if(!result.matchedCount) return res.status(404).json({ err: 'user not found'});
            res.json(result)
        })
        .catch(err => res.status(500).json(err));
    }
}

module.exports = controller

함수 용도
get : 모든 user 들을 json 형식으로 반환
getSeq : seq == req.params.userSeq 인 user 반환
post : 새로운 유저
deleteSeq: seq == req.params.userSeq 인 user 삭제
updateSeq: seq == req.params.userSeq 인 user 업데이트

사실 이렇게 했을 때 Router 에서 직접 하는 것과 차이가 없다.

2-3. Routes

/** /routes/userRoutes/ */
const express = require('express')
const router = express.Router();
const userController = require('../controller/userController')

router.get('/', userController.get);
router.post('/', userController.post);
router.get('/:userSeq', userController.getSeq);
router.delete('/:userSeq', userController.deleteSeq);
router.patch('/:userSeq', userController.updateSeq);

module.exports = router

컨트롤러와 라우터가 일대일 대응한다면 Controller를 분리 할 이유가 없다.

MVC 패턴의 의의를 찾아보고자, 나는 방법을 고안해냈다.
바로 미들 웨어를 사용하는 것이다.

우선 강력한 미들 웨어를 만들어보자

2-?. 미들 웨어

> mkdir middleware
그 안에 강력한 미들웨어를 작성하자.
> touch bodybody.js

우리의 친구 바디바디는 무려 req.body 를 콘솔에 찍어주는 미들웨어이다.
내가 작성했지만 너무 강력한 미들웨어라서 코드는 공개하지 않겠다.

이제 Routes 에서 post 요청, patch 요청이 들어왔을 땐, bodybody 를 먼저 실행하게 하자.

/** /routes/userRouter */
const express = require('express')
const router = express.Router();
const userController = require('../controller/userController')
const bodybody = require('../middleware/bodybody')

router.get('/', userController.get);
router.post('/', bodybody, userController.post);
router.get('/:userSeq', userController.getSeq);
router.delete('/:userSeq', userController.deleteSeq);
router.patch('/:userSeq', bodybody, userController.updateSeq);

module.exports = router

만약 우리의 강력한 bodybody가 사용자가 로그인했는지 확인하는 미들웨어라면?
다른 주소로 요청이 들어왔을 때도 같은 컨트롤러로 대응하고 싶다면?
블로그 게시판을 담당하는 boardRouter 에서 userController 의 로직을 사용하고 싶다면?
아아, MVC

  1. 완성
/** index.js */
const express = require('express')
const app = express();
require('dotenv').config();
const mongoose = require('mongoose')

const UserRouter = require('./routes/userRouter')

app.use(express.json())
app.use(express.urlencoded({extended: false}))
mongoose
  .connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('Successfully connected to mongodb'))
  .catch(e => console.error(e));



app.use('/user', UserRouter)
app.get('/', (req, res)=>{
    res.send('Hello World')
})

app.listen(process.env.PORT, ()=>{
    console.log('http://localhost:'+ process.env.PORT)
})

POSTMAN 을 이용해서 여러가지 요청들을 테스트해본다.

1개의 댓글

comment-user-thumbnail
2023년 8월 3일

큰 도움이 되었습니다, 감사합니다.

답글 달기