1. mongoDB ์ค์
- ์ด์ ํฌ์คํ ์ฐธ๊ณ ํจ.
[Node] Mongo DB ์ฐ๊ฒฐ
1. CLUSTER(ํด๋ฌ์คํฐ) ๋ง๋ค๊ธฐ1) ๋ชฝ๊ณ DB ์ฌ์ดํธ ๊ฐ์ ํ์ ๊ฐ์ 2) CLUSTER(ํด๋ฌ์คํฐ) ๋ง๋ค๊ธฐ - ๋ฌด๋ฃ ๋ฒ์ ์ ํ 2. ๋ชฝ๊ณ DB ์ ์ ์์ฑ1) ๋ชฝ๊ณ DB ์ ์ ์์ฑ- ์ด๋ Username๊ณผ Password๋ ๊ธฐ์ตํด๋๊ฒ!2) ์ดํ๋ฆฌ์ผ์ด์
jaejae-sosp.tistory.com
- ์ด๋ฒ์๋ DB ์ด๋ฆ์ instagram_clone ์ผ๋ก ๋ง๋ค์ด ์ฃผ์์.
/* index.js */
const mongoose = require("mongoose");
// MongoDB ์ฐ๊ฒฐ ์ dbName ์ง์
mongoose
.connect(config.mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
dbName: "instagram_clone", // ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ฆ ์ค์
})
.then(() => console.log("MongoDB connected.."))
.catch((err) => console.log(err));
2. ์คํค๋ง ์์ฑ
1) user DB์ ๋ฃ๊ธฐ ์ํด ์คํค๋ง ์์ฑ
/* user.js */
const userSchema = mongoose.Schema({
user_id: {
type: String,
},
email: {
type: String,
trim: true,
unique: 1,
},
password: {
type: String,
minlength: 5,
},
phone: {
type: String,
maxlength: 11,
},
gender: {
type: String,
},
birth: {
type: String,
Timestamp: true,
},
name: {
type: String,
maxlength: 50,
},
introduce: {
type: String,
maxlength: 100,
},
image: String,
token: {
type: String,
},
role: {
// user๋ ๊ด๋ฆฌ์ ๋๋ ์ผ๋ฐ์ธ
type: Number, // ์๋ฅผ ๋ค์ด, number๊ฐ 1์ด๋ฉด ๊ด๋ฆฌ์, 0์ด๋ฉด ์ผ๋ฐ์ ์
default: 0,
},
tokenExp: {
// token์ด ์ ํจํ๋ ๊ธฐ๊ฐ
type: Number,
},
emailVerificationCode: {
type: String, // ์ธ์ฆ ์ฝ๋๋ฅผ ์ ์ฅํ ํ๋
},
emailVerificationCodeExpires: {
type: Date, // ์ธ์ฆ์ฝ๋ ์ ํจ๊ธฐ๊ฐ
},
isEmailVerified: {
type: Boolean, // ์ด๋ฉ์ผ ์ธ์ฆ ์ฌ๋ถ
default: false, // ๊ธฐ๋ณธ๊ฐ: ์ธ์ฆ๋์ง ์์
},
});
2) ๋น๋ฐ๋ฒํธ ์ํธํ ์ค์
- user.js ์ ์ถ๊ฐ
/* user.js */
userSchema.pre("save", function (next) {
// userModel์ user์ ๋ณด๋ฅผ ์ ์ฅํ๊ธฐ ์ ์ ์ฒ๋ฆฌ๋จ
var user = this;
if (user.isModified("password")) {
// password๊ฐ ๋ณํ๋ ๋๋ง ์ํธํ
// ๋น๋ฐ๋ฒํธ๋ฅผ ์ํธํ ์ํค๊ธฐ
bcrypt.genSalt(saltRounds, function (err, salt) {
// salt ๋ง๋ค๊ธฐ
if (err) return next(err);
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
user.password = hash; // ์ํธํ ํค ๋ง๋๋ ๋ฐ ์ฑ๊ณตํ์ผ๋ฉด, ์๋ ๋น๋ฐ๋ฒํธ๋ hash ๋ฐ๊พธ๊ณ
next(); // index.js๋ก ๋์๊ฐ๊ธฐ
});
});
} else {
// ๋น๋ฐ๋ฒํธ ๋ง๊ณ ๋ค๋ฅธ ๊ฑธ ๋ฐ๊ฟ ๊ฒฝ์ฐ
next(); // next() ์์ผ๋ฉด ๊ณ์ ๋จธ๋ฌผ๊ฒ ๋จ
}
});
3. ํ์๊ฐ์
1) ์ด๋ฉ์ผ๋ก ์ธ์ฆ ์ฝ๋ ์ ์ก
- ํ์๊ฐ์
์ ์ํด ์ ํจํ ์ด๋ฉ์ผ์ ์ฌ์ฉํ๋์ง ํ์ธํด๋ณด๊ณ ์ถ์ด ๋ง๋ ๊ธฐ๋ฅ.
- ์ด๋ฉ์ผ์ ์
๋ ฅํ๊ณ ์ธ์ฆํ๊ธฐ๋ฅผ ๋๋ฅด๋ฉด ์
๋ ฅํ ์ด๋ฉ์ผ๋ก ์ธ์ฆ ์ฝ๋๊ฐ ์ ์ก๋๊ณ ์ ํจ ์๊ฐ ๋ด์ ์ธ์ฆ ์ฝ๋ ์
๋ ฅ์ ์ฑ๊ณตํ๋ฉด ์ ํจํ ์ด๋ฉ์ผ์ด๋ผ๊ณ ๊ฐ์ฃผํ๊ณ ํ์๊ฐ์
์ ์ํํจ.
- nodemailer ๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ํจ.
/* sendEmail.js */
const nodemailer = require("nodemailer");
require("dotenv").config(); // ํ๊ฒฝ๋ณ์
const transporter = nodemailer.createTransport({
service: "gmail", // Gmail ์ฌ์ฉ
auth: {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_PASS,
},
});
// ์ด๋ฉ์ผ ์ ์ก ํจ์
const sendVerificationEmail = async (email, verificationCode) => {
try {
await transporter.sendMail({
from: `"(์์)์ธ์คํ๊ทธ๋จ" <${process.env.GMAIL_USER}>`,
to: email,
subject: "(์์)์ธ์คํ๊ทธ๋จ ํ์๊ฐ์
์ด๋ฉ์ผ ์ธ์ฆ ์ฝ๋",
text: `์ธ์ฆ์ฝ๋: ${verificationCode}`,
});
return true; // ์ด๋ฉ์ผ ์ ์ก ์ฑ๊ณต
} catch (error) {
console.error("Error sending email:", error);
return false; // ์ด๋ฉ์ผ ์ ์ก ์คํจ
}
};
module.exports = { sendVerificationEmail };
โ๏ธ์ฃผ์
- nodemailer ์ ๊ฒฝ์ฐ, transporter ํฌ๋งท์์ ๋ฉ์ผ์ ํ์์ Gmail๋ก ์ค์ ํ์ง ์์ผ๋ฉด ์ธ์ํ ์ ์์.
- ๊ทธ๋์ ๋์ด ๊ผญ @gmail.com ์ผ๋ก ๋๋๊ฑฐ๋ SMTP ๋ฐฉ์์ ์ ํํด์ผํจ.
2) ํ์๊ฐ์
/* signUp.js */
// ํ์๊ฐ์
์ฒ๋ฆฌ ๋ฐ ์ด๋ฉ์ผ ์ธ์ฆ ์ฝ๋ ๋ฐ์ก
router.post("/", async (req, res) => {
const { email } = req.body;
try {
// ์ด๋ฉ์ผ์ด ์ด๋ฏธ ์กด์ฌํ๋์ง ํ์ธ
const existingUser = await User.findOne({ email });
if (existingUser) {
return res
.status(400)
.json({ success: false, message: "์ด๋ฏธ ๊ฐ์
๋ ์ฌ์ฉ์์
๋๋ค." });
}
// ์ด๋ฉ์ผ๋ก ๋ณด๋ผ ์ธ์ฆ๋ฒํธ ์์ฑ
const emailVerificationCode = crypto.randomBytes(3).toString("hex"); // 6์๋ฆฌ ์ฝ๋ ์์ฑ
const user = new User({
...req.body,
emailVerificationCode, // ์์ฑ๋ ์ธ์ฆ์ฝ๋ ์ ์ฅ
emailVerificationCodeExpires: Date.now() + 3600000, // ์ธ์ฆ ์ฝ๋ ์ ํจ์๊ฐ 1์๊ฐ
});
// ์ด๋ฉ์ผ๋ก ์ธ์ฆ๋ฒํธ ์ ์ก
const emailSent = await sendVerificationEmail(email, emailVerificationCode);
if (!emailSent) {
return res
.status(500)
.json({ success: false, message: "์ด๋ฉ์ผ ์ ์ก ์คํจ" });
}
await user.save(); // ์ฌ์ฉ์ ์ ๋ณด ์ ์ฅ
res.status(200).json({ success: true, message: "์ด๋ฉ์ผ ์ ์ก ์ฑ๊ณต" });
} catch (err) {
res.status(500).json({ success: false, err });
}
});
- ๋จผ์ ์ด๋ฏธ ๊ฐ์
๋ ์ฌ์ฉ์์ธ์ง ํ์ธํ๊ณ (๊ฐ์
๋์ด ์๋ ์ฌ์ฉ์๋ฉด ์ง๊ธ์ 400 ์๋ฌ๋ฅผ ๋์ฐ์ง๋ง, ์์ผ๋ก login ํ์ด์ง๋ก ๋ณด๋ผ ์์ )
- ๊ฐ์
์ด ๋์ด ์์ง ์์ ์ฌ์ฉ์๋ผ๋ฉด, ์ธ์ฆ ์ฝ๋๋ฅผ ์์ฑํ๊ณ ์ด๋ฉ์ผ๋ก ์์ฑ๋ ์ธ์ฆ ์ฝ๋๋ฅผ ์ ์กํจ.
/* signUp.js */
// ์ด๋ฉ์ผ ์ธ์ฆ ์ฒ๋ฆฌ
router.post("/verify-email", async (req, res) => {
const { email, verificationCode } = req.body;
try {
const user = await User.findOne({ email });
if (!user)
return res
.status(400)
.json({ success: false, message: "์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค." });
// ์ด๋ฉ์ผ์ด ์ด๋ฏธ ์ธ์ฆ๋์๋์ง ํ์ธ
if (user.isEmailVerified) {
return res.status(400).json({
success: false,
message: "์ด๋ฏธ ์ด๋ฉ์ผ ์ธ์ฆ์ด ์๋ฃ๋์์ต๋๋ค.",
});
}
if (user.emailVerificationCode !== verificationCode) {
return res
.status(400)
.json({ success: false, message: "์ธ์ฆ์ฝ๋๊ฐ ์ผ์นํ์ง ์์ต๋๋ค." });
}
// ์ธ์ฆ ์ฝ๋๊ฐ ๋ง๋ฃ๋์๋์ง ํ์ธ
if (user.emailVerificationCodeExpires < Date.now()) {
return res
.status(400)
.json({ success: false, message: "์ธ์ฆ์ฝ๋๊ฐ ๋ง๋ฃ๋์์ต๋๋ค." });
}
user.isEmailVerified = true; // ์ด๋ฉ์ผ ์ธ์ฆ ์๋ฃ
// ์ด๋ฉ์ผ ์ธ์ฆ ์ฌ๋ถ๋ง true๋ก ๋ณ๊ฒฝ
user.emailVerificationCode = undefined; // ์ธ์ฆ์ฝ๋ ์ญ์
user.emailVerificationCodeExpires = undefined; // ์ธ์ฆ ์ฝ๋ ๋ง๋ฃ ์๊ฐ ์ญ์
await user.save();
res
.status(200)
.json({ success: true, message: "์ด๋ฉ์ผ ์ธ์ฆ์ด ์๋ฃ๋์์ต๋๋ค." });
} catch (err) {
res.status(500).json({ success: false, err });
}
});
- 1์๊ฐ ์ด๋ด๋ก ์ธ์ฆ ์ฝ๋๋ฅผ ์ ๋ ฅํ๊ฒ๋ ๊ตฌํํจ.
- ์ด๋ฉ์ผ ์ธ์ฆ์ด ์๋ฃ๋๋ฉด ์ธ์ฆ์ฝ๋์ ์ธ์ฆ ์ฝ๋ ๋ง๋ฃ ์๊ฐ์ ์ญ์ ๋๊ณ ์ธ์ฆ ์ฌ๋ถ๋ง DB์ ๋จ์.
4. ๊ฒฐ๊ณผ ๐
- ํ๋ก ํธ๊ฐ ์์ง ์๋ ๊ด๊ณ๋ก postman์ ํ์ฉ
1) ์ด๋ฉ์ผ๋ก ์ธ์ฆ ์ฝ๋ ์ ์ก ๋ฐ ํ์๊ฐ์ ์๋
- ์ด๋ฉ์ผ ์ธ์ฆ์ด ์๋ฃ๋๊ธฐ ์ ์ isEmailVerified ๊ฐ false์.
2) ์ด๋ฉ์ผ ์ธ์ฆ ์๋ฃ ๋ฐ ํ์ ๊ฐ์
์๋ฃ
- ์ด๋ฉ์ผ ์ธ์ฆ์ด ์๋ฃ๋๋ฉด isEmailVerified ๊ฐ true ๋ก ๋ฐ๋๊ณ
- emailVerificationCode ํ๋์ emailVerificationCodeExpires ํ๋๊ฐ DB์์ ์ญ์ ๋จ!