node.JS 세션 갱신

조회수 3992회

express, passport, mongoose, express-session 사용중입니다.

User.findOne({'_id': req.user._id}, function(err, user){    // user document 검색
    if(err){console.log('findOne 에러')}else{
        user.profImgHref = /*생략*/;            // DB 수정
        req.session.passport.user.profImgHref = user.profImgHref;    //    session 갱신
        user.save();        //    DB 수정
        req.logIn(user, function(err){
            if(err)return err;
        });
    }
});

User는 mongoose 모델을 require 해와서 쓰고 있고 지금 하려고 하는 부분은 user의 profImgHref를 갱신하려고 하는 중입니다. DB만 수정하는 것이 아니라 세션값도 같이 수정해야하는데 passport가 연동되어있는 상황이고 여기서는(https://github.com/jaredhanson/passport/issues/208) req.login를 불러서 다시한번 deserialize해야 한다고 하더군요 그래서 마지막에 req.login을 넣어봤는데 여전히 변화가 없습니다. 빼먹은 부분이 있는걸까요? session값은 app.js에서 res.locals.userSession = req.user;로 걸어놓고 불러오고 있습니다.

질문 내용 추가설명

일단 제 코드의 로직은 이렇습니다.

  1. View에서 ajax post로 이미지 파일을 받습니다.
  2. Validation을 거쳐서 s3로 업로드를 합니다.
  3. public Url을 Session과 DB에 저장합니다. (Session-store로 mongoDB를 사용하고 있으므로 user collection과 session collection이 별도로 존재합니다.)
  4. 세션정보는 모든 response에 userSession이라는 객체로 보내집니다.
  5. 페이지 refresh를 하면 View에서 handlebars 템플릿형식을 통해 userSession에 저장된 public Url이 태그에 삽입되어 출력됩니다.

그런데 Robomongo로 확인한 바로는 세션값이 전혀 바뀌지 않고 있습니다. passport를 거치면서 세션이 req.user형태로 전달되는 것은 맞는듯 싶으나 (req.user를 라우터단에서 직접 넣어도 효과가 없었습니다.) 다만 DB값에만 변동이 있어서 직접 재 로그인을 하면 passport가 세션을 재구성하는 단계에서 Session에 변동을 보이는 정도입니다.

라우터쪽의 코드를 상세히 다시 첨부합니다.

router.post('/profImg', function(req, res, next){
    var form = new multiparty.Form({ // multiparty 모듈 사용중
        maxFilesSize: 512 * 1024
    });
    form.on('error', function(message) {
                res.status = 413;
                res.send(413, '문제가 발생했습니다. 다시 시도해 보시거나 관리자에게 문의해주세요.');
                res.end();
            });
    form.on('part', function(part) {
        if(part.fileName){  // 파일 타입, 파일 크기 재점검
            var type = part.headers['content-type'];
            var size = part.byteCount - part.byteOffset;
            if(type != 'image/jpeg' && type != 'image/png' && type != 'application/gif' || size == 0) {
                this.emit('error');
            }
        }
    });
    form.on('close', function(){
        res.json();
    });
    form.parse(req, function(err, fields, files){
        function instFile (filesArg){
            //  files.profImg[0]을 인자로 받음
            var data = fs.readFileSync(filesArg.path),
                fName = filesArg.path.split('/').pop(),
                userID = req.user._id.valueOf(),
                forKey = 'profile/'+userID+'/'+fName,
                params = {
                    Bucket: 'bucketName',
                    ACL: 'public-read',
                    Key: forKey,
                    Body: data
                }
            if(filesArg.size==0){   //  size가 0이면 임시파일 삭제
                fs.unlinkSync(filesArg.path);
            }else{
                if(req.user.profImgHref){
                    var params2 = {
                        Bucket: 'bucketName',
                        Key: 'profile/'+userID
                    }
                    s3.deleteObject(params2, function(err, data){
                        if(data){
                            console.log('파일 삭제 성공'); //  삭제 안되는 상황. 원인 불명
                        }else{
                            console.log(err);
                        }
                    });
                }
                s3.putObject(params, function (err, data) {
                    if(err){
                        console.log(err, err.stack);
                        console.log('putObject 에러');
                    }else{
                        User.findOne({'_id': req.user._id}, function(err, user){
                            if(err){console.log('findOne 에러')}else{
                                //  이미지 Url 수정
                                user.profImgHref = 'https://ㅠbucketName.s3.amazonaws.com/'+forKey;
                                req.user.profImgHref = user.profImgHref;
                                user.save();
                                    console.log(user);
                                    req.logIn(user, function(err){
                                        if(err)return err;
                                    });
                                console.log(req.session.passport.user.profImgHref);
                            }
                        });
                    }
                });
            }
        }
        instFile(files.profImg[0]);
    });
});

3 답변

  • session 설정에 resave가 false로 설정돼있네요.

    store에 반영하시려면 req.session.save를 직접 부르시던지 resave를 true로 돌려놓으시면 될거같습니다.

    https://github.com/expressjs/session#sessionsave

    SPA가 아니라면 req.login 는 안하셔도 될거같아요.

    • (•́ ✖ •̀)
      알 수 없는 사용자
  • 추가 답변내용

    올려주신 코드와 제 코드가 다른점을 보면 올려주신 코드에서는 req.session.passport.user를 쓰고 계신것 같은데요. 저는 req.user를 view에 전달해서 사용했습니다.

    https://github.com/tutsplus/passport-mongo 에 있는 코드를 이용해서 테스트 해 봤어요.

    아래는 1차 답변

    https://github.com/jaredhanson/passport/issues/208 의 아래 부분을 보다 보면 passport에서 id를 가지고 session을 업데이트 하기 때문에 별도로 session을 업데이트할 필요는 없다는 이야기가 있네요(samhuntsocial라는 분의 댓글) 안녕하세요 로그인이 유지된 상태에서 테스트를 해 봤는데요.

    router.get('/home', isAuthenticated, function(req, res){
    
        User.findOne({'_id': req.user._id}, function(err, user){
        if(err){console.log('findOne 에러')}else{
            user.firstName = user.firstName+"#";
            user.save();
        }
        });
    
        res.render('home', { user: req.user });
    });
    

    와 같이 해 봤더니 페이지를 새로고침 할 때 마다 firstName에 #이 계속 추가되더군요. 하지만 혹시 페이지 리로드 없이 single page app으로 하시려는거라면 다른 방법을 찾아 봐야겠지만 그렇지 않다면 페이지만 리로드 되면 될 것 같습니다.

    • 다행히 SPA로 만들고 있지는 않지만 리로드를 몇번 해봐도 바뀌지 않더라고요 분명히 DB값도 바뀌어있고 req.session.passport.user.profImgHref 값을 확인해봐도 바뀌어있는 것이 맞는데 직접 재 로그인 하지 않는 이상 바뀌지가 않습니다. Snark 2016.3.15 16:06
    • 바뀌지가 않는 부분이라고 하면 실제 HTML에서 나오는 부분인데 app.js에서 res.locals.userSession=req.user로 모든 res에 전역적으로 걸어서 보내고 handlebars 템플릿 엔진을 이용해서 src="{{userSession.profImgHref}}" 이런식으로 받습니다. 문제가 있다면 이쪽이 아닐까 하는데 해결할 방법이 딱히 떠오르지 않습니다 Snark 2016.3.15 16:14
    • jade파일과 render를 호출하는 부분을 더 보여주실 수 있나요? 정토드 2016.3.15 16:27
  • jade는 아니고 handlebars 쓰고 있습니다

    이게 html이고

    <div id="preview"><img src="{{#if userSession.profImgHref}}{{userSession.profImgHref}}{{/if}}" alt=""></div>
    

    app.js에서 session관련한 코드 발췌하였습니다.

    var express = require('express'),
        path = require('path'),
        logger = require('morgan'),
        cookieParser = require('cookie-parser'),
        bodyParser = require('body-parser'),
        exphbs = require('express-handlebars'),
        routes = require('./routes/index'),
        newclass = require('./routes/newClassSubmit'),
        mypage = require('./routes/mypage'),
        mongoose = require('mongoose'),
        csurf = require('csurf'),
        filter = require('content-filter');
    const session = require('express-session');
    const MongoStore = require('connect-mongo')(session);
    var app = express();
    app.use(session({
        cooke: {
            maxAge: 30 * 24 * 60 * 60 * 1000,
            expires: 7200000,
            httpOnly: true
        },
        resave: false,
        saveUninitialized: false,
        secret: '~~~',
        store: new MongoStore({
            mongooseConnection: mongoose.connection
        })
    }));
    //  passport
    var passport = require('./config/passport');
    app.use(passport.initialize());
    app.use(passport.session());
    //  session 전달
    app.use(function(req, res, next){
        res.locals.userSession = req.user;
        next();
    });
    //  router Setup
    app.use('/', routes);
    app.use('/newClassSubmit', newclass);
    app.use('/mypage', mypage);
    

    config/passport.js

    var passport = require('passport'),
        LocalStrategy = require('passport-local').Strategy,
        User = require('../models/User.js'),
        bcrypt = require('bcrypt');
    
    passport.serializeUser(function(user, done){
        done(null, user);
    });
    passport.deserializeUser(function(user, done){
            done(null, user);
    });
    passport.use('local-login', //LocalStrategy Setup
                 new LocalStrategy({
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
        function(req, email, password, done){
        User.findOne({'email': email}, function(err, user){     //  입력받은 email로 user 검색
            if(err) return done(err);   //  err 메세지 출력
            if(!user){
                return done(null, false, {msg: 'NoID'});        //  해당 user가 없을 경우 NoID 리턴
            }
            if(!user.authenticate(password)){                   //  비밀번호 대조하는 mongoose method
                return done(null, false, {msg: 'WrPW'});        //  비밀번호가 다를경우 WrPW 리턴
            }
            return done(null, user);//문제 없으면 user DB 리턴
        });
    })
    );
    
    module.exports = passport;
    

    models/User.js

    var mongoose = require('mongoose'),
        bcrypt = require('bcrypt');
    
    //  회원가입 스키마 설정
    var userSchema = new mongoose.Schema({
        //생략
        profImgHref: String             //  프로필 이미지 주소
    });
    
    userSchema.pre('save', function(next){      //  유저정보 수정 전 password 암호화
        var user = this;
        if(!user.isModified('password')){       //  비밀번호 변동사항이 없으면 next()
            return next();
        }else{
            user.password = bcrypt.hashSync(user.password, bcrypt.genSaltSync());   //  변동사항이 있으면 암호화
            return next();
        }
    });
    
    //비밀번호 검증 method
    userSchema.methods.authenticate = function (password) {
        var user = this;
        return bcrypt.compareSync(password,user.password);
    }
    
    var User = mongoose.model('user',userSchema);
    
    module.exports = User;
    
    • 답변으로 코드를 달아 주시면 좋아요 숫자에 따라서 위치가 변해서 순서 유지가 안됩니다. 질문을 편집해서 내용을 추가해 주시면 더 좋겠네요. 제 답변도 수정해 두었습니다. 정토드 2016.3.15 18:34

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)