🙌🏻 해당 글은 김시훈님의 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 연결에 성공!
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();
mongoose.set("debug", true);
이와 같이 설정하면 mongoose가 어떤 과정을 거치는지 콘솔을 통해 확인할 수 있다.
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, 중복값 제외 여부
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 });
}
});
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 내장 메서드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 });
}
});
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})
세번째 전달인자는 갱신을 하고난 후에 수정된 유저의 정보를 전달받고 싶을 때 사용한다.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 });
}
});