본문 바로가기
개발/차근차근 개발일지 TIL

쇼핑몰//회원가입-로그인 api 구현 참고용 총정리

by 코딩하는짱구 2023. 4. 21.
반응형

쇼핑몰//회원가입-로그인 api 구현 참고용 총정리

https://teamsparta.notion.site/Node-js-Lv-2-b312975a295d43599cb9d76fb472c040-

 

Node.js Lv.2

Goal:

teamsparta.notion.site

 

큰 틀 (장바구니가 완성되어있을 경우)

 회원가입기능->로그인기능->사용자인증 미들웨어->내 정보조회->상품수정->장바구니수정

 

결국 쿠키가 뭘 의미하는가? 로그인할때 인증을 도와줄 키를 가지고있다. 

 

 


  • 1단계 회원가입 기능 구현

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;

 


  • 2단계 로그인 기능 구현

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기능

 


  • 3단계 인증미들웨어

시작하자마자 해야할것, 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;
  }
};

  • 4단계 내 정보 조회 api 구현하기

작동원리: 사용자가 토큰을 헤더에 담아서 보내야 동작하는 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;
반응형