이 게시물에는 코드작성이 포함되어 있습니다. 소스코드를 받으신 후 진행해 주세요. MEAN Stack/개발 환경 구축에서 설명된 프로그램들(git, npm, atom editor)이 있어야 아래의 명령어들을 실행할 수 있습니다.
- Github에서 소스코드 보기: https://github.com/a-mean-blogger/google-oauth/tree/514441a77e60d669e462ea429ec52c40f8d78704
Google OAuth client ID와 secret를 사용하여 구글 소셜 로그인 기능을 구현해 봅시다. Google OAuth client ID와 secret가 없다면 이전 글을 읽고 준비를 하시기 바랍니다.
이 강의는 MEAN Stack 강의 시리즈의 게시판 만들기/게시판 Login 기능 추가까지를 읽은 분들을 대상으로 합니다. 즉, 기본적인 node.js Express 사이트 제작 지식과 passport 패키지에 대한 기본적인 지식을 알고 있는 사람들을 대상으로 합니다. 만약 해당 내용을 모르신다면 MEAN Stack 강의 시리즈를 먼저 읽고 진행해 주시기 바랍니다.
이번에 만들 사이트는 main 페이지 그리고 login 페이지의 2 페이지 밖에 없습니다. 처음 사이트에 접속하면 아래와 같이 main 페이지가 표시됩니다.
로그인 링크를 누르면 auth 페이지로 이동하고, 구글 login 링크가 있습니다.
google login 링크를 누르면 구글을 통해 로그인한 후 다시 main 페이지로 돌아옵니다.
처음과 같은 main 페이지지만, 로그인이 되었기 때문에 구글에서 읽어온 정보로 'Welcome {name}'을 표시하고, logout 링크를 표시합니다.
코딩에 앞서 Google OAuth client ID를 GOOGLE_CLIENT_ID, secret을 GOOGLE_SECRET로 환경 변수에 저장합니다.
환경 변수 없이 테스트하고 싶으신 분들은 아래 코드에서 process.env.GOOGLE_CLIENT_ID와 process.env.GOOGLE_SECRET를 각각 client ID와 secret으로 변경하시면 되겠습니다.
프로젝트 폴더를 생성합니다. 해당 폴더에서 command line(cmd, git bash 등)에 아래 명령어 입력하여 node.js 프로젝트를 생성합니다.
$ npm init --yes
프로젝트에 필요한 package들도 설치해줍니다.
$ npm install express express-session ejs passport passport-google-oauth2 --save
passport-google-oauth2가 이번 강의의 핵심 패키지입니다. 나머지 패키지들은 이미 MEAN Stack 강의 시리즈에서 설명을 하였습니다.
// index.js var express = require('express'); var app = express(); var passport = require('passport'); var session = require('express-session'); app.set('view engine', 'ejs'); app.use(session({secret:'MySecret', resave: false, saveUninitialized:true})); // Passport setting app.use(passport.initialize()); app.use(passport.session()); // Routes app.use('/', require('./routes/main')); app.use('/auth', require('./routes/auth')); // Port setting var port = 3000; app.listen(port, function(){ console.log('server on! http://localhost:'+port); });
특별히 설명할 것은 따로 없습니다. 다만, express session 설정이 반드시 passport session 위에 있어야 합니다. 아래에 있으면 로그인이 되지 않습니다.
// config/passport.js var passport = require('passport'); var GoogleStrategy = require('passport-google-oauth2').Strategy; passport.serializeUser(function(user, done) { done(null, user); }); passport.deserializeUser(function(user, done) { done(null, user); }); passport.use(new GoogleStrategy( { clientID : process.env.GOOGLE_CLIENT_ID, clientSecret : process.env.GOOGLE_SECRET, callbackURL : '/auth/google/callback', passReqToCallback : true }, function(request, accessToken, refreshToken, profile, done){ console.log('profile: ', profile); var user = profile; done(null, user); } )); module.exports = passport;
Passport를 설정하는 부분입니다. 자세한 설정법은 passport-google-oauth2 패키지 github 페이지(https://github.com/mstade/passport-google-oauth2)에서 확인 할 수 있습니다.
callbackURL
부분은 구글에 로그인이 이루어 진 후 구글이 다시 사이트로 돌려보내는 주소를 설정하는 부분입니다. 그러므로 사이트는 해당 route를 가지고 있어야 합니다. 다음 파일에서 해당부분을 설명합니다.
간략히 코드의 진행 순서를 설명드리면,
new GoogleStrategy
의 callback function이 실행. 여기서 done(null, user)
를 통해 user를 passport.serializeUser
에 전달passport.serializeUser
에서 user를 session에 저장.passport.deserializeUser
를 실행하여 session에서 user를 꺼내 user를 복원.즉 1-3번은 로그인 이후 단 한번만 실행되고, 4번은 이후 request마다 실행됩니다. 위 코드에서는 별도의 user 구조 없이 구글에서 보낸 profile을 그대로 user 모델로 사용하고 있습니다.
만약 사이트의 정해진 user 모델이 있고, DB에 저장하는 경우, new GoogleStrategy
의 callback function에서 DB의 user를 찾거나 생성해 주고, 자신의 용도에 맞게 session에 user를 저장할지(위 코드는 session에 user의 전체 정보를 저장합니다), 아니면 매번 DB에서 읽어 올지를 결정하면 됩니다(이 경우 passport.serializeUser에서는 user의 db id만 사용하고, passport.deserializeUser
에서 저장된 db id값으로 DB에서 user를 읽어오게 코드를 수정합니다).
console.log('profile: ', profile)
를 통해 구글이 어떤 값을 전달하는지 확인해 볼 수 있게 하였습니다. 아래와 같은 정보가 전달됩니다.
provider
는 'google'로 이 정보가 구글에서 왔다는 것을 알려줍니다. subid
는 google에서 유저를 특정할 수 있는 고유 아이디입니다.
만약 user를 DB에 저장하여 관리한다면 이 두값을 이용하여 DB에 해당 값을 가진 user가 없다면 user를 생성하고, 해당 값을 가진 user가 있다면 그 user를 가져와서 사이트에 로그인시키면 되겠습니다.
// routes/auth.js var express = require('express'); var router = express.Router(); var passport = require('../config/passport.js'); router.get('/login', function(req,res){ res.render('auth/login'); }); router.get('/logout', function(req, res) { req.logout(); res.redirect('/'); }); router.get('/google', passport.authenticate('google', { scope: ['profile'] }) ); router.get('/google/callback', passport.authenticate('google'), authSuccess ); function authSuccess(req, res) { res.redirect('/'); } module.exports = router;
중요하게 살펴봐야 할 곳은 /google route과 /google/callback route입니다.
/google route에서 passport.authenticate('google', { scope: ['profile'] })
를 실행하면 구글 로그인페이지로 이동하여 로그인이 이루어지게 됩니다. 로그인이 성공하면 config/passport.js에서의 callbackURL
설정에 따라 /google/callback 페이지로 이동하게 되고, 여기서 authSuccess
callback function이 호출됩니다.
// routes/main.js var express = require('express'); var router = express.Router(); router.get('/', function(req,res){ res.render('main', {user: req.user}); }); module.exports = router;
main page는 req.user
를 user로 view에 전달하는데, 로그인이 되어 있다면 req.user
에는 user의 정보가 들어가고, 로그인이 되어 있지 않다면 req.user
는 아무런 값이 없습니다.
<!-- views/main.ejs --> <h1>Main Page</h1> <% if(!user){ %> <a href="auth/login">Login</a> <% } else { %> <p>Welcome <%= user.displayName %></p> <a href="auth/logout">Logout</a> <% } %>
user
의 값의 유무를 판단하여 로그인이 되어 있으면 displayName
을 표시하고 logout 링크를 보여줍니다. 아니라면 login 페이지 링크를 보여줍니다.
<!-- views/auth/login.ejs --> <h1>Auth Page</h1> <a href="google">Google Login</a>
로그인 페이지에는 /auth/google 페이지로 이동할 수 있는 링크가 있습니다.
댓글
이 글에 댓글을 다시려면 SNS 계정으로 로그인하세요. 자세히 알아보기