Tech Blog of Pinomaker
전 게시글 확인!
 

[Node JS] typescript + sequelize + passport로 유저 API 개발 - Local User 생성

전 게시글을 확인하고 읽어주시기 바랍니다:) [Node JS] typescript + sequelize + passport로 유저 API 개발 - Sequelize 셋팅 해당 포스트는 Typescript가 셋팅 되어있어야 합니다. [Node JS] Node.js를 Typescr..

pinomaker.com

 

passport는 Node에서 로그인을 처리하기 위한 모듈이다. 

 

간단하게 생각해서 여권이라고 생각하면 되는 데, 서비스를 개발하면 로컬 유저만 있는 경우도 있지만, 구글과 카카오 등 oauth를 이용한 다양한 종류의 유저를 처리해야할 때가 있는 데 이 때 passport를 사용하면 편리하다.

 

로그인 할 때 나라별 여권이 있는 것처럼, 로컬인지 카카오인지 등의 유저 종류에 따라서 로그인 로직을 쉽게 나눌 수 있는 장점이 있다.

 

1. 필요한 모듈 설치

 

먼저 passport 개발을 위해 필요한 모듈을 아래와 같이 설치하자.

  • passport, @types/passport : passport 설정을 위한 모듈
  • passport-local, @types/passport-local : 로컬 로그인을 위한 passport 모듈
  • jsonwebtoken, @types/jsonwebtoken : jwt 관련 모듈
npm install passport passport-local jsonwebtoken

npm install --save-dev @types/passport @types/passport-local @types/jsonwebtoken

 

2.  passport 셋팅

passport 셋팅을 해주기 위하여 먼저 src/index.ts에서 아래와 같이 passport를 위한 코드를 추가해준다!

 

passport 설정을 담은 PassportConfig를 가져와 실행 시키고, passport 사용을 위한 passport.initalize를 이용하여 설정한다.

 
// src/index.ts

import express from 'express'
import passport from 'passport'
import sequelize from './models'

const app = express()
// passport 파일 가져오기
const PassportCofig = require('./utils/passport')

// passport 실행
PassportCofig()
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
// passport 설정
app.use(passport.initialize())

app.use('/api', require('./api'))

const port: number = 3000
app.listen(port, async () => {
  console.log(`SERVER ON! PORT : ${port}`)
  await sequelize
    .authenticate()
    .then(async () => {
      console.log('connection success')
    })
    .catch((e) => {
      console.log(e)
    })
})

 

3. passport 설정 파일 작성

src에 utils 폴더를 만들고 passport 폴더와 그 안에 index.ts를 생성한다.

해당 파일에는 passport 종류에 따른 분기처리를 하는 코드라고 생각하면 된다.

일단 passport-local, 로컬 로그인만 작성해준다.

// src/utils/passport/index.ts

import passportLocal from 'passport-local'
import passport from 'passport'
import { User } from '../../models/domain/User'
import * as bcrypt from 'bcrypt'

// passport-local 설정
const LocalStrategy = passportLocal.Strategy

module.exports = () => {
  // passport에서 "local"이면 해당 로직으로 들어옴.
  passport.use(
    new LocalStrategy(
      {
        usernameField: 'email',
        passwordField: 'password',
      },
      // Local Login 처리 로직
      async (email: any, password: any, done: any) => {
        try {
          // sequelize를 이용해 email를 검색해 유저를 조회한다.
          const findUser = await User.findOne({ where: { email: email } })
          // 유저가 없으면 없는 회원이라는 실패 로직 처리
          if (!findUser)
            done(null, false, { message: '가입되지 않은 회원 입니다.' })
          else {
            // 유저가 있으면 비밀번호를 암호화 해제를 해본다.
            const result = await bcrypt.compare(password, findUser.password)
            result
              ? done(null, findUser) // 비밀번호 일치하면 로그인 성공
              : done(null, false, { message: '비밀번호가 일치 하지 않음' }) // 일치 안 하면 실패
          }
        } catch (err) {
          console.error(err)
          // 에러 로직
          done(err)
        }
      },
    ),
  )
}

 

4. Local Login 작성

이제 user.service에 아래의 코드를 작성하자.

localLogin 함수는 passport-local를 호출하는 코드라고 생각하면 된다.

로그인이 성공하면, 유저의 idx를 이용하여 jwt를 생성하고 cookie에 담아서 보낸다.

// src/api/user/user.service.ts

import { Response, Request } from 'express'
import { User } from '../../models/domain/User'
import * as bcrypt from 'bcrypt'
import { Provider } from '../../models/interface/User.interface'
import passport from 'passport'
import jwt from 'jsonwebtoken'

exports.saveUser = async (req: Request, res: Response) => {
  try {
    const { email, password, name } = req.body
    const hash = await bcrypt.hash(password, 10)
    const saveUser = await User.create({
      email,
      password: hash,
      name,
      provider: Provider.LOCAL,
    })
    res.json({ result: true, user: saveUser })
  } catch (err) {
    console.log(err)
  }
}

// Local Login
exports.localLogin = async (req: Request, res: Response) => {
  // passport-local로 전송
  passport.authenticate('local', { session: false }, (err, user) => {
    // passport-local 결과에서 Err가 있거나, 유저가 없을 때
    if (err || !user) {
      return res.status(400).json({
        message: 'Something is not right',
        user: user,
      })
    }
    // 로그인 후 코드 작성
    req.login(user, { session: false }, (err) => {
      // err 있을 때 처리
      if (err) {
        console.log(err)
        res.send(err)
      }
      // jwt를 이용하여 token을 생성
      const token = jwt.sign({ idx: user.idx }, '123')
      
      // cookie에 accessToken에 token을 담아서 저장
      res.cookie('accessToken', token, {
        expires: new Date(Date.now() + 86400e3),
        sameSite: 'lax',
      })
      
      // user 정보 리턴
      return res.send({ user })
    })
  })(req, res)
}

 

 

그리고 /src/api/user/index.ts에서 라우팅 처리까지 해주면 끝이다.

// src/api/user/index.ts

const router = require('express').Router()
const user = require('./user.service')

router.post('/', user.saveUser)
//Local Login 추가
router.post('/local', user.localLogin)

module.exports = router
export {}

 

Postman으로 API를 실행하면 쿠키도 잘 담겨오고 실행도 잘 되는 것을 볼 수 있다.

profile

Tech Blog of Pinomaker

@pinomaker

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!