회원가입을 완성시켰으니 로그인 기능을 만든다. 프론트에서 유저가 폼에 입력한 데이터를 받아 백엔드로 전송하고, 백엔드는 받은 데이터를 DB에 들어있는 데이터와 비교한다. 만일 입력받은 아이디가 DB에 존재하면, 비밀번호를 DB와 비교한다. 아이디와 비밀번호가 모두 일치할 시 로그인 유지를 위해 토큰을 생성하고, 유저 정보와 성공 메세지를 프론트로 전송한다.
Frontend
회원가입에서 사용한 Register.js 와 원리가 같기 때문에 axios를 제외한 설명은 생략한다. 회원가입때와 마찬가지로 State와 onChange를 사용하여, 입력값이 변할 때 마다 State에 데이터를 저장해주는 형식이다.
State로 받아온 데이터를 백엔드에 작성할 api로 보낸다.
Login.js
로그인에 성공하면 navigate를 이용해 메인 페이지로 이동시킨다.
// < ----------------------------백엔드 api 호출 ---------------------------->
function doLogin() {
let body = {
email:userEmail,
password:userPassword
}
if (userEmail.length===0) {
alert('아이디를 입력해주세요.')
} else if (userPassword. length === 0) {
alert('비밀번호를 입력해주세요')
} else {
axios.post('/api/user/login',body)
.then(response => {console.log('로그인 정보 전송함..', response)
// 로그인 성공했을 경우 redux에 userEmail을 저장
if(response.data.loginSuccess===true) {
navigate('/')
// eslint-disable-next-line no-restricted-globals
location.reload()
// 로그인 실패할 경우 알림창 출력
} else if (response.data.loginSuccess === false) {
alert('이메일 또는 비밀번호가 올바르지 않습니다.')
}
})
}
}
server/router/users.js
첫줄의 [ User.findOne({ email: req.body.email }, (err, user) => {if (!user) ] 는 몽구스의 findOne()메소드를 이용해, 프론트에서 보내온 email이 DB에 존재하는지 검사하는 문장이다.
router.post("/login", (req, res) => {
// 로그인 요청한 이메일을 db에서 찾는다.
User.findOne({ email: req.body.email }, (err, user) => {
if (!user)
return res.json({
loginSuccess: false,
message: "Auth failed, email not found"
});
// 이메일이 db에 있다면 비밀번호를 찾아 검증한다.
user.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({ loginSuccess: false, message: "Wrong password" });
공식 문서 : https://mongoosejs.com/docs/api.html#model_Model-findOne
검사 후 유저가 없으면(!user) find 실패 결과를 반환하고, 이메일이 db에 존재한다면 찾아온 정보를 (err, user)의 user에 담아 반환한다. 이렇게 반환된 user에 들어있는 db 속 password와 입력받은 password(req.body.password)를 서로 비교해준다. 이 메소드는 몽구스 모델 파일인 User.js 에 작성해 주어야 한다.
/server/models/User.js
plainPassword는 comparePassword를 users.js에서 호출할 때 파라미터로 넣어준 req.body.password, 즉 유저가 입력한 생 비밀번호를 가리킨다. bcrypt를 이용해 유저가 입력한 비밀번호와 db 속 비밀번호의 복호화 결과가 같은지를 검사한 후 결과를 cb에 넣어 반환한다.
userSchema.methods.comparePassword = function(plainPassword,cb){
bcrypt.compare(plainPassword, this.password, function(err, isMatch){
if (err) return cb(err);
cb(null, isMatch)
})
}
다시 라우터 파일로 돌아가, 로그인에 성공할 경우 로그인 유지를 위해 토큰을 생성해 주는 문장을 작성한다. 토큰은 세션에 특정한 값을 저장할 수도, 쿠키에 저장할 수도 있지만 보안을 위해 jwt를 사용하기로 했다. 토큰을 쿠키에 저장하여 매 페이지 이동마다 '유저가 로그인 한 상태인지'를 검사해 어떤 페이지에는 진입할 수 있게 하고, 어떤 페이지에는 진입할 수 없게 만든다. 또한 유저의 데이터가 필요한 상황에서도 토큰을 이용해 백엔드로 토큰을 전송하여 user 정보를 불러올 수 있다.
JWT
JWT는 유저를 인증하고 식별하기 위한 토큰 기반 인증 방법이다. 토큰은 서버가 아닌 클라이언트에 저장되므로 서버의 부담을 덜 수 있다. 클라이언트가 아이디, 비밀번호로 웹서버에 인증을 받으면, 서버는 서명된 JWT를 클라이언트에 응답으로 돌려준다. 이후 클라이언트가 추가적인 정보를 요청하기 위해 서버에 접근할 때 JWT를 HTTP Header에 첨부한다. 서버는 JWT를 검증하여 로직을 처리한다.
토큰은 모델 파일 User.js 에서 user._id를 이용해 생성해 줄 것이다. 'secret' 라는 스트링을 사용해 토큰을 HexString()한다.
// 로그인 성공 시 토큰을 생성한다.
userSchema.methods.generateToken = function(cb) {
var user = this;
console.log('user',user)
console.log('userSchema', userSchema)
// 토큰을 생성한다.
var token = jwt.sign(user._id.toHexString(),'secret')
var oneHour = moment().add(1, 'hour').valueOf();
user.tokenExp = oneHour;
user.token = token;
// 생성한 토큰과 함께 db에 저장한다.
user.save(function (err, user){
if(err) return cb(err)
cb(null, user);
})
}
server/router/users.js
다시 라우터로 돌아와 생성한 토큰을 받고, 쿠키에 저장하여 로그인 로직 처리 결과를 프론트엔드로 전달해준다.
router.post("/login", (req, res) => {
// 로그인 요청한 이메일을 db에서 찾는다.
User.findOne({ email: req.body.email }, (err, user) => {
if (!user)
return res.json({
loginSuccess: false,
message: "Auth failed, email not found"
});
// 이메일이 db에 있다면 비밀번호를 찾아 검증한다.
user.comparePassword(req.body.password, (err, isMatch) => {
if (!isMatch)
return res.json({ loginSuccess: false, message: "Wrong password" });
// 로그인에 성공하면 토큰을 생성한다.
user.generateToken((err, user) => {
if (err) return res.status(400).send(err);
// 토큰을 쿠키에 저장한다.
console.log('로그인 성공')
res.cookie("w_authExp", user.tokenExp);
res
.cookie("w_auth", user.token)
.status(200)
.json({
loginSuccess: true, userId: user._id, userRole: user.role
});
});
});
});
});
저번 글에서 가입했던 아이디로 로그인해본다.
메인화면으로 이동되었고, 로그인에 성공하였다. 다음은 유저가 로그인 한 상태인지 검증해주는 유저 인증과 로그아웃을 구현해본다.
'프로젝트 > 풀스택 프로젝트' 카테고리의 다른 글
4-4. [React + Node.js Express] 로그아웃 기능 (0) | 2022.12.05 |
---|---|
4-3. [React + Node.js Express] 유저 인증 기능 (1) | 2022.12.05 |
4-1. [React + Node.js Express] 회원가입 기능 (0) | 2022.12.02 |
3. [ React + Node.js Exrpess ] 리액트와 익스프레스 연결하기 (0) | 2022.11.19 |
1. 무비 API 이용 풀스택 웹 프로젝트 ( React, Node.js express, MongoDB ) (0) | 2022.11.13 |
댓글