[MongoDB] - mongoose 모듈 사용하기

Donggu(oo)·2024년 8월 11일
0

Node.js

목록 보기
9/9
post-thumbnail
  • Node.js에서 MongoDB 데이터를 조작하는 데 가장 많이 사용하는 모듈은 mongoose이다. mongoose는 MongoDB를 위한 ODM 모듈인데, 여기서 ODM은 Object Document Mapping의 약자로, 객체(Object)와 문서(Document)를 매핑(Mapping)해 주는 모듈이다. 여기서 객체(Object)는 자바스크립트 객체이고, 문서(Document)는 MongoDB의 문서(데이터)다. 즉, 자바스크립트 객체 데이터를 MongoDB의 문서로 변환해서 저장한다거나 MongoDB 문서를 자바스크립트 객체로 변환해 주는 역할을 해서, Node.js에서 MongoDB를 손쉽게 조작할 수 있도록 해 준다.

1. mongoose 설치


  • 다음 명령어로 Mongoose를 설치한다.
npm install mongoose

2. MongoDB 연결


  • Node.js에서 MongoDB는 mongoose.connect() 함수를 사용해서 연결할 수 있다. 연결 형식은 다음과 같다.
mongoose.connect('mongodb://username:password@host:port/database', options)
  • 이전 글에서 설치한 MongoDB에 접속하기 위해 username은 'root', password는 '1234', host는 'localhost', port는 '27017', database는 'admin'을 사용하고 있다. 여기서 데이터베이스 admin은 인증 연결을 하기 위해 사용한다. 접속 시 사용한 데이터베이스는 admin이지만, 실제 사용할 데이터베이스는 새로 생성한 dev이기 때문에 dbName으로는 dev를 사용한다.
mongoose.connect('mongodb://root:1234@localhost:27017/admin', {dbName: 'dev'})
  • mongoose라는 이름의 폴더를 생성하고 mongoose 폴더 안데 connectDB.js 파일을 생성한 후 다음 코드를 작성한다.
const mongoose = require("mongoose");

const connectDB = async () => {
  try {
    // 콘솔에서 쿼리 내용을 확인할 수 있도록 디버그 모드 활성화
    mongoose.set("debug", true);
    // mongodb://[사용자이름]:[비밀번호]@호스트:포트번호/데이터베이스
    await mongoose.connect("mongodb://root:1234@localhost:27017/admin", {
      dbName: "dev", // 접속할 데이터베이스
    });
  } catch (err) {
    if (err) {
      console.error("MongoDB 연결 에러", err);
    } else {
      console.log("MongoDB 연결 성공", "localhost:27017/admin");
    }
  }
};

// MongoDB 연결 시 에러가 있을 때 발생하는 이벤트 리스너
mongoose.connection.on("error", (err) => {
  console.error("MongoDB 연결 에러", err);
});

// MongoDB 연결이 종료되었을 때 발생하는 이벤트 리스너
mongoose.connection.on("disconnected", () => {
  console.error("MongoDB 연결이 종료되어 연결을 재시도합니다.");
  connect();
});

module.exports = connectDB;
  • 여러 개의 데이터베이스로 연결할 때는 다음과 같이 createConnection() 함수를 사용한다.
const mongoose = require('mongoose');
const conn1 = mongoose.createConnection('mongodb://localhost/mydb1');
const conn2 = mongoose.createConnection('mongodb://localhost/mydb2');

3. 스키마와 모델 생성


  • mongoose에서 스키마는 MongoDB 컬렉션에 들어가는 문서 내부의 각 필드가 어떤 형식으로 되어 있는지 정의하는 객체이다. mongoose가 MongoDB의 데이터베이스에서 실제 작업을 처리하기 위해 모델(model)이란 것을 사용하는데, 이 모델은 스키마를 사용해서 만들 수 있다. 모델은 MongoDB의 데이터에 대한 조회, 생성, 수정, 삭제와 같은 처리를 할 수 있는 다양한 함수를 내장하고 있다.

  • 스키마를 정의할 때 문서의 각 필드에 대해서는 다음과 같은 옵션을 사용할 수 있다.

    • type: 데이터 타입
    • required: 필수 값 여부
    • unique: 유일한 값인지 여부
    • default: 데이터가 없을 경우 기본 값(예: Date.now())
  • MongoDB를 설치하고 todos라는 컬렉션을 생성하였고 다음과 같은 Document를 추가했다.

{
  "task": "test task",
  "importance": "high"
}
  • Document에는 task, importance 필드가 있고 모든 필드는 문자열 타입으로 되어 있다. 여기서 task, importance 모두 필수 값으로 반드시 저장되도록 설정하려고 한다. 이렇게 하면 2개의 필드는 다음과 같이 정의할 수 있다.
필드명typerequireduniquedefault
taskStringtrue
importanceStringtrue
  • 스키마는 다음과 같은 타입을 지원한다.

    • String: 문자열
    • Number: 숫자
    • Date: 날짜
    • Buffer: 파일을 담을 수 있는 버퍼
    • Boolean: 참(true)과 거짓(false)
    • Mixed(Schema.Types.Mixed): 어떤 데이터도 넣을 수 있는 형식
    • Array: 배열
    • ObjectId(Schema.Types.ObjectId): 객체 아이디, 다른 객체(문서)에 대한 참조
  • mongoose 폴더에 models 폴더를 만들고 todo.model.js 파일을 생성하고 다음 코드를 작성한다.

const mongoose = require("mongoose");
const { Schema } = mongoose;

const todoSchema = new Schema(
  {
    task: {
      type: String,
      required: true,
    },
    importance: {
      type: String,
      required: true,
    },
  },
  {
    timestamps: true,
  }
);

// 스키마를 사용해서 모델 생성
const TodoModel = mongoose.model("Todo", todoSchema);

module.exports = TodoModel;
  • mongoose 모듈에서 Schema 생성자를 사용해서 스키마를 정의한다. 이때, _id 필드는 제외하고 나머지 필드에 대해서만 작성한다. MongoDB에서는 컬렉션에 새로운 문서가 추가될 때마다 자동으로 _id에 유일한 키 값이 생성된다.

  • mongoose 모델은 다음과 같이 mongoose.model() 함수를 사용해서 생성할 수 있다.

const TodoModel = mongoose.model("Todo", todoSchema);
  • mongoose.model() 함수는 두 개의 파라미터를 가지고 있다. 첫 번째 파라미터는 스키마 이름이고, 두 번째 파라미터는 스키마 객체다. 일반적으로 스키마 이름의 첫 문자와 모델을 할당하는 변수명의 첫 문자는 대문자를 사용한다.

  • 여기서 중요한 것은 스키마 이름이 정의되면 MongoDB의 데이터베이스에서 스키마 이름의 소문자 복수 형태로 컬렉션이 만들어진다는 것이다. 예를 들어, 스키마 이름이 'Todo'이면 MongoDB의 데이터베이스에 'todos'라는 이름으로 컬렉션이 만들어진다. 이미 동일한 컬렉션이 있다면 해당 컬렉션을 사용하게 된다.

4. 문서 검색(GET)


  • 다음과 같이 express로 웹 서버를 생성한다.
// index.js

const express = require("express");
const cors = require("cors");
const connectDB = require("./mongoose/connectDB"); // mongoose 폴더의 index.js
const apiRouter = require("./routes/api.route");
const app = express();
const port = 8080;

connectDB(); // MongoDB 연결

app.use(cors());
app.use(express.json());

// 기본 경로로 /api 사용
app.use("/api", apiRouter);

app.listen(port, () => {
  console.log(`서버가 실행됩니다. http://localhost:${port}`);
});
// controllers/todo.controller.js

const TodoModel = require("../mongoose/models/todo.model");

const getTodos = async (req, res) => {
  try {
    // localhost:8080/todos 접속 시 실행
    const todos = await TodoModel.find(); // 별도의 조건 없이 todos 컬렉션이 모든 문서 조회
    return res.status(200).json({
      status: "success",
      message: "전체 todo 목록 불러오기 성공",
      data: {
        todos,
      },
    });
  } catch (error) {
    res.status(500).json({
      status: "error",
      message: "Error fetching todos",
      error: {
        code: 500,
        message: "Internal Server Error",
        error,
      },
    });
  }
};

module.exports = {
  getTodos
};
  • Todo 모델에는 todos 컬렉션의 데이터를 조작할 수 있는 함수들이 들어 있다. 이 중 find() 함수는 데이터 조회를 위한 함수다. 현재는 컬렉션에 문서가 한 건만 등록되어 있기 때문에 별도의 조회 조건 없이 함수를 호출했다.
await Todos.find();
  • 이제 index.js를 실행하고 브라우저에서 localhost:8080/todos로 접속하면 아래와 같이 콘솔에 todos 컬렌션에 저장된 Document 조회 결과가 출력된다.

1) Model.find()

  • find() 함수는 다음과 같은 파라미터로 구성되어 있다.

    • filter: 조회 조건, 자바스크립트 Object
    • [projection]: 필드 중 어떤 필드를 가져올지 결정
    • [options]: sort(정렬), limit(출력 개수 제한), skip(출력 데이터 생략) 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// 전체 문서를 모두 조회한다.
await MyModel.find({});

// name이 john이고, age가 18보다 큰 문서를 조회한다.
await MyModel.find({ name: 'john', age: { $gte: 18 }}).exec();

// 쿼리를 실행하고, 콜백 함수로 데이터를 전달한다.
MyModel.find({ name: 'john', age: { $gte: 18 }}, (err, docs) => {});

// LIKE 검색. name에 john을 포함한 문서를 가져오되, name, email 2개의 필드만을 가져온다.
await MyModel.find({ name: /john/i }, 'name email').exec();

// 쿼리를 실행하고, 첫 10개의 데이터를 생략하고 11번째 데이터부터 가져온다.
await MyModel.find({ name: /john/i }, null, { skip: 10 }).exec();

2) Model.findById()

  • findById() 함수는 다음과 같은 파라미터로 구성되어 있다.

    • id: 문서의 _id 값
    • [projection]: 필드 중 어떤 필드를 가져올지 결정
    • [options]: sort(정렬), limit(출력 개수 제한), skip(출력 데이터 생략) 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// 주어진 id 값에 해당하는 문서 한 건을 가져온다.
await MyModel.findById(id).exec();

// 쿼리 결과를 콜백 함수로 전달
MyModel.findById(id, (err, doc) => {});

// 쿼리를 실행하고 name, email 필드만 가져온다.
await MyModel.findById(id, 'name email').exec();

3) Model.findOne()

  • findOne() 함수는 finde() 함수와 사용하는 방법이 동일하며, 하나의 문서만을 결과로 반환한다. 다음과 같은 파라미터로 구성되어 있다.

    • [condition]: 조회 조건, 자바스크립트 Object
    • [projection]: 필드 중 어떤 필드를 가져올지 결정
    • [options]: sort(정렬), limit(출력 개수 제한), skip(출력 데이터 생략) 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// name이 john인 문서 한 건만 반환
await MyModel.findOne({ name: 'john' }).exec();

// 쿼리 실행 결과를 콜백 함수로 전달
MyModel.findOne({ name: 'john' }, (err, doc) => {});

// 쿼리를 실행하고 name, email 필드만 가져온다.
await MyModel.findOne({ name: 'john' }, 'name email').exec();

5. 문서 추가(POST)


  • 문서를 추가할 때는 모델의 create() 함수를 사용해서 추가한다. customers 컬렉션에 새로운 문서를 하나 추가할 때는 create() 함수에 자바스크립트 Object로 문서 데이터를 전달하면 된다.
// controllers/todo.controller.js

const createTodo = async (req, res) => {
  try {
    const { task, importance } = req.body;
    await TodoModel.create({
      task,
      importance,
    });
    return res.status(200).json({
      status: "success",
      message: "새로운 todo 등록 성공",
    });
  } catch (error) {
    res.status(500).json({
      status: "error",
      message: "Error creating todos",
      error: {
        code: 500,
        message: "Internal Server Error",
        error,
      },
    });
  }
};

module.exports = { createTodo };
  • 두 개 이상의 문서를 한 번에 추가할 때는 다음과 같이 create() 함수에 배열로 자바스크립트 Object를 전달하면 된다.
await MyModel.create([{...}, {...}])

6. 문서 수정


1) Model.findByIdAndUpdate()

  • findByIdAndUpdate() 함수는 문서의 _id 값으로 조회된 결과에 해당하는 문서를 수정한다. 다음과 같은 파라미터로 구성되어 있다.

    • id: 문서의 _id 값
    • update: 수정할 필드, 자바스크립트 Object
    • [options]: new, lean, session, strict, timestamp, returnOriginal, overwrite 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// controllers/todo.controller.js

const editTodo = async (req, res) => {
  try {
    const { id } = req.params;
    const { task, importance } = req.body;
    await TodoModel.findByIdAndUpdate(id, {
      task,
      importance,
    });
    return res.status(200).json({
      status: "success",
      message: "todo 수정 성공",
    });
  } catch (error) {
    res.status(500).json({
      status: "error",
      message: "Error editing todos",
      error: {
        code: 500,
        message: "Internal Server Error",
        error,
      },
    });
  }
};

module.exports = { editTodo };

2) Model.updateMany()

  • updateMany() 함수는 조회된 모든 문서를 수정한다. 다음과 같은 파라미터로 구성되어 있다.

    • [conditions]: 조회의 조건, 자바스크립트 Object
    • update: 수정할 필드, 자바스크립트 Object
    • [options]: strict, upsert, writeConcern, timestamps 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
await MyModel.updateMany({ name: /Jeremy/ }, { phone: '010-7777-5555' });

3) Model.updateOne()

  • updateOne() 함수는 조회된 결과 중 첫 번째 문서를 수정한다. 다음과 같은 파라미터로 구성되어 있다.

    • [conditions]: 조회의 조건, 자바스크립트 Object
    • update: 수정할 필드, 자바스크립트 Object
    • [options]: strict, upsert, writeConcern, timestamps 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
await MyModel.updateOne({ name: 'Jeremy Go' }, { phone: '010-7777-5555' });

7. 문서 삭제


1) Model.findByIdAndDelete()

  • Model.findByIdAndDelete() 함수는 요청으로 받은 params의 id값에 해당하는 문서를 찾고 삭제한다. 그리고 삭제된 문서를 반환한다.

    • id: 삭제할 문서의 id 값
    • [options]: projection, lean, sort 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// controllers/todo.controller.js

const deleteTodo = async (req, res) => {
  try {
    const { id } = req.params;
    await TodoModel.findByIdAndDelete(id);
    return res.status(200).json({
      status: "success",
      message: "todo 삭제 성공",
    });
  } catch (error) {
    res.status(500).json({
      status: "error",
      message: "Error removing todos",
      error: {
        code: 500,
        message: "Internal Server Error",
        error,
      },
    });
  }
};

module.exports = router;

2) Model.deleteMany()

  • deleteMany() 함수는 조건에 해당하는 모든 문서를 삭제한다. 다음과 같은 파라미터로 구성되어 있다.

    • [conditions]: 조회의 조건, 자바스크립트 Object
    • [options]: strict, sessions 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// name에 'Jeremy'가 포함된 모든 문서 삭제
await MyModel.deleteMany({ name: /Jeremy/ });

3) Model.deleteOne()

  • deleteOne() 함수는 조회된 결과 중 첫 번째 문서를 삭제한다. 다음과 같은 파라미터로 구성되어 있다.

    • [conditions]: 조회의 조건, 자바스크립트 Object
    • [options]: strict, sessions 등의 옵션
    • [callback]: 콜백 함수로 결과를 전달
// name에 'Jeremy Go'가 포함된 문서 중 첫 번째 문서 삭제
await MyModel.deleteOne({ name: 'Jeremy Go' });

0개의 댓글

관련 채용 정보