보라코딩
Day 28, Node.js + React 본문
인프런 무료 강의
노트랑 리액트 공부하기에 매우 좋고 재밌음 :)
[무료] 따라하며 배우는 노드, 리액트 시리즈 - 기본 강의 - 인프런 | 강의
이 강의를 통해서 리액트와 노드를 어떻게 사용하는지 기본적인 내용들을 배울 수 있습니다., 리액트와 노드의 기본을 학습하세요! 📝 강의 소개 안녕하세요 ^ ^ 이 강의에서는 리액트와 노드
www.inflearn.com
Node + React
1. Node.js, Express.js 설치
2. 몽고 DB 연결 (Atlas로 Cluster 만들기)
3. Mongoose 다운 및 DB 연결
const express = require("express");
const app = express();
const port = 3000;
app.listen(port, () => console.log(`포트 연결 중.. ${port}!`));
const mongoose = require("mongoose");
mongoose
.connect(
"본인코드!!!",
{
useNewUrlParser: true,
useUnifiedTopology: true,
// useCreateIndex: true,
// useFindAndModify: false,
}
)
.then(() => console.log("몽고DB connected!"))
.catch((err) => console.log(err));
app.get("/", (req, res) => res.send("Hello world~~"));
4. MongoDB Model&Schema
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);
// 모델을 다른 파일에서도 사용할 수 있게 export
module.exports = { User };
5. Git 연결 (SSH로 안전하게 연결)
// SSH 설정하기
github generating ssh 검색
ssh key 생성 후 github 설정가서 ssh key 등록하기
<윈도우 SSH 확인방법>
cd ~/.ssh
ls
6. Body-parser 설치
// 클라이언트에서 오는 정보를 서버에서 분석해서 가져올 수 있게
const bodyParser = require("body-parser");
// application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// application/json
app.use(bodyParser.json());
7. Rest Client 사용 (test.rest 파일 만들어서 test하면 postman 대체 가능)
GET http://localhost:3000/ HTTP/1.1
###
POST http://localhost:3000/register/ HTTP/1.1
Content-Type: application/json; charset=UTF-8
{
"name": "gaeun",
"email": "gaeun@gaeun.com",
"password": "1234abcd"
}
8. NODE MON 다운로드
- 코드 변경했을 때 서버 내리고 재시작하지 않아도 변경된 부분 바로 반영됨
- npm i nodemon --save-dev (develop 일때만 쓰겠다는 의미)
- package.json에서 내용 추가하고 이제부터 시작할 땐 npm run backend
현재 package.json
{
"name": "node_react",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"backend" : "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2",
"mongoose": "^7.5.2"
},
"devDependencies": {
"nodemon": "^3.0.1"
}
}
9. 비밀 설정 정보 관리
- mongoDB URI 관리를 위해 config 폴더 아래 dev.js key.js prod.js 파일을 생성함
// key.js
if (process.env.NODE_ENV === "production") {
module.exports = require("./prod");
} else {
module.exports = require("./dev");
}
// dev.js
module.exports = {
mongoURI:
"본인꺼!!",
};
// prod.js
module.exports = {
mongoURI: process.env.MONGO_URI,
};
10. Bcrypt로 비밀번호 암호화 (아래 순서로 진행)
- npm i bcrypt 로 설치
- saltRounds = 10 (10 자리인 salt)
- salt 생성
- salt 이용해서 비밀번호 암호화
User.js 코드 변경
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10; // 10자리인 salt 만들기
// 스키마
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,
},
});
// save 하기 전에 이 코드를 수행함
userSchema.pre("save", function (next) {
var user = this; // this는 userSchema 의미함
if (user.isModified("password")) {
// 비밀번호 암호화
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function (err, hash) {
// user.password는 순수한 비밀번호이고 hash는 암호화된 비밀번호
if (err) return next(err);
user.password = hash;
next();
});
});
} else {
next();
}
});
// 스키마를 모델로 감싸주기
// mongoDB에 users 테이블 생성됨
const User = mongoose.model("User", userSchema);
// 모델을 다른 파일에서도 사용할 수 있게 export
module.exports = { User };
11. 로그인하기 (+ 토큰 생성)
User.js
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10; // 10자리인 salt 만들기
const jwt = require("jsonwebtoken");
// 스키마
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,
},
});
// save 하기 전에 이 코드를 수행함
userSchema.pre("save", function (next) {
var user = this; // this는 userSchema 의미함
if (user.isModified("password")) {
// 비밀번호 암호화
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function (err, hash) {
// user.password는 순수한 비밀번호이고 hash는 암호화된 비밀번호
if (err) return next(err);
user.password = hash;
next();
});
});
} else {
next();
}
});
userSchema.methods.comparePassword = async function (plainPassword) {
try {
// plainPassword 1234567 이런 것 this.password는 암호화된 패스워드
const isMatch = await bcrypt.compare(plainPassword, this.password);
return isMatch;
} catch (err) {
throw err;
}
};
userSchema.methods.generateToken = async function () {
const user = this;
// jsonwebtoken을 이용해서 token을 생성하기
const token = jwt.sign(user._id.toHexString(), "secretToken");
user.token = token;
try {
await user.save();
return user;
} catch (err) {
throw err;
}
};
// 스키마를 모델로 감싸주기
// mongoDB에 users 테이블 생성됨
const User = mongoose.model("User", userSchema);
// 모델을 다른 파일에서도 사용할 수 있게 export
module.exports = { User };
index.js
const express = require("express");
const app = express();
const port = 3000;
const { User } = require("./models/User");
const cookieParser = require("cookie-parser");
// 클라이언트에서 오는 정보를 서버에서 분석해서 가져올 수 있게
const bodyParser = require("body-parser");
// application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// application/json
app.use(bodyParser.json());
app.use(cookieParser());
const mongoose = require("mongoose");
const config = require("./config/key");
mongoose
.connect(config.mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
// useCreateIndex: true,
// useFindAndModify: false,
})
.then(() => console.log("몽고DB connected!"))
.catch((err) => console.log(err));
app.get("/", (req, res) => res.send("Hello world~~"));
// 회원가입
app.post("/register", async (req, res) => {
// 회원가입 할때 필요한 정보들을 client에서 가져와서
// 데이터에 넣어준다
const user = new User(req.body);
try {
// save 메서드를 프로미스로 대체하여 사용
await user.save();
return res.status(200).json({
success: true,
});
} catch (err) {
return res.json({ success: false, err });
}
});
// 로그인
app.post("/login", async (req, res) => {
try {
// 요청된 이메일을 데이터베이스에 있는지 찾는다
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res.json({
loginSuccess: false,
message: "제공된 이메일에 해당하는 유저가 없습니다.",
});
}
// 요청된 이메일이 데이터베이스에 있다면 비밀번호가 맞는 비밀번호인지 확인
const isMatch = await user.comparePassword(req.body.password);
if (!isMatch) {
return res.json({
loginSuccess: false,
message: "비밀번호가 틀렸습니다.",
});
}
// 비밀번호가 맞다면 token 생성하기
await user.generateToken();
// 토큰을 저장한다. 어디에? [쿠키] 또는 로컬스토리지 또는 세션
res
.cookie("x_auth", user.token)
.status(200)
.json({ loginSuccess: true, userId: user._id });
} catch (err) {
console.error(err);
res.status(500).json({ loginSuccess: false, message: "서버 에러" });
}
});
app.listen(port, () => console.log(`포트 연결 중.. ${port}!`));
12. Auth 기능
- Auth 필요한 이유 : 페이지 이동 시, 로그인 되어 있는지 또는 관리자 유저인지 체크. 글쓸때 권한 있는지 체크
- 앞에서
user._id + secretToken = token
이였고 - token을 decode하면 user._id를 알 수 있다!
middleware/auth.js
const { User } = require("../models/User");
// 인증 처리 하는 곳
// 유저 있으면 인증 OK, 유저 없으면 인증 No
let auth = async (req, res, next) => {
try {
// 클라이언트에서 쿠키에서 토큰을 가져온다
let token = req.cookies.x_auth;
// 토큰을 복호화 후 유저를 찾는다
const user = await User.findByToken(token);
if (!user) {
return res.json({ isAuth: false, error: true });
}
// user가 있는 경우
req.token = token;
req.user = user;
next(); // next 해야 다음으로 넘어갈 수 있게
} catch (err) {
console.error(err);
//res.status(500).json({ isAuth: false, error: true, message: "인증 에러" });
}
};
module.exports = { auth };
index.js 에 추가된 부분
// auth 인증
app.get("/api/users/auth", auth, async (req, res) => {
// 여기까지 미들웨어 통과했다는 뜻 = Authentication이 True라는 의미
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true, // role 0 : 일반유저 0이 아니면 관리자
isAuth: true,
email: req.user.email,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image,
});
});
13. 로그아웃
index.js 만 바꿔주면 된다
// 로그아웃
app.get("/api/users/logout", auth, async (req, res) => {
try {
// auth middleware에서 가져온 req.user를 이용해 로그아웃 처리
const user = await User.findOneAndUpdate(
{ _id: req.user._id },
{ token: "" }
);
if (!user) {
return res.json({ success: false, message: "로그아웃에 실패했습니다." });
}
return res.status(200).send({
success: true,
message: "로그아웃에 성공했습니다.",
});
} catch (err) {
console.error(err);
}
});
참고자료
'개발자가 되었다?' 카테고리의 다른 글
Day 30, React (0) | 2023.09.26 |
---|---|
Day 29, React (0) | 2023.09.25 |
Day 27, Typescript (0) | 2023.09.22 |
Day 26, Node.js (0) | 2023.09.20 |
Day 25, JS, React (0) | 2023.09.19 |