8장 MongoDB 🚀

[TOC]

8.1 NoSQL vs SQL

SQL(MySQL) NoSQL(MongoDB)
규칙에 맞는 데이터 입력 자유로운 데이터 입력
테이블 간 JOIN 지원 컬렉션 간 JOIN 미지원
안정성, 일관성 확장성, 가용성
용어(테이블, 로우, 컬럼) 용어(컬렉션, 다큐먼트, 필드)

8.2 몽고디비 설치하기

$ brew tap mongodb/brew
$ brew install mongodb-community

/* 몽고디비 실행 */
$ brew services start mongodb-community

관리자 계정으로 접속하기

> use admin
> db.createUser({ user: '이름', pwd: '비밀번호', roles: ['root'] })
$ mongod --auth // 로그인 필요
$ mongo admin -u [이름] -p [비밀번호]

8.3 컴퍼스 설치하기

컴퍼스: 몽고디비 관리 도구. GUI로 데이터를 시각적으로 관리할 수 있다.

$ brew cask install mongodb-compass-community

8.4 데이터베이스 및 컬렉션 생성하기

/* 데이터베이스 생성: use [데이터베이스명] */
use nodejs

/* 데이터베이스 목록 확인 */
show dbs

/* 현재 사용 중인 데이터베이스 확인 */
db

/* 컬렉션 생성하기 (다큐먼트 넣는 순간 컬렉션 자동 생성) */
db.createCollection('users')

8.5 CRUD 작업하기

*자료형

몽고디비는 기본적으로 JS문법을 따르므로 자료형 또한 JS를 따른다. (Date() 등)

추가적으로 쓰는 자료형은 ObjectId, Binary Data, Timestamp 등이 있음.

8.5.1 Create(생성)

db.컬렉션명.save(다큐먼트) 로 생성.

db.users.save({ name: 'zero', age: 24, married: false, comment: '안녕하세요.', createdAt: new Date() });
db.comments.save({ commenter: ObjectId("5fec1beac0dfb10221f22f4a"), comment: '안녕하세요. 댓글입니다.', createdAt: new Date() });

8.5.2 Read(조회)

db.컬렉션명.find({}) 로 전체 조회.

db.users.find({}, { _id: 0, name: 1, married: 1 });
// _id: 0 또는 false로 id 필드는 가져오지 않도록 한다.

db.users.find({ age: { $gt: 30 } }, { _id: 0, name: 1, married: 1 });
// $gt, $gte, $lt, $lte, $ne, $or, $in 연산자로 조건부 조회. 시퀄라이즈와 비슷하다.

db.users.find({ $or: [{ age: { $gt: 30 }}, { married: false }] }, { _id: 0, name: 1, married: 1 });

db.users.find({}, { _id: 0, name: 1, married: 1 }).sort({ age: -1 }).limit(1).skip(1);
// .sort()로 정렬, .limit()로 조회 개수, .skip()으로 건너뛸 개수 설정.

8.5.3 Update(수정)

db.users.update([수정할 다큐먼트 정보], [수정할 내용]) 으로 수정

$set{} 연산자를 지정해야. 특정 필드만 수정할 수 있다. 아니면 통째로 수정됨.

db.users.update({ name: 'zero' }, { $set: { comment: '안녕하세요 이 필드를 바꿔보겠습니다' } })

8.5.4 Delete(삭제)

db.users.remove([삭제할 다큐먼트 정보]) 로 삭제. 성공 시 삭제된 개수 반환.

db.users.remove({ name: 'nero' });

8.6 몽구스 사용하기

노드와 MongoDB를 연결해주고 쿼리를 만들어주는 ODM(Object Document Mapping).

장점
  • 스키마: 몽고디비에 데이터를 넣기 전 스키마로 데이터를 한 번 필터링함으로써 실수를 방지할 수 있다.
  • populate 메서드: MySQL의 JOIN 기능과 유사. 관계가 있는 데이터를 쉽게 가져올 수 있다.
  • ES2015 프로미스 문법과 가독성 높은 쿼리 빌더 지원.

8.6.1 몽고디비 연결하기

// package.json
{
  "name": "learn-mongoose",
  "version": "0.0.1",
  "description": "몽구스를 배우자",
  "main": "app.js",
  "scripts": {
    "start": "nodemon app",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "jiy",
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1",
    "mongoose": "^5.11.9",
    "morgan": "^1.10.0",
    "nunjucks": "^3.2.2"
  },
  "devDependencies": {
    "nodemon": "^2.0.6"
  }
}
// schemas/index.js

const mongoose = require('mongoose');

// 개발 환경일 때만 콘솔을 통해 몽구스가 생성하는 쿼리 내용을 확인
const connect = () => {
    if (process.env.NODE_ENV !== 'production') {
        mongoose.set('debug', true);
    }
}

// 몽구스와 몽고디비를 연결
mongoose.connect('mongodb://jiy:0602@localhost:27017/admin', {
    dbName: 'nodejs',
    useNewUrlParser: true, // useNewUrlParser, useCreateIndex 는 콘솔에 경고 메시지 띄우는 용도
    useCreateIndex: true,
}, (error) => {
    if (error) {
        console.log('몽고디비 연결 에러', error);
    } else {
        console.log('몽고디비 연결 성공');
    }
})

// 몽구스 커넥션에 이벤트 리스너 추가. 에러 발생 시 에러 내용 기록하고 연결 종료 시 재연결 시도
mongoose.connection.on('error', (error) => {
    console.error('몽고디비 연결 에러', error);
})

mongoose.connection.on('disconnected', () => {
    console.error('몽고디비 연결이 끊어졌습니다. 연결을 재시도합니다.');
    connect();
})

module.exports = connect;
// ./app.js

const express = require('express');
const path = require('path');
const morgan = require('morgan');
const nunjucks = require('nunjucks');

const connect = require('./schemas');

const app = express();
app.set('port', process.env.PORT || 3002);
app.set('view engine', 'html');
nunjucks.configure('views', {
    express: app,
    watch: true,
});
connect();

app.use(morgan('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

app.use((req, res, next) => {
    const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
    error.status = 404;
    next(error);
});

app.use((err, req, res, next) => {
    res.locals.message = err.message;
    res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
    res.status(err.status || 500);
    res.render('error');
});

app.listen(app.get('port'), () => {
    console.log(app.get('port'), '번 포트에서 대기 중');
});

8.6.2 스키마 정의하기

// ./schemas/user.js
const mongoose = require('mongoose');

const { Schema } = mongoose;
// _id를 기본 키로 생성하므로 선언X
const userSchema = new Schema({
    name: {
        type: String,
        required: true,
        unique: true,
    },
    age: {
        type: Number,
        required: true,
    },
    comment: String,
    createdAt: {
        type: Date,
        default: Date.now,
    }
});

// model메서드로 스키마와 몽고디비 컬렉션 연결
module.exports = mongoose.model('User', userSchema);
// ./schemas/comment.js
const mongoose = require('mongoose');

const { Schema } = mongoose;
const { Types: { ObjectId } } = Schema;
const commentSchema = new Schema({
    commenter: {
        type: ObjectId,
        required: true,
        ref: 'User',
    },
    comment: {
        type: String,
        required: true,
    },
    createdAt: {
        type: Date,
        default: Date.now,
    },
});

// model메서드로 스키마와 몽고디비 컬렉션 연결
module.exports = mongoose.model('Comment', commentSchema);

8.6.3 쿼리 수행하기

https://github.com/ZeroCho/nodejs-book/tree/master/ch8/8.6/learn-mongoose

results matching ""

    No results matching ""