본문 바로가기

개발

Node.js 유저인증, 권한부여를 위한 Passport 사용법

이번에는 Node.js에서의 인증과 권한 부여에 대해 이야기해보려고 합니다. 인증이란, 사용자의 신원을 확인하는 과정입니다. 예를 들어, 이메일과 비밀번호를 입력하여 로그인하는 것이 인증의 한 예입니다. 권한 부여란, 인증된 사용자에게 특정 자원이나 기능에 접근할 수 있는 권한을 부여하는 과정입니다. 예를 들어, 관리자만 게시글을 삭제할 수 있도록 하는 것이 권한 부여의 한 예입니다.

 

Node.js에서 인증과 권한 부여를 구현하는 방법은 여러 가지가 있습니다. 그 중에서도 가장 널리 사용되는 방법 중 하나는 Passport.js라는 미들웨어를 사용하는 것입니다. Passport.js는 Node.js에서 인증을 쉽게 구현할 수 있도록 도와주는 라이브러리입니다. Passport.js의 특징은 다음과 같습니다.

- 다양한 인증 전략을 지원합니다.
- 로컬 인증, 소셜 로그인, OAuth 등의 인증 방식을 쉽게 적용할 수 있습니다.

- Express와 호환됩니다. Express 애플리케이션에서 Passport.js를 미들웨어로 사용할 수 있습니다.

- 세션 관리를 지원합니다. Passport.js는 세션을 생성하고 저장하고 복원하는 기능을 제공합니다.

- 커스터마이징이 가능합니다. Passport.js는 인증 과정에서 필요한 함수들을 직접 정의하고 변경할 수 있습니다.

 

그럼 Passport.js를 이용하여 간단한 로컬 인증과 권한 부여를 구현해보겠습니다. 예시로, 사용자 등록과 로그인, 로그아웃, 마이페이지 접근 등의 기능을 만들어보겠습니다. 다음은 애플리케이션의 구조와 기능입니다.

애플리케이션 구조

- app.js: 애플리케이션의 메인 파일

- models/user.js: 사용자 모델 정의 파일

- routes/index.js: 메인 라우터 파일

- routes/user.js: 사용자 관련 라우터 파일

- views/index.ejs: 메인 페이지 템플릿 파일

- views/login.ejs: 로그인 페이지 템플릿 파일

- views/register.ejs: 사용자 등록 페이지 템플릿 파일

- views/mypage.ejs: 마이페이지 템플릿 파일

 

애플리케이션 기능

- /: 메인 페이지로, 로그인 여부에 따라 다른 내용을 보여줌

- /login: 로그인 페이지로, 이메일과 비밀번호를 입력하여 로그인함

- /logout: 로그아웃 기능으로, 세션을 파기하고 메인 페이지로 리다이렉트함 /register: 사용자 등록 페이지로, 이메일과 비밀번호를 입력하여 사용자를 생성함 /mypage: 마이페이지로, 로그인한 사용자만 접근할 수 있으며, 자신의 이메일을 보여줍니다.

 

이제 해당 애플리케이션의 몇가지 주요코드를 보여드리겠습니다.

다음은 app.js의 주요 코드입니다.

// app.js

// 모듈 임포트
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const User = require('./models/user');
const indexRouter = require('./routes/index');
const userRouter = require('./routes/user');

// 앱 생성
const app = express();

// 미들웨어 설정
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(session({
  secret: 'secret',
  resave: false,
  saveUninitialized: false,
}));
app.use(passport.initialize());
app.use(passport.session());

// 뷰 엔진 설정
app.set('view engine', 'ejs');

// 라우터 설정
app.use('/', indexRouter);
app.use('/user', userRouter);

// 에러 핸들러 설정
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({ message: err.message });
});

// 데이터베이스 연결
mongoose.connect('mongodb://localhost:27017/passport', { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('Connected to MongoDB'))
  .catch((err) => console.error(err));

// Passport.js 설정
passport.use(new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
}, async (email, password, done) => {
  try {
    // 데이터베이스에서 이메일로 사용자를 찾음
    const user = await User.findOne({ email });
    if (!user) {
      // 사용자가 없으면 에러 반환
      return done(null, false, { message: 'Incorrect email' });
    }
    // 비밀번호를 비교함
    const match = await bcrypt.compare(password, user.password);
    if (!match) {
      // 비밀번호가 틀리면 에러 반환
      return done(null, false, { message: 'Incorrect password' });
    }
    // 사용자를 반환함
    return done(null, user);
  } catch (err) {
    // 에러가 발생하면 에러 반환
    return done(err);
  }
}));

passport.serializeUser((user, done) => {
  // 세션에 사용자 아이디를 저장함
  done(null, user._id);
});

passport.deserializeUser(async (id, done) => {
  try {
    // 세션에서 사용자 아이디를 가져와서 데이터베이스에서 사용자를 찾음
    const user = await User.findById(id);
    // 사용자를 반환함
    done(null, user);
  } catch (err) {
    // 에러가 발생하면 에러 반환
    done(err);
  }
});

// 서버 시작
app.listen(3000, () => console.log('Server listening on port 3000'));

다음으로는 사용자 모델정의를 위한 models/user.js 파일입니다. 아래는 해당 파일의 주요 코드입니다.

// models/user.js

// 모듈 임포트
const mongoose = require('mongoose');

// 스키마 정의
const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
});

// 모델 생성 및 내보내기
module.exports = mongoose.model('User', userSchema);

다음으로 사용자 관련 라우터 정의를 위해 routes/user.js 파일입니다.

// routes/user.js

// 모듈 임포트
const express = require('express');
const passport = require('passport');
const User = require('../models/user');
const bcrypt = require('bcrypt');

// 라우터 생성
const router = express.Router();

// 로그인 페이지 라우트
router.get('/login', (req, res, next) => {
  try {
    // 로그인 페이지 템플릿을 렌더링함
    res.render('login');
  } catch (err) {
    next(err);
  }
});

// 로그인 기능 라우트
router.post('/login', passport.authenticate('local', {
  // 인증 성공 시 메인 페이지로 리다이렉트함
  successRedirect: '/',
  // 인증 실패 시 로그인 페이지로 리다이렉트함
  failureRedirect: '/user/login',
}));

// 로그아웃 기능 라우트
router.get('/logout', (req, res, next) => {
  try {
    // 세션을 파기하고 메인 페이지로 리다이렉트함
    req.logout();
    req.session.destroy();
    res.redirect('/');
  } catch (err) {
    next(err);
  }
});

// 사용자 등록 페이지 라우트
router.get('/register', (req, res, next) => {
  try {
    // 사용자 등록 페이지 템플릿을 렌더링함
    res.render('register');
  } catch (err) {
    next(err);
  }
});

// 사용자 등록 기능 라우트
router.post('/register', async (req, res, next) => {
  try {
    // 요청 바디에서 이메일과 비밀번호를 가져옴
    const { email, password } = req.body;
    // 이메일이 이미 존재하는지 확인
    const existingUser = await User.findOne({ email });
    if (existingUser) {
      // 이미 존재하면 에러 발생시킴
      const error = new Error('Email already exists');
      error.status = 409;
      throw error;
    }
    // 비밀번호를 해싱함
    const hashedPassword = await bcrypt.hash(password, 12);
    // 새로운 사용자를 생성함
    const user = new User({
      email,
      password: hashedPassword,
    });
    // 데이터베이스에 저장함
    await user.save();
    // 메인 페이지로 리다이렉트함
    res.redirect('/');
  } catch (err) {
    next(err);
  }
});

// 마이페이지 라우트
router.get('/mypage', (req, res, next) => {
  try {
    // 로그인한 사용자만 접근할 수 있도록 함
    if (!req.user) {
      // 로그인하지 않은 경우 에러 발생시킴
      const error = new Error('Please login first');
      error.status = 401;
      throw error;
    }
    // 마이페이지 템플릿을 렌더링하고 사용자 이메일을 보여줌
    res.render('mypage', { email: req.user.email });
  } catch (err) {
    next(err);
  }
});

module.exports = router;

 

애플리케이션을 실행한 후, 브라우저에서 http://localhost:3000/ 에 접속해보세요. 로그인하지 않은 상태에서는 메인 페이지에 로그인 링크와 사용자 등록 버튼이 보입니다. 사용자 등록 버튼을 클릭하면 사용자 등록 페이지로 이동합니다. 이메일과 비밀번호를 입력하여 사용자를 생성할 수 있습니다. 사용자를 생성하면 메인 페이지로 리다이렉트됩니다. 이때, 메인 페이지에는 마이페이지 링크와 로그아웃 버튼이 보입니다. 마이페이지 링크를 클릭하면 마이페이지로 이동합니다. 마이페이지에는 자신의 이메일이 보입니다. 로그아웃 버튼을 클릭하면 세션이 파기되고 메인 페이지로 리다이렉트됩니다. 이때, 다시 로그인 링크와 사용자 등록 버튼이 보입니다. 로그인 링크를 클릭하면 로그인 페이지로 이동합니다. 이메일과 비밀번호를 입력하여 로그인할 수 있습니다. 로그인에 성공하면 메인 페이지로 리다이렉트됩니다.

 

이상으로 Node.js에서의 인증과 권한 부여에 대한 간단한 소개와 예제를 마치겠습니다. Passport.js는 Node.js에서 인증을 쉽게 구현할 수 있도록 도와주는 라이브러리입니다. Passport.js를 이용하여 여러분만의 인증과 권한 부여 기능을 만들어보세요. 감사합니다.