[mongoDB] Node.js로 MongoDB 다루기

9rganizedChaos·2021년 7월 17일
1
post-thumbnail

🙌🏻 해당 글은 김시훈님의 mongoDB 기초부터 실무까까지의 강의 노트입니다.

Express에서 mongoDB 사용하기

npm mongodb도 있지만, npm mongoose를 사용하기로 한다. 기본적으로는 mongoDB의 npm package라는 점에서 기능은 비슷하지만, mongoose에는 mongoDB사용을 편하게 해주는 다양한 메서드들이 존재한다. 때문에 npm install mongoose로 설치해 import 해준다.

const express = require("express");
const app = express();
const mongoose = require("mongoose");

const MONGO_URI =
  "mongodb+srv://admin:<password>@mongodbtutorial.ztbzd.mongodb.net/<dbname>?retryWrites=true&w=majority";

mongoose.connect(MONGO_URI);

위 이미지 화면에서 URI를 가져와서 설정해주면 mongoDB 연결에 성공!

app 세팅하기

const server = async () => {
  try {
    await mongoose.connect(MONGO_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true, // 자동 인덱스 생성을 위한 옵션
      useFindAndModify: false, // deprecated
    });
    mongoose.set("debug", true);
    console.log("MongoDB connected");
    app.use(express.json()); // json 파상을 위한 미들웨어

    app.use("/user", userRouter); // 후에 라우터 연결을 위한 미들웨어

    app.listen(3000, () => console.log("server listening on port 3000"));
  } catch (err) {
    console.log(err);
  }
};

server();

debugger 연결

mongoose.set("debug", true);

이와 같이 설정하면 mongoose가 어떤 과정을 거치는지 콘솔을 통해 확인할 수 있다.

user model 만들기

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

const UserSchema = new Schema(
  {
    username: { type: String, required: true, unique: true },
    name: {
      first: { type: String, required: true },
      last: { type: String, required: true },
    },
    age: Number,
    email: String,
  },
  { timestamps: true }
);

const User = model("user", UserSchema);
module.exports = { User };

type: 해당 정보의 타입 지정
required: 필수로 요구되는지에 관한 여부
timestamps: 생성, 수정 시 자동으로 정보 기록 (createdAt, updatedAt)
unique: primary key, 중복값 제외 여부

userRoutes

User 콜렉션 전체 조회

userRouter.get("/", async (req, res) => {
  try {
    // User 콜렉션 전부 조회하기
    const users = await User.find({});
    return res.send({ users });
  } catch (err) {
    console.log(err);
    return res.status(500).send({ err: err.message });
  }
});

특정 user 조회

userRouter.get("/:userId", async (req, res) => {
  try {
    const { userId } = req.params;
    if (!mongoose.isValidObjectId(userId))
      return res.status(400).send({ err: "invalid userId" });
    const user = await User.findOne({ _id: userId });
    return res.send({ user });
  } catch (err) {
    console.log(err);
    return res.status(500).send({ err: err.message });
  }
  // console.log(req.params);
});
  • isValidObjectId() 메서드: 유효한 아이디인지 검증하는 mongoose 내장 메서드

user 정보 추가

userRouter.post("/", async (req, res) => {
  try {
    let { username, name } = req.body;
    if (!username) return res.status(400).send({ err: "username is required" });
    if (!name || !name.first || !name.last)
      return res
        .status(400)
        .send({ err: "Both first and last names are required" });

    const user = new User(req.body);
    await user.save();
    // console.log(req.body);
    // users.push({ name: req.body.name, age: req.body.age });
    // return을 적어주어도 되고 안 적어주어도 되는데,
    // 만약 res.send가 multiple하게 반복될 경우
    // 둘 다 호출된다. 그걸 방지하기 위해 return 작성
    return res.send({ user });
  } catch (err) {
    console.log(err);
    return res.status(500).send({ err: err.message });
  }
});

user 정보 갱신

userRouter.put("/:userId", async (req, res) => {
  try {
    const { userId } = req.params;
    if (!mongoose.isValidObjectId(userId))
      return res.status(400).send({ err: "invalid userId" });
    const { age, name } = req.body;
    if (!age && !name)
      return res.status(400).send({ err: "age or name is required" });
    // if (!age) return res.status(400).send({ err: "age is required" });
    if (age && typeof age !== "number")
      return res.status(400).send({ err: "age must be a number" });
    if (name && typeof name.first !== "string" && typeof name.last !== "string")
      return res.status(400).send({ err: "first and last name are strings" });
    // let updateBody = {};
    // if (age) updateBody.age = age;
    // if (name) updateBody.name = name;

    // const user = await User.findByIdAndUpdate(userId, updateBody, {
    //   new: true,
    // });

    let user = await User.findById(userId);
    if (age) user.age = age;
    if (name) user.name = name;
    await user.save();
    return res.send({ user });
  } catch (err) {
    console.log(err);
    return res.status(500).send({ err: err.message });
  }
});

업데이트 하는 두 가지 방식이 있다.

첫 번째 방식!

    let updateBody = {};
    if (age) updateBody.age = age;
    if (name) updateBody.name = name;

    const user = await User.findByIdAndUpdate(userId, updateBody, {
      new: true,
    });

find와 update를 동시에 하는 방식이다!
이 때 문제는 name 안에 first와 last가 모두 있는지에 대한 검사를 하지 못한다는 것이다.
mongoose가 이를 인지하지 못한 채로 바로 업데이트를 해버린다.

name의 first와 last를 모두 required로 설정했는데, last를 보내지 않았음에도 업데이트가 이루어진 것을 확인할 수 있다.

두 번째 방식!

위에서 발견한 문제를 해결해주기 위해서, find와 update의 과정을 분리해준다!

    let user = await User.findById(userId);
    if (age) user.age = age;
    if (name) user.name = name;
    await user.save();

이번에는 에러가 발생한다!

  • User.findByIdAndUpdate(userId, updateBody, {new: true}) 세번째 전달인자는 갱신을 하고난 후에 수정된 유저의 정보를 전달받고 싶을 때 사용한다.

특정 user 삭제

userRouter.delete("/:userId", async (req, res) => {
  try {
    const { userId } = req.params;
    if (!mongoose.isValidObjectId(userId))
      return res.status(400).send({ err: "invalid userId" });
    const user = await User.findOneAndDelete({ _id: userId });
    return res.send({ user });
  } catch {
    console.log(err);
    return res.status(500).send({ err: err.message });
  }
});
profile
부정확한 정보나 잘못된 정보는 댓글로 알려주시면 빠르게 수정토록 하겠습니다, 감사합니다!

0개의 댓글