반응형

# 몽고디비

1. NoSQL

  • MySQL 같은 SQL 데이터베이스와는 다른 유형의 데이터
  • NoSQL의 대표주자인 mongoDB (몽고디비) 사용.
SQL 과 NoSQL 비교
SQL (MySQL) NoSQL (몽고디비)
규칙에 맞는 데이터 입력 자유로운 데이터 입력
테이블 간 JOIN 지원 컬렉션 간 JOIN 미지원
안정성, 일관성 확장성, 가용성
용어 (테이블, 로우, 컬럼) 용어 (컬렉션, 다큐먼트, 필드)
  • JOIN : 관계가 있는 테이블끼리 데이터를 합치는 기능 (몽고디비 aggregate로 흉내 가능)
  • 빅데이터, 메시징, 세션 관리 등 (비정형 데이터)에는 몽고디비 사용하면 좋음.

 

2. 몽고디비 설치

### 윈도의 경우.

 

MongoDB Community Download

Download the Community version of MongoDB's non-relational database server from MongoDB's download center.

www.mongodb.com

  • community 버전 다운로드하여 설치 진행, 설치 시 complete 클릭 후 Install MongD as a Service 체크 해제 이후에 Install MongoDB Compass는 체크하여 설치 진행.

 

3. 몽고디비 연결

  • 윈도의 경우 C:\에 data 폴더를 만들고 그 안에 db 폴더 생성.
  • 콘솔로 몽고디비가 설치된 경로(기본적으로 C:\Program Files\MongoDB\Server\5.0\bin)로 이동하여 몽고디비를 실행. 그리고 콘솔에 아래와 같이 이력하여 실행
# 콘솔
mongod
  • mongd로 실행 후 그대로 놔둬야 몽고디비 서버가 돌아감. (만약 실행해둔걸 끄게되면 몽고디비 꺼짐)
  • mongd가 실행된 상태에서 mongo.exe 실행 시 몽고디비 사용 가능.

 

4. 어드민 설정

  • 어드민 권한을 설정하여 디비에 비밀번호 걸기
# 콘솔
use admin

db.createUser({ user: '이름'. pwd: '비밀번호', roles: ['root']});
  • mongod를 입력했던 콘솔을 종료한 후 mongod --auth 명령어로 접속. (--auth는 로그인이 필요하다는 의미.)
  • mongo를 입력한 콘솔도 종료한 후 mongo admin -u 이름 -p 비밀번호로 접속.

 

5. 커넥션 생성하기

  • 컴퍼스 (MongDB Compass Community)로 접속 (mongod 실행된 상태에서 진행)
  • Fill in connection fields individually 클릭 후 아래와 같이 입력 후 connect 진행

  • 아래는 접속 성공 화면 (기본적으로 admin / config / local 존재)

 

6. 데이터 베이스 생성

### 터미널 이용 시.

  • use 데이터베이스명으로 생성
# 콘솔
use nodejs	// nodejs 이름의 데이터베이스 생성.
  • show dbs로 목록 확인.
# 콘솔
show dbs

출력 예
admin   0.000GB
config  0.000GB
local   0.000GB

=> 방금 생성한 nodejs는 보이지 않음. (데이터 넣기 전까지는 보이지 않음)
  • db로 현재 사용중인 데이터베이스 확인 가능.
# 콘솔
db

출력 예
nodejs

### MongoDB Compass 이용 시.

  • CREATE DATABASE 클릭하여 생성

 

7. 컬렉션 생성 (=SQL에서의 테이블)

  • 따로 생성할 필요는 없음.
  • 다큐먼트(=로우)를 넣는 순간 컬렉션도 자동 생성된다.
  • 직접 생성하는 명령어도 있다. (아래참고)
# 콘솔
db.createCollection('users')
{ "ok" : 1 }

db.createCollection('comments')
{ "ok" : 1 }
  • show collections로 현재 컬렉션 확인 가능.
# 콘솔
show collections

출력 예
comments
users
  • 컬렉션 생성 후 MongoDB Compass에서 새로고침 클릭 시 생긴 걸 확인 가능.

 

8. 몽고디비 CRUD 작업

### Create

  • 몽고디비는 컬럼을 정의하지 않아도 된다.
  • 자유로움이 장점, 무엇이 들어올지 모른다는 단점 (몽고디비는 오타에 취약하므로 필드명에 주의.)
  • 자바스크립트의 자료형을 따른다. (차이점도 존재)
  • ObjectId : 몽고 디비의 자료형으로 고유 아이디 역할. ( _id : ObjectId(" ") )
  • save method로 저장
# 콘솔
use nodejs

db.users.save({ name: 'zero', age: 28, married: false, comment: 'hello hi.', createAt: new Date() });

db.users.save({ name: 'mero', age: 32, married: true, comment: 'what hi hello.', createAt: new Date() });
  • MongoDB Compass 로 생성할 경우 : users 컬렉션 클릭 후 ADD DATA 클릭 > Insert Document 클릭하여 json 형식에 맞게 작성 or 한줄씩 추가(VIEW 옆에 { } or 목록모양 클릭)하여 생성.

### Create (관계 설정)

  • 컬렉션 간 관계를 강요하는 제한이 없으므로 직접 ObjectId를 넣어 연결.
  • 사용자의 ObjectId를 찾은 뒤 댓글 컬렉션에 넣음.
# 콘솔
db.users.find({ name : 'zero' }, { _id : 1 })

=>
첫 번째 객체가 find 하는 조건. ({ name : 'zero' })
두 번째 객체는 어떤 필드를 보여줄지. ({ _id : 1 })	// 1 : 보여줌, 0 : 숨김
  • users에서 댓글 작성자의 ObjectId를 comments에서 commenter에 넣어주면 유저가 연결되는 것임. (단, 몽고디비가 commenter에 대해 존재하는지 여부 등을 검사해 주지 않음. 그래서 오타에 주의해야 함)

### Read

  • find로 조회, findOne으로 하나만 조회
# 콘솔
db.users.find({});	// users 컬렉션 전체 조회.

db.comments.find({});	// comments 컬렉션 전체 조회.

### Read (조건)

  • 두 번째 인수로 조회할 필드를 선택할 수 있다. (1은 추가 / 0은 제외) ( _id는 기본적으로 1임)
# 콘솔
db.users.find({ _id: 0, name: 1, married: 1 });
  • 첫 번째 인수로 조회 조건을 입력할 수 있다. ($gt 나 $or 같은 조건 연산자 사용)
# 콘솔
db.users.find({ $or: [{ age: { $gt: 30 }, { married: false }] }, { _id: 0, name: 1, age: 1 });
  • 정렬은 sort 메서드로 함. (1 : 오름차순 / -1 : 내림차순)
# 콘솔
db.users.find({}, { _id: 0, name: 1, age: 1}).sort({ age: -1 });
  • limit 메서드로 조회할 다큐먼트 개수 제한
# 콘솔
db.users.find({}, { _id: 0, name: 1, age: 1 }).sort({ age: -1 }).limit(1);
  • skip 메서드로 건너뛸 다큐먼트 개수 제공
# 콘솔
db.users.find({}, { _id: 0, name: 1, age: 1 }).sort({ age: -1 }).limit(1).skip(1);

### Update

  • update 메서드로 쿼리.
  • 첫 번째 인수로 수정 대상을, 두 번째 인수로 수정 내용을 제공
  • $set을 붙이지 않으면 다큐먼트 '전체'가 대체되므로 주의
# 콘솔
db.users.update({ name: 'nero' }, { $set: { comment: '안녕하세요. 해당 필드 수정' } });
  • 결과로 수정된 개수가 나옴.

### Delete

  • remove 메서드로 쿼리.
  • 첫 번째 인수로 삭제할 대상 조건 제공
# 콘솔
db.users.remove({ name: 'nero' });
  • 성공 시 삭제된 개수가 반환 됨.

 

9. 몽구스 ODM

  • 몽고디비 작업을 쉽게 할 수 있도록 도와주는 라이브러리. (몽고디비 드라이버도 내장되어 있음)
  • ODM : Object Document Mapping로 객체와 도큐먼트를 매핑(1대 1 짝지음)
  • 몽고디비에 없어 불편한 기능들을 몽구스가 보완.
  • 테이블과 유사한 기능, JOIN 기능 추가.
  • $set 등 빼먹는 것 방지, 쿼리빌더(쿼리를 쉽게 만들 수 있게 해주는 기능) 기능 제공.
  • 몽구스 예제 참고 : https://github.com/zerocho/nodejs-book/tree/master/ch8/8.6/learn-mongoose 
 

GitHub - ZeroCho/nodejs-book

Contribute to ZeroCho/nodejs-book development by creating an account on GitHub.

github.com

  • 위 예제의 경우 프로젝트 세팅 후 콘솔을 통해 경로로 이동한 후 package.json 설치
# 콘솔
npm i express morgan nunjucks mongoose

npm i -D nodemon
  • /schemas/index.js
const mongoose = require('mongoose');

const connect = () => {
  if (process.env.NODE_ENV !== 'production') {
    mongoose.set('debug', true);    // 개발 시 debug 모드 true.(쿼리가 콘솔에 보이도록 해줌)
  }

  // mongodb://설정한 관리자아이디:비밀번호@localhost:27017/admin
  mongoose.connect('mongodb://root:nodejsbook@localhost:27017/admin', { // 로그인을 위한 db
    dbName: 'nodejs',   // 실제로 데이터 저장할 db명
    useNewUrlParser: true,
    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 indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');

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('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comments', commentsRouter);

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'), '번 포트에서 대기 중');
});

 

10. 몽구스 스키마 사용 (학습중)

### 스키마 정의하기

  • schemas 폴더 안에 작성. MySQL의 테이블처럼 정해진 데이터만 들어갈 수 있게 강제함
  • type은 자료형, require는 필수 여부 default는 기본값, unique는 고유 여부.
  • schemas/user.js
const mongoose = require('mongoose');

const { Schema } = mongoose;
const userSchema = new Schema({
    name : {
        type: String,
        required: true, // required: 필수여부.
        unique: true,
    },
    age : {
        type: Number,
        required: true,
    },
    married: {
        type: Boolean,
        required: true,
    },
    comment: String,    // 이 경우 required는 false. (필수값 x)
    createdAt: {
        type: Date,
        default: Date.now,  // 현재시간
    },
});

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',    // 관계 설정 : user를 적음으로써 user 스키라마의 ObjectId를 의미.
    },
    comment: {
        type: String,
        required: true,
    },
    createdAt: {
        type: Date,
        default: Date.now,
    },
});

module.exports = mongoose.model('Comment', commentSchema);

### 라우터 작성하기

  • 프론트엔드 코드는 서버에 요청을 보내는 AJAX 요청 위주로, 서버 코드는 응답을 보내는 라우터 위주로 살펴보기.

### 사용자 라우터

  • router.get, post, put, patch, delete 라우터 작성.
  • routes/uses.js
const express = require('express');
const User = require('../schemas/user');
const Comment = require('../schemas/comment');

const router = express.Router();

router.route('/')
    .get(async (req, res, next) => {
        try {
            const users = await User.find({});
            res.json(users);
        } catch (err) {
            console.error(err);
            next(err);
        }
    })
    .post(async (req, res, next) => {
        try {
            const user = await User.create({
                name: req.body.name,
                age: req.body.age,
                married: req.body.married,
            });
            console.log(users);
            res.status(201).json(user);
        } catch (err) {
            console.error(err);
            next(err);
        }
    });

router.get('/:id/comments', async (req, res, next) => {
    try {
        const comments = await Comment.find({ commenter: req.params.id })
            .populate('commenter');
        console.log(comments);
        res.json(comments);
    } catch (err) {
        console.error(err);
        next(err);
    }
});

module.exports = router;
  • 몽구스의 populate는 자바스크립트로 하는 거라서 속도가 느리다.

### 댓글 라우터

  • router.get, post, put, patch, delete 라우터 작성.
  • routes/comments.js
const express = require('express');
const Comment = require('../schemas/comment');

const router = express.Router();

router.post('/', async (req, res, next) => {
    try {
        const comment = await Comment.create({
            commenter: req.body.id,
            comment: req.body.comment,
        });
        console.log(comment);
        const result = await Comment.populate(comment, { path: 'commenter' });
        res.status(201).json(result);
    } catch (err) {
        console.error(err);
        next(err):
    }
});

router.route('/:id')
    .patch(async (req, res, next) => {
        try {
            const result = await Comment.update({
                _id: req.params.id,
            }, {
                comment: req.body.comment,
            });
            res.json(result);
        } catch (err) {
            console.error(err);
            next(err);
        }
    })
    .delete(async (req, res, next) => {
        try {
            const result = await Comment.remove({ _id: req.params.id });
            res.json(result);
        } catch (err) {
            console.error(err);
            next(err);
        }
    });

module.exports = router;

### 라우터 연결하기

  • app.js에 연결
  • app.js
const connect = require('./schemas');
const indexRouter = require('./routes');
const usersRouter = require('./routes/users');
const commentsRouter = require('./routes/comments');

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

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/comments', commentsRouter);

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

반응형

+ Recent posts