Mongo, NodeJs, React를 이용해서 회원가입 로그인을 만들어보쟈
최종 목표는 유튜브 클론코딩이다!
2020.10.15 - [React] - React와 Express연동하기 1
React와 Express연동하기 1
준비 과정 1. CRA npx create-react-app tistory(파일명) 2. npm start Express 설치 1. Express설치 npm install express --save --save는 안붙혀도 된다. --save의 뜻은 종속 항목 목록에 저장한다는 뜻이다. --..
coding-hyeok.tistory.com
2020.10.17 - [React] - React - Mysql 연동하기, 연결(데이터 저장)
React - Mysql 연동하기, 연결(데이터 저장)
1. 준비 mysql mysql workbench를 사용할 것이다. 2. database 만들기 및 express에 연결하기 database와 table을 만들어주자. 나는 tistory라는 database와 test라는 table을 만들거다. CREATE DATABASE tistory;..
coding-hyeok.tistory.com
이 두개는 기본이고 몽고까지 연결하고 와야 이해가 될 것이다
폴더구조
server/index.js
// server/index.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const config = require("./config/key");
const { auth } = require("./middleware/auth");
const { User } = require("./models/User");
//서버에서 가져온 데이터를 파싱해서 가져와준다.
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
const mongoose = require("mongoose");
mongoose
.connect(config.mongoURI, {
useNewUrlParser: true,
useUnifiedTopology: true,
//useCreateIndex: true,
//useFindAndModify: false,
})
.then(() => console.log("MongoDB Connected ... "))
.catch((err) => console.log(err));
app.get("/", (req, res) => {
res.send("Hello World!");
});
//회원가입
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,
});
});
});
//로그인
app.post("/api/users/login", (req, res) => {
//1. 요청된 이메일이 데이터베이스에 있는 지 찾는다.
User.findOne({ email: req.body.email }, (err, user) => {
if (!user) {
return res.json({
loginSuccess: false,
message: "이메일에 해당하는 유저가 없습니다.",
});
}
//2. 1조건 충족 시, 비번 맞는 지 확인
user.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({
loginSuccess: false,
message: "비밀번호가 틀렸습니다.",
});
//3. 2조건 충족 시, 토큰 생성
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
//토큰을 저장한다. where? 쿠키 OR 로컬 스토리지 OR 세션스토리지
//쿠키 name : value
res
.cookie("x_auth", user.token)
.status(200)
.json({ loginSuccess: true, userId: user._id });
});
});
});
});
//Authentication 자격 (관리자화면, 비회원화면)
//auth는 미들웨어
app.get("/api/users/auth", auth, (req, res) => {
//여기까지 오면 미들웨어를 통과했다는 거임
//그렇다면 Auth가 True라는 뜻
res.status(200).json({
_id: req.user._id,
// 0> 일반 유저 ^ 나머지 관리자
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
lastname: req.user.lastname,
role: req.user.role,
image: req.user.image,
});
});
//LogOut
app.get("/api/users/logout", auth, (req, res) => {
User.findOneAndUpdate({ _id: req.user._id }, { token: "" }, (err, user) => {
if (err) return res.json({ success: false, err });
return res.status(200).send({ success: true });
});
});
const port = 5000;
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
간단한 회원가입과 로그인시 아이디 검사 > 비번검사를 했다.
그리고 로그인시 쿠키생성해서 토큰을 주고 로그아웃 시에는 토큰이 없어지게 해따
server/models/User.js
//server/models/User.js
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10;
const jwt = require("jsonwebtoken");
const userSchema = mongoose.Schema({
name: {
type: String,
maxlength: 150,
},
email: {
type: String,
trim: true,
unique: 1,
},
password: {
type: String,
minlength: 5,
},
lastname: {
type: String,
maxlength: 150,
},
role: {
type: Number,
default: 0,
},
image: String,
token: {
type: String,
},
//토큰 유효기간
tokenExp: {
type: Number,
},
});
//mongoose 기능 pre > save 전에 뭘한다
userSchema.pre("save", function (next) {
var user = this;
if (user.isModified("password")) {
//비밀번호 암호화 bcrypt
//salt 생성 (saltRounds= 10)
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) return next(err);
//this.password = myPlaintextPassword
bcrypt.hash(user.password, salt, function (err, hash) {
// Store hash in your password DB.
if (err) return next(err);
user.password = hash;
next();
});
});
} else {
next();
}
});
userSchema.methods.comparePassword = function (plainPassword, cb) {
// plainPassword : 123456 / 암호화된 비번 : #!@#1241@$1~!asd
//plainPassword 암호화 해서 비교한다
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을 이용하여 토큰 생성 user._id는 mongo id
// user._id + 'secretToken' = token
//jwt.sign(payload, secretKey)이 기대값
//user_.id는 문자열이 아니기 때문에 .toHexString으로 24바이트 16진수 문자열로 바꿔줌?
var token = jwt.sign(user._id.toHexString(), "secretToken");
user.token = token;
user.save(function (err, user) {
if (err) return cb(err);
cb(null, user);
});
};
userSchema.statics.findByToken = function (token, cb) {
var user = this;
//token decode
jwt.verify(token, "secretToken", function (err, decoded) {
//유저 아이디를 이용해서 유저를 찾은 다음에
//클라이언트에서 가져온 토큰과 디비에 보관된 토큰이 일치하는지 확인
user.findOne({ _id: decoded, token: token }, function (err, user) {
if (err) return cb(err);
cb(null, user);
});
});
};
const User = mongoose.model("User", userSchema);
module.exports = { User };
bcrypt라이브러리를 사용하여 사용자 정보를 저장하기 전에 비번을 암호화 했다. (pre부분)
그리고 findByToken은 유저 아이디를 이용해서 유저를 찾고 클라이언트에서 가져온 토큰과 디비에 보관된 토큰이 일치하는 지 확인해서
유저가 로그인했는지 안했는지를 판단한다.
generateToken : 토큰 생성
jwt.token(payload, secretKey)가 기대값이다
user_.id는 문자열이 아니기 때문에 .toHexString()으로 문자열로 변환시켜줘서 토큰을 생선한다
(toHexString 없으면 오류납뉘당)
comparePassword : 암호화해서 비밀번호가 맞는지 확인하는 부분
server/middleware/auth.js
const { User } = require("../models/User");
let auth = (req, res, next) => {
//인증 처리를 하는 곳
//1. 클라이언트 쿠키에서 토큰을 가져온다.
let token = req.cookies.x_auth;
//2. 토큰을 복호화한다 > 유저를 찾는다.
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();
});
//3. 2조건 만족시 Okay
//4. 2조건 불만족시 NO
};
module.exports = { auth };
인증처리하는 부분이답
여기까지가 서버부분(백엔드)이다.
이제 클라이언트(프론트엔드)로 가보쟈
폴더 및 파일 구조는 요렇다
registerpage.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { registerUser } from "../../../_actions/user_actions";
import { useNavigate } from "react-router-dom";
function RegisterPage(props) {
const dispatch = useDispatch();
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [name, setName] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const onEmailHandler = (e) => {
setEmail(e.target.value);
};
const onNamedHandler = (e) => {
setName(e.target.value);
};
const onPasswordHandler = (e) => {
setPassword(e.target.value);
};
const onConfirmPasswordHandler = (e) => {
setConfirmPassword(e.target.value);
};
const onSubmitHandler = (e) => {
//새로고침 방지
e.preventDefault();
if (password !== confirmPassword) {
return alert("비밀번호와 비밀번호확인은 같아야 합니다.");
}
let body = {
email: email,
name: name,
password: password,
};
dispatch(registerUser(body)).then((response) => {
if (response.payload.success) {
navigate("/login");
} else {
alert("Failed to sign up");
}
});
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}
>
<form
style={{ display: "flex", flexDirection: "column" }}
onSubmit={onSubmitHandler}
>
<label>Email</label>
<input type="email" value={email} onChange={onEmailHandler} />
<label>Name</label>
<input type="text" value={name} onChange={onNamedHandler} />
<label>Password</label>
<input type="password" value={password} onChange={onPasswordHandler} />
<label>Password Confirm</label>
<input
type="password"
value={confirmPassword}
onChange={onConfirmPasswordHandler}
/>
<br />
<button type="submit">Sign UP</button>
</form>
</div>
);
}
export default RegisterPage;
보면 알겟지만 redux를 사용했다
client/src/_action/user_actions.js
import axios from "axios";
import { LOGIN_USER, REGISTER_USER, AUTH_USER } from "./types";
//로그인
export function loginUser(dataTosubmit) {
const request = axios
.post("/api/users/login", dataTosubmit)
.then((response) => response.data);
return {
//
type: LOGIN_USER,
payload: request,
};
}
//회원가입
export function registerUser(dataTosubmit) {
const request = axios
.post("/api/users/register", dataTosubmit)
.then((response) => response.data);
return {
type: REGISTER_USER,
payload: request,
};
}
//인증처리
export function auth() {
const request = axios
.get("/api/users/auth")
.then((response) => response.data);
return {
type: AUTH_USER,
payload: request,
};
}
axios를 통해 서버와 통신했다
client/src/_reducers/user_reducers.js
import { LOGIN_USER, REGISTER_USER, AUTH_USER } from "../_actions/types";
export default function (state = {}, action) {
switch (action.type) {
case LOGIN_USER:
return { ...state, loginSuccess: action.payload };
break;
case REGISTER_USER:
return { ...state, register: action.payload };
break;
case AUTH_USER:
return { ...state, userData: action.payload };
break;
default:
return state;
}
}
마지막으로 로그인
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { loginUser } from "../../../_actions/user_actions";
import { useNavigate } from "react-router-dom";
function LoginPage(props) {
const dispatch = useDispatch();
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const onEmailHandler = (e) => {
setEmail(e.target.value);
};
const onPasswordHandler = (e) => {
setPassword(e.target.value);
};
const onSubmitHandler = (e) => {
//새로고침 방지
e.preventDefault();
let body = {
email: email,
password: password,
};
dispatch(loginUser(body)).then((response) => {
if (response.payload.loginSuccess) {
navigate("/");
} else {
alert("ERROR");
}
});
};
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "100%",
height: "100vh",
}}
>
<form
style={{ display: "flex", flexDirection: "column" }}
onSubmit={onSubmitHandler}
>
<label>Email</label>
<input type="email" value={email} onChange={onEmailHandler} />
<label>Password</label>
<input type="password" value={password} onChange={onPasswordHandler} />
<br />
<button type="submit">Login</button>
</form>
</div>
);
}
export default LoginPage;
요렇게하면
서버에 잘 저장 되는 걸 볼 수 있다.
로그인하면 토큰이 잘 생성되는 것을 볼 수 있따!
끝!!
취업하고 싶다ㅏ~~~
취준생들 화팅~! 모두다 열코염
'React' 카테고리의 다른 글
[React] node router설정 (0) | 2022.01.11 |
---|---|
[TS] 타입스크립트로 간단한 블록체인 만들어보귀 (0) | 2022.01.07 |
[React] 로그인 시 사용자 정보 storage(저장소)에 넣기 (localstorage VS sessionstrorage) (0) | 2022.01.07 |
[React] 절대경로 설정하기 (0) | 2022.01.06 |
[React] react router (v6) (1) | 2021.12.16 |