구글 로그인 구현 — Passport + OAuth
이 토픽을 마치면
Google Cloud Console에서 OAuth 클라이언트를 설정하고, Passport.js의 Google Strategy로 소셜 로그인을 구현할 수 있습니다.
전제 조건
이전 토픽에서 배운 내용이 필요합니다:
- OAuth 2.0 흐름 — Authorization Code Flow의 4명 등장인물
- Passport.js — Strategy 패턴과 serializeUser/deserializeUser
- Express Session — 세션 기반 인증
이 세 가지가 합쳐져서 구글 로그인이 됩니다.
1단계 — Google Cloud Console 설정
구글에서 우리 앱을 등록해야 합니다. 이 과정은 코드가 아니라 웹 콘솔 작업입니다.
- Google Cloud Console → 새 프로젝트 생성
- "OAuth 동의 화면" → 앱 이름, 이메일 입력
- "사용자 인증 정보" → OAuth 2.0 클라이언트 ID 만들기
- 승인된 리다이렉션 URI에
http://localhost:3000/auth/google/callback추가
결과로 Client ID와 Client Secret 두 개를 받습니다. 이 값은 .env에 저장합니다.
GOOGLE_CLIENT_ID=abc123...
GOOGLE_CLIENT_SECRET=xyz789...2단계 — 패키지 설치
npm install passport-google-oauth20이미 passport, express-session이 설치되어 있다고 가정합니다.
3단계 — Strategy 등록
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback',
},
(accessToken, refreshToken, profile, done) => {
// profile에 구글 사용자 정보가 들어있음
// DB에서 사용자를 찾거나 새로 생성
const user = {
googleId: profile.id,
name: profile.displayName,
email: profile.emails[0].value,
photo: profile.photos[0].value,
};
// 실제로는 DB 조회/저장
done(null, user);
}
));이 콜백 함수는 구글에서 인가 코드→토큰 교환이 끝난 뒤 호출됩니다. Passport가 OAuth의 복잡한 흐름을 전부 처리하고, 우리는 사용자 정보만 받으면 됩니다.
4단계 — 라우트 연결
// 구글 로그인 시작
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile', 'email'] })
);
// 구글이 인가 코드를 돌려보내는 곳
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/');
}
);
// 로그아웃
app.get('/logout', (req, res) => {
req.logout(() => {
res.redirect('/');
});
});흐름을 따라가 보겠습니다:
- 사용자가
/auth/google접속 → 구글 로그인 화면으로 이동 - 구글에서 로그인 + 허용 → 구글이
/auth/google/callback?code=xxx로 리다이렉트 - Passport가
code로 토큰 교환 → Strategy 콜백 호출 → 세션에 사용자 저장 res.redirect('/')→ 로그인된 상태로 홈으로
5단계 — serialize/deserialize
passport.serializeUser((user, done) => {
done(null, user.googleId);
});
passport.deserializeUser((googleId, done) => {
// 실제로는 DB에서 googleId로 조회
done(null, { googleId, name: '...' });
});serialize는 세션에 뭘 저장할지, deserialize는 세션에서 꺼낸 값으로 사용자 객체를 복원하는 방법을 정의합니다.
세션에 사용자 객체 전체를 넣지 말고, ID만 저장하세요. 세션 데이터가 작을수록 서버 부담이 줄어듭니다.
전체 구조 정리
사용자 우리 서버 구글
│ │ │
├─ /auth/google ─────→│ │
│ ├─ redirect ────────→│
│ │ │
│←── 구글 로그인 화면 ─┤ │
│ │ │
├─ 로그인+허용 ───────→│ │
│ │←── code ──────────┤
│ ├─ code+secret ─────→│
│ │←── access_token ──┤
│ ├─ token ───────────→│
│ │←── profile ───────┤
│←── 세션 생성, / ─────┤ │Passport가 가운데 5단계(code 수신 → token 교환 → profile 요청)를 전부 처리합니다. 우리가 한 건 Strategy 등록과 라우트 연결뿐입니다.
자주 하는 실수
- redirect URI 불일치 — 콘솔에 등록한 URI와 코드의
callbackURL이 정확히 같아야 합니다. 슬래시 하나 차이로 에러 납니다. - scope 누락 —
scope: ['profile', 'email']을 안 넣으면profile.emails가 undefined입니다. - Client Secret 노출 —
.env에 넣고.gitignore에.env를 추가하세요. GitHub에 Secret이 올라가면 구글이 자동으로 비활성화합니다.
핵심
Passport.js + Google Strategy는 OAuth 2.0의 복잡한 흐름을 Strategy 하나로 감싸줍니다. 우리가 할 일은 Console 설정 → Strategy 등록 → 라우트 2개 연결 — 이것이 전부입니다. 핵심 흐름:
/auth/google→ 구글 로그인 →/auth/google/callback→ 세션 생성 → 홈 리다이렉트.