잊지 않겠습니다.

'Express'에 해당되는 글 2건

  1. 2016.02.17 Express에서의 Session 사용법
  2. 2014.11.26 nodejs + express를 이용한 Google OAuth 연동

Express에서의 Session 사용법

기초중의 기초이지만, 간혹 사용법이 헛갈릴때가 많아서 정리하였습니다.

요구사항

  1. ES6 문법을 기초로 이용
  2. Session을 이용한 로그인 관리
  3. Cluster를 이용가능 (pm2 등을 이용)
  4. login을 통해 얻어진 사용자 이름을 session에 저장.
  5. view는 모두 json을 이용

개발 환경 구성

yo generator중에 generator-express 설치

npm install -g generator-express

특정 directory에서 yo express 실행하면 기본적인 express application이 구성됩니다. 구성된 폴더안에서 gulp default를 실행하면 기본 port 3000번으로 application이 구동됩니다.

Application이 구동되지 않을때.

port 35729가 사용중일때 : gulp.liveload를 이용한 다른 application이 구동되고 있을 때, 발생되는 error. 두개의 application을 동시에 실행하고 싶은 경우에는 gulpfile.js에서 liveload method에 port 번호를 명시. livereload.listen(60000);

config.root undefined가 될때. config/config.js가 정상적으로 로드되지 않은 경우. NODE_ENVdevelopment, production 등으로 지정하지 않은 경우에 발생. 자신의 NODE_ENV를 확인후, NODE_ENV에 맞게 config.js를 수정

추가 npm - express-session, filestore

Session을 이용하기 위해서 express-session 설치합니다. 구 버젼을 기반으로한 책이나 blog에서는 이 과정이 나오지 않습니다. express의 middleware들은 대부분 npm module로 따로 분리가 되어 사용됩니다. express에 기본으로 들어가 있는 경우는 얼마 없습니다.

npm install express-session --save
babel-express-app@0.0.1 /home/ykyoon/dev/code/babel-express-app
└─┬ express-session@1.13.0 
  ├── crc@3.4.0 
  └─┬ uid-safe@2.0.0 
    └── base64-url@1.2.1 

npm WARN optional Skipping failed optional dependency /chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.7

express.js파일에 express-session을 middleware로 등록합니다.

const session = require('express-session');
app.use(session({
    secret: 'keyboard cat',
    resave: false,
    saveUninitialized: false,
    cookie: { secure: false }
}));

지정되는 parameter는 다음 의미를 갖습니다.

  • secret: session의 암호화에 사용되는 key값입니다.
  • resave: requrest가 요청되었을때, 기존의 session이 존재하는 경우 다시 저장할 필요가 있는지를 확인하는 option입니다. express-session에서 제공되는 touch를 구현하고 있는지를 확인해보면 됩니다. 기본적인 MemoryStore의 경우, touch가 구현되어 있기 때문에 설정할 필요가 없습니다.
  • saveUninitialized: 설정하지 않는 경우, Session이 저장되지 않습니다. 일반적인 로그인을 구성할 때, false로 지정해줘야지 됩니다. 기본값은 true이며 모든 초기화되지 않은 session은 저장되게 됩니다.
  • cookie.secure: https로 호출되는 경우에만 session cookie를 생성하는 option입니다. 실 production에 유용한 option으로 테스트나 개인 공부를 할 때는 false로 지정해주시면 됩니다.
  • cookie.maxAge: cookie가 만료되는 시간을 설정합니다.

login url 구현

DB를 사용하지 않는 최소한의 application이기 때문에 사용자가 입력한 username을 그대로 session의 username으로 넣어주는 code를 구성합니다.

'use strict';

const express = require('express'),
  router = express.Router();

router.get('/login', (req, res) => {
  const session = req.session;
  session.username = req.query.username;
  return res.json(session);
});

module.exports = (app) => {
  app.use('/user', router);
};

원래는 login의 경우, post로 구현해야지 되는 것이 원칙이지만, 테스트를 위한 application이고, browser에서 그대로 구현하기 위해서이니 그냥 get으로 구현하였습니다. 이제 저장된 username을 얻어내야지 됩니다. 이제 browser에서는 저장된 username이 계속 유지가 되어야지 됩니다.

router.get('/info', (req, res) => {
  return res.json(req.session);
});

이제 url을 두개를 연달아 browser에서 실행하면 다음과 같은 결과를 볼 수 있습니다.

{
    "cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":true,"path":"/"},
    "username":"abcdef"
}

Session store의 변경

고성능, 고가용성을 구현하기 위해서는 기본적으로 nodejs는 cpu의 core 숫자와 동일한 process숫자로 실행되어야지 됩니다. 이 사실은 지금 application의 경우 심각한 문제를 가지고 있는 것을 의미합니다. process간의 memory 공유는 원칙적으로 되지 않는거니까요. (hacking이나 memory editor의 경우 다르겠지만, web application의 기본 기능과는 거리가 있는 상황이니.)

그래서, nodejs application에서는 공용으로 사용되어야지 되는 정보는 file이나 redis, db와 같은 공용 IO에 저장해서 모든 process가 공유하는 형식으로 사용하게 됩니다. 또한, nodejs application은 application을 종료했다가 다시 올리는 경우가 왕왕 있습니다. express-session의 기본값인 MemoryStore는 기본적으로 production에 올릴수 없는 설정입니다.

성능은 안좋고, multiple instance에서는 절대로 사용할 수 없는 방법이지만, File에 session정보를 저장하기 위해서 FileStore를 이용해보도록 하겠습니다. FileStore와 Session간의 interface를 하는 session-file-store를 설치하면 FileStore가 같이 설치됩니다.

npm install session-file-store --save
babel-express-app@0.0.1 /home/ykyoon/dev/code/babel-express-app
├── filestore@0.1.3 
└─┬ session-file-store@0.0.24 
  ├── bagpipe@0.3.5 
  ├─┬ fs-extra@0.26.5 
  │ ├── jsonfile@2.2.3 
  │ ├── klaw@1.1.3 
  │ └─┬ rimraf@2.5.2 
  │   └── glob@7.0.0 
  └── retry@0.8.0

FileStore를 Session에 설정합니다. 코드는 다음과 같습니다.

const session = require('express-session');
const FileStore = require('session-file-store')(session);
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: false,
  cookie: { secure: false },
  store: new FileStore()
}));

설치후, folder를 보면 session라는 folder가 생성되어 있는 것을 볼 수 있습니다. 이제 위에서 실행했던 URL을 다시 실행해보면 File이 하나 생성되어 있는 것을 볼 수 있습니다. 파일을 열어보면, session의 정보가 그대로 저장되어 있는 것을 볼 수 있습니다.

정리

express에서 session을 구성하는 방법에 대해서 간단히 알아봤습니다. 또한 MemoryStore가 아닌 다른 Store를 이용하는 방법에 대해서도 간단히 알아 보았습니다. 실무에서는 대부분 redis를 이용하는 경우가 일반적이긴 하지만, 개발단에서 편하게 알아보기 위해서, File을 사용하는 경우도 꼭 나쁜 것은 아닙니다.


Posted by Y2K
,

nodejs + express를 이용한 Google OAuth 연동

OAuth는 근간에 거의 모든 WebSite에서 사용되고 있는 인증방법입니다. 회원 가입절차를 빠르게 할 수 있으며, 개인정보에 대한 관리 이슈를 피할 수 있어, 많이들 사용되는 방법입니다. nodejs를 이용한 OAuth 인증 방법에 대해서 알아보도록 하겠습니다.

npm package 설치

npm install passport
npm install passport-google-oauth
npm install cookie-session
  • passport: OAuth를 지원하기 위한 base package 입니다. twitter, facebook, google에서 지원하고 있는 OAuth를 모두 지원합니다.
  • passport-google-oauth: passport를 기반으로 하는 google-oauth 지원 package입니다.
  • cookie-session: express에서 session을 지원하기 위한 package입니다. express-session으로 대체해서 사용 가능합니다.

google auth api 설정

https://console.developers.google.com 에서 Google에서 제공하는 API를 이용하는 project를 생성할 수 있습니다. 새로운 project를 생성한 후, API 및 인증 > 사용자인증정보에서 새클라이언트 ID 만들기를 통해서 새로운 API를 만들어줍니다. 여기서 중요한 설정은 다음과 같습니다.

  • 승인된 javascript 원본 : 인증을 요청할 url을 넣습니다. 개발자 환경인 http://localhost:3000 과 같은 표현 역시 가능합니다.
  • 승인된 redirection URL : 인증이 완료된 후, redirect될 url을 넣어줍니다.

만들어진 웹어플리케이션용 Client ID에서 이제 3개의 정보는 우리가 작성할 application에서 사용해야지 됩니다.

  • 클라이언트 ID
  • 클라이언트 보안 비밀
  • URI 리디렉션

Login uri handling

사용자가 OAuth를 통해서 인증될 URL을 설정합니다. express controller code에 해당되는 내용입니다.

module.exports = OAuthController;

function OAuthController(app) {
  var passport = require('passport');
  app.use(passport.initialize());
  app.use(passport.session());
  // Google OAuth를 사용하는데 필요한 객체를 선언합니다.
  var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;

  passport.serializeUser(function(user, done) {
    done(null, user);
  });
  passport.deserializeUser(function(obj, done) {
    done(null, obj);
  });

  passport.use(new GoogleStrategy({
    clientID: '클라이언트 ID',
    clientSecret: '클라이언트 보안 비밀',
    callbackURL: 'URI 리다렉션'
  },
  function(accessToken, refreshToken, profile, done) {
    //
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // req.session.passport 정보를 저장
    // done 메소드에 전달된 정보가 세션에 저장된다.
    // profile을 이용해서 사용자 정보를 DB에 넣는 등의 작업에 활용할 수 있다.
    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //
    return done(null, profile);
  }));

  // 인증 URI : uri link를 이곳에 넣어주면 됩니다.
  app.get('/auth/google', passport.authenticate('google', {
    scope: [
      'https://www.googleapis.com/auth/userinfo.profile',
      'https://www.googleapis.com/auth/userinfo.email'
    ]
  }));

  app.get('/oauth2callback', passport.authenticate('google', {
    successRedirect: '/auth/successed',
    failureRedirect: '/auth/failure'
  }));

  app.get('/logout', function(req, res) {
    //
    // passport 에서 지원하는 logout 메소드이다.
    // req.session.passport 의 정보를 삭제한다.
    //
    req.logout();
    req.session = null;
    res.redirect('/');
  });
};

Summary

Passport를 통해서 Google OAuth에 접근하는 방법은 많은 예시 code들이 있고, 거의 유사한 형태를 갖습니다. 위 controller code를 그대로 copy & paste로만도 간단히 Google OAuth를 구현 가능합니다.


Posted by Y2K
,