쇼핑몰//회원가입-로그인 api 구현 참고용 총정리
큰 틀 (장바구니가 완성되어있을 경우)
회원가입기능->로그인기능->사용자인증 미들웨어->내 정보조회->상품수정->장바구니수정
결국 쿠키가 뭘 의미하는가? 로그인할때 인증을 도와줄 키를 가지고있다.
1.email, nickname, password, confirmPassword를 전달 받음
2. password, confirmPassword가 동일한지 검증
3. email과 nickname 값이 이미 DB에 존재하는지 검증
4. email, nickname, password를 DB에 저장 // 이건 user 스키마를 통해 들어간다
5. 회원가입 성공
1. app.js 생성
npm iniy -y
npm install express
npm install mongoose 등 필요한 라이브러리 싹다설치
로그인 구현할 때는 뭐 설치해야되는지 확인요망
아래와 같은 형식으로 설정을 해준다.
const express = require('express');
const app = express();
const port = 3000;
const goodsRouter = require("./routes/goods");
const cartsRouter = require("./routes/carts.js");
const connect = require("./schemas");
connect();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static("assets"));
app.use("/api", [goodsRouter, cartsRouter]);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(port, '포트로 서버가 열렸어요!');
});
2. user스키마 생성하기
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
email: {
// 중복되면안됌
type: String,
required: true,
unique: true,
},
nickname: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});
UserSchema.virtual('userId').get(function () {
return this._id.toHexString();
});
UserSchema.set('toJSON', {
virtuals: true, //JSON 형태로 가공할 때 userId를 출력 시켜준다.
});
module.exports = mongoose.model('USER', UserSchema);
3. 정보 받아올 users.routes 생성하기
const express = require('express');
const router = express.Router();
const User = require('../schemas/user');
// 회원가입 API
router.post('/users', async (req, res) => {
const { email, nickname, password, confirmPassword } = req.body;
// 비번과 확인용 비번이 같은지 확인
if (password !== confirmPassword) {
res.status(400).json({
errorMessage: '패스워드가 패스워드 확인값과 다릅니다.',
});
return;
}
// email 또는 nickname이 동일한 데이터가 있는지 확인하기 위해 가져온다.
const existsUsers = await User.findOne({
$or: [{ email }, { nickname }], //이메일 또는 닉네임이 일치할 때 조회한다. $or는 둘중 하나라도 일치한다면~의 뜻
});
if (existsUsers) {
// NOTE: 보안을 위해 인증 메세지는 자세히 설명하지 않습니다. 만약 이메일 혹은 비번을 특정해주면 해킹범에게 더 많은 정보를 주게 되는 꼴.
res.status(400).json({
errorMessage: '이메일 또는 닉네임이 이미 사용중입니다.', //뭉뚱그려서 둘중 하나가 사용중이다, 라고 전달.
});
return; //return을 써서 다음코드로 진행되지 않도록
}
const user = new User({ email, nickname, password }); //원래는 password는 암호화해서 저장한다. 해킹시 바로 알게되지않게
await user.save(); //DB에 저장한다.
res.status(201).json({}); //전달 메세지는 아무것도 안보낼 것이기 때문에 json 옆에 빈 칸
});
module.exports = router;
1. email, password를 전달 받음
2. email에 해당하는 사용자가 db에 존재하는지 확인
3. 사용자가 존재하지 않거나 email,password가 일치하는지
4. jwt 생성 후 쿠키 및 바디로 클라이언트에게 전달
5. 로그인 성공!
1. 로그인 routes 만들기 - auth.js -여기서 jwt 쓴다
const express = require('express');
const router = express.Router();
const User = require('../schemas/user.js');
const jwt = require('jsonwebtoken');
//로그인 API
router.post('/auth', async (req, res) => {
const { email, password } = req.body;
//이메일이 일치하는 유저 찾기
const user = await User.findOne({ email }); // 여기서 user는 어디서 확인해? 몽고디비 데이터에서 확인할꺼니까 맨 위에 연결해줘야겠지? const User = require('../schemas/user.js')
//1. 이메일에 일치하는 유저가 존재하지 않거나
//2. 유저를 찾았지만, 유저의 비밀번호와 입력 비밀번호가 다를때를 조건으로 걸어줌
if (!user || user.password !== password) {
//뒤의 password가 현재 입력받은 비번
res.status(400).json({
errorMessage: '로그인에 실패하였습니다.',
});
return;
}
//jwt를 생성, 생성하려면 맨 위에 또 jwt를 생성해줘야겠죠? const jwt = require("jsonwebtoken")
const token = jwt.sign({ userId: user.userId }, 'customized-secret-key');
res.cookie('Authorization', `Bearer ${token}`);
res.status(200).json({ token });
});
module.exports = router;
로그인 기능 구현 성공시 이렇게 나와야함
jwt기능
시작하자마자 해야할것, cookeparser 가져오기
입력해놓고 시작한다.
middleware폴더를 따로 만들고 -> auth-middleware.js 생성
const jwt = require('jsonwebtoken');
const User = require('../schemas/user.js');
//자 async자꾸 붙이는 이유 알제? 데이터를 가져와서 사용할 것이기 때문임.
module.exports = async (req, res, next) => {
const { authorization } = req.cookies; //cookies안에 있는 authorization가져올꺼야
//Bearer werwerw.erwerwerwer형태로 있었고 이것을 배열로 분리해줄꺼야
//존재하지 않으면 undefined.;이고 에러가 날 것임. 그래서 대비를 할꺼야
const [authType, authToken] = (authorization ?? '').split(); //??은 null병합 문자열 왼쪽에 있는 값이 비었거나 null일 경우 오른쪽에 있는 "" 즉 빈문자열로 대체해줄꺼야.
//authType === Bearer값인지 확인
//authToken 검증
if (authType !== 'Bearer' || !authToken) {
res.status(400).json({
errorMessage: '로그인 후에 이용할 수 있는 기능입니다.',
});
return;
}
//try를 왜쓰는가? jwt 검증, 에러나서 서버가 멈추지않게 하기 위해
//검증실패했을때 실패했다고 알려주기 위해
try {
//자 여기서 JWT를 쓸꺼면 맨 위에다 추가해줘야겠죠? 추가 한 후에
//authToken이 만료되었는지, authToken이 서버가 발급한 토큰이 맞는지 verify로 확인
const { userId } = jwt.verify(authToken, 'customized-secret-key'); // 이 key는 이전에 auth에서 썼던거와 같은것
//authToken에 있는 userId에 해당하는 사용자가 실제로 db에 있는지
//확인하려면 data 가져와야하니까 await이랑, User 데이타 위에 추가해줘야겠지? const User = require("../schemas/user.js")
const user = await User.findById(userId);
res.locals.user = user; //실제 db에서 정보가져오지말고, express가 제공하는 안전한 변수에 담아두고 언제나 꺼내서 사용할 수 있게 함. 사용 후 소멸
next(); //이 미들 웨어 다음으로 보낸다.
} catch (error) {
console.error(error); //사용자 인증에 실패한 내용을 알 수 있지만 사용자가 많아지면 관리하기가 힘들 수 있다.
res
.status(400)
.json({ errorMessage: '로그인 후에 이용할 수 있는 기능입니다.' });
return;
}
};
작동원리: 사용자가 토큰을 헤더에 담아서 보내야 동작하는 api 즉 로그인을 해야 "내정보"를 확인하는 것
사용자 인증 미들웨어에서 이미 구현했기때문에 그걸 바탕으로 만들면 댐.
const express = require('express');
const router = express.Router();
const User = require('../schemas/user');
const authMiddleware = require('../middlewares/auth-middleware');
//내정보 조회 API
//authMiddleware를 쓸꺼니까 끌어올거 위에 추가하고 시작, const authMiddleware = require("../middlewares/auth-middleware")
//작동원리는 get으로 들어온 data가 사용자미들인증웨어를 들렀다가 여기 api로 다시 돌아옴
router.get('/users/me', authMiddleware, async (req, res) => {
console.log(res.locals.user);
const { email, nickname } = res.locals.user;
res.status(200).json({
user: {
email: email,
nickname: nickname,
},
});
});
// 회원가입 API
router.post('/users', async (req, res) => {
const { email, nickname, password, confirmPassword } = req.body;
// 비번과 확인용 비번이 같은지 확인
if (password !== confirmPassword) {
res.status(400).json({
errorMessage: '패스워드가 패스워드 확인값과 다릅니다.',
});
return;
}
// email 또는 nickname이 동일한 데이터가 있는지 확인하기 위해 가져온다.
const existsUsers = await User.findOne({
$or: [{ email }, { nickname }], //이메일 또는 닉네임이 일치할 때 조회한다. $or는 둘중 하나라도 일치한다면~의 뜻
});
if (existsUsers) {
// NOTE: 보안을 위해 인증 메세지는 자세히 설명하지 않습니다. 만약 이메일 혹은 비번을 특정해주면 해킹범에게 더 많은 정보를 주게 되는 꼴.
res.status(400).json({
errorMessage: '이메일 또는 닉네임이 이미 사용중입니다.', //뭉뚱그려서 둘중 하나가 사용중이다, 라고 전달.
});
return; //return을 써서 다음코드로 진행되지 않도록
}
const user = new User({ email, nickname, password }); //원래는 password는 암호화해서 저장한다. 해킹시 바로 알게되지않게
await user.save(); //DB에 저장한다.
res.status(201).json({}); //전달 메세지는 아무것도 안보낼 것이기 때문에 json 옆에 빈 칸
});
module.exports = router;