FE : React
BE : Node.js
DB : MongoDB

node.js 설치
boiler-plate 디렉토리를 만든다.
해당 폴더 안에서 npm init (기본 설정을 따라간다. enter!)
package.json 에서 해당 설정을 볼 수 있다.express.js 설치 : boiler-plate $ npm install express --save
--save 구문은 package.json에 자동으로 dependency를 추가해준다.index.js 파일을 만든다. 그리고 package.json에 start를 수정한다.
// index.js
const express = require('express')
const app = express()
const port = 7777
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
// package.json
"scripts": {
"start": "node index.js", // <- 추가
"test": ...
},
이제 npm run start 로 시작할 수 있다.
몽고DB를 사용하기 위한 프레임워크인 mongoose를 설치 : npm install mongoose --save
몽고DB 홈페이지에서 클러스터 생성. 접속 userid, password를 만들어준다.
user키를 복사후, index.js에 다음을 추가한다.
// index.js
const mongoose = require('mongoose')
mongoose.connect('mongodb+srv://led:<password>@boilerplate.7zof7an.mongodb.net/?retryWrites=true&w=majority')
.then(() => console.log('MongoDB Connect...'))
.catch(err => console.log(err))
유저 모델을 생성할 것이다. models 폴더 생성. User.js를 생성하고 다음을 작성.
// models/User.js
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
name: {
type: String,
maxlength: 50
},
email: {
type: String,
trim: true,
unique: 1
},
password: {
type: String,
minlength: 5
},
lastname: {
type: String,
maxlength: 50
},
role: {
type: Number,
default: 0
},
Image: String,
token: {
type: String
},
tokenExp: {
type: Number
}
})
const User = mongoose.model('User', userSchema)
module.exports = { User }
npm install body-parser --save --no-fund다만 요즘은, express에 기본적으로 포함되어 있음.
// index.js에 해당코드 추가
app.use(express.json());
app.use(express.urlencode({extended: true}));
// index.js
const bodyParser = require('body-parser')
const { User } = require("./models/User") // 유저 모델 가져오기
...
// application/x-www-form-urlencoded 분석 가능
app.use(bodyParser.urlencoded({extended: true}));
// application/json 분석 가능
app.use(bodyParser.json())
...
app.post('/api/users/register', (req, res) => { // 클라이언트에서 가져온 정보를 데이터베이스에 넣는다.
const user = new User(req.body) // 바디파서로 조각조각낸 정보
user.save((err, userInfo) => { // 유저 모델에 저장. +콜백함수
if (err) return res.json({ success: false, err})
return res.status(200).json({
success: true
})
})
})

npm install nodemon --save-devnpm run dev// package.json
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js", // <- 추가.
"test": "echo \"Error: no test specified\" && exit 1"
},key.js . local 환경(개발환경)에서는 dev, 배포환경에서는 prod
// key.js
if(process.env.NODE_ENV === 'production') {
module.exports = require('./prod');
} else {
module.exports = require('./dev');
}
// dev.js
module.exports ={
mongoURI: 'mongodb+srv://led:led1234@boilerplate.7zof7an.mongodb.net/?retryWrites=true&w=majority'
}
// prod.js
module.exports = {
mongoURI: process.env.MONGO_URI
}
// index.js
const config = require('./config/key');
...
mongoose.connect(config.mongoURI) // 변수로 교체!
.then(() => console.log('MongoDB Connect...'))
.catch(err => console.log(err))
비밀번호 암호화를 위해 bcrypt 설치 : npm install bcrypt --save
user.js에 bcrypt 추가
// user.js
const bcrypt = require('bcrypt');
const saltRounds = 10; // salt를 이용해 비밀번호를 암호화. (saltRounds = 몇글자인지)
비밀번호 변경 시 암호화해서 저장해주는 함수 추가.
// user.js
userSchema.pre('save', function( next ) {
var user = this;
if (user.isModified('password')) { // 패스워드를 바꿀 때만 해당 함수 실행 (이메일 등등 해당X)
// 비밀번호 암호화
bcrypt.genSalt(saltRounds, function (err, salt) {
if(err) return next(err)
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err)
user.password = hash
next()
})
});
} else {
next()
}
}) // 몽구스를 이용. save 전에 해당 함수를 실행. 끝난 후 next로 돌아감.

jsonwebtoken 추가 : npm install jsonwebtoken --save
comparePassword. plainpw를 받아오고, 유저의 비밀번호와 bcrypt.compare로 비교한다.generateToken.// user.js
const jwt = require('jsonwebtoken');
...
userSchema.methods.comparePassword = function(plainPassword, cb) {
bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
})
}
userSchema.methods.generateToken = function(cb) {
var user = this;
// jsonwebtoken 이용하여 토큰 생성.
var token = jwt.sign(user._id.toHexString(), 'secretToken')
// user._id + 'secretToken' = token ... 'secretToken' -> user._id . => 따라서 이때 만든 토큰도 기억해줘야 함.
user.token = token
user.save(function (err, user) {
if(err) return cb(err)
cb(null, user)
})
}
쿠키파서 설치 : npm install cookie-parser --save
comparePasswordgenerateToken// index.js
const cookieParser = require('cookie-parser')
...
app.use(cookieParser())
...
app.post('/login', (req, res) => {
// 요청된 이메일이 데이터베이스에서 있는지 찾음.
User.findOne({ email: req.body.email }, (err, user) => {
if(!user) {
return res.json({
loginSuccess: false,
message: "이메일에 해당하는 유저가 없습니다."
})
}
// 있다면, 비밀번호가 맞는 비밀번호인지 확인한다.
user.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다."})
// 둘 다 맞다면, 유저를 위한 토큰 생성.
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
// 토큰을 저장한다.
res.cookie("x_auth", user.token)
.status(200)
.json({ loginSuccess: true, userId: user._id })
})
})
})
})

: 로그인이 되어있는지, 권한이 부여되어 있는지를 확인하기 위해 필요한 기능. 토큰을 가져와 유저 토큰과 비교!
// index.js
const { auth } = require("./middleware/auth")
...
app.get('/api/users/auth', auth, (req, res) => {
// 미들웨어가 있으므로, 여기까지 온거면, authentication이 true인것.
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.name,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image
})
})
// auth.js
const { User } = require("../models/User");
let auth = (req, res, next) => {
// 클라이언트 쿠키에서 토큰을 가져오기
let token = req.cookies.x_auth;
// 토큰 복호화 후, 유저 찾기
User.findByToken(token, (err, user) => {
if (err) throw err;
if (!user) return res.json({ isAuth: false, error: true})
req.token = token;
req.user = user;
next();
})
// 유저O -> 인증O
} // 인증 처리 하기.
module.exports = { auth };
// User.js
userSchema.statics.findByToken = function(token, cb) {
var user = this;
// 토큰 디코드
jwt.verify(token, 'secretToken', function(err, decoded) {
// 복호화하여 나온 유저 아이디를 찾는다.
user.findOne({"_id": decoded, "token": token}, function(err, user) {
if (err) return cb(err);
cb(null, user);
})
})
}
로그인할때 부여한 토큰을 지워 로그아웃한다.
// index.js
app.get('/api/users/logout', auth, (req, res) => {
User.findByIdAndUpdate({ _id: req.user._id},
{ token: "" }
, (err, user) => {
if (err) return res.json({ success: false, err });
return res.status(200).send({
success: true
})
})
})

