TIL - 20260302

juni·2026년 3월 2일

TIL

목록 보기
282/316

0302 NestJS 심화 (2/N): Refresh Token과 OAuth2 소셜 로그인


✅ 1. JWT 인증의 한계와 Refresh Token 전략

  • 이전에 구현한 JWT 인증 방식은 Access Token만 사용했습니다. 이 방식은 몇 가지 한계점을 가집니다.

  • Access Token의 딜레마:

    • 만료 시간을 길게 설정하면?: 한 번 토큰이 탈취되면, 만료될 때까지 공격자가 계속해서 악용할 수 있어 보안에 취약합니다.
    • 만료 시간을 짧게 설정하면?: 사용자가 너무 자주 다시 로그인해야 하므로 사용자 경험(UX)이 저하됩니다.
  • 해결책 (Refresh Token 전략): Access Token과 Refresh Token이라는 두 종류의 토큰을 함께 사용하는 방식입니다.

토큰 종류Access TokenRefresh Token
역할실제 API 요청에 대한 인증Access Token 재발급 전용
만료 시간짧음 (e.g., 15분 ~ 1시간) (e.g., 7일 ~ 30일)
전송 위치Authorization 헤더 (매 요청마다 전송)Access Token 재발급 요청 시에만 전송
저장 위치(클라이언트) 메모리, 로컬 스토리지(클라이언트) Http-Only 쿠키 (더 안전)

➕ Refresh Token 동작 흐름

  1. [최초 로그인]: 사용자가 로그인에 성공하면, 서버는 Access TokenRefresh Token을 모두 발급합니다.
  2. [API 요청]: 클라이언트는 API 요청 시, Access Token만 헤더에 담아 보냅니다.
  3. [Access Token 만료]: Access Token이 만료되어 서버로부터 401 Unauthorized 에러를 받습니다.
  4. [토큰 재발급 요청]: 클라이언트는 이 에러를 감지하고, 가지고 있던 Refresh Token을 담아 서버의 토큰 재발급 엔드포인트(e.g., /auth/refresh)로 조용히 요청을 보냅니다.
  5. [서버의 Refresh Token 검증]: 서버는 전달받은 Refresh Token이 유효한지, 그리고 DB에 저장된 값과 일치하는지 검증합니다.
  6. [새 Access Token 발급]: 검증에 성공하면, 서버는 새로운 Access Token만을 생성하여 클라이언트에게 전달합니다.
  7. [실패했던 요청 재시도]: 클라이언트는 새로 발급받은 Access Token으로, 아까 실패했던 원래의 API 요청을 다시 시도합니다. 이 모든 과정은 사용자 모르게 백그라운드에서 진행됩니다.
  • 장점: Access Token의 수명을 짧게 유지하여 보안을 강화하면서도, Refresh Token 덕분에 사용자가 로그인을 자주 할 필요가 없어 사용자 경험을 해치지 않습니다.

✅ 2. OAuth2 소셜 로그인

  • OAuth2는 사용자가 특정 서비스(우리의 앱)에게, 자신의 계정이 있는 다른 서비스(e.g., Google, Kakao)의 특정 자원(e.g., 프로필 정보)에 접근할 수 있는 권한을 부여하는 "인가(Authorization)"를 위한 표준 프로토콜입니다.

  • 핵심 아이디어: 우리 서비스가 사용자의 Google/Kakao 비밀번호를 직접 받지 않고도, 신뢰할 수 있는 외부 서비스로부터 인증을 위임받는 안전한 방식입니다.

➕ OAuth2의 주요 용어

  • Resource Owner: 사용자 (자원의 소유자).
  • Client: 사용자의 자원에 접근하려는 우리의 애플리케이션.
  • Authorization Server: 사용자를 인증하고, Client에게 Access Token을 발급해주는 서버 (e.g., Google/Kakao 인증 서버).
  • Resource Server: 사용자의 자원을 실제로 가지고 있는 서버 (e.g., Google/Kakao API 서버).

➕ OAuth2 동작 흐름 (Authorization Code Grant)

  1. [사용자]: 우리 앱에서 "Google로 로그인" 버튼을 클릭합니다.
  2. [우리 앱 → Google]: 우리 앱은 사용자를 Google의 로그인 및 동의 화면으로 리다이렉트시킵니다.
  3. [사용자 ↔ Google]: 사용자는 Google 계정으로 로그인하고, 우리 앱이 자신의 프로필 정보에 접근하는 것을 동의합니다.
  4. [Google → 우리 앱]: Google은 사용자를 우리 앱이 미리 지정한 리다이렉트 URI로 다시 돌려보내면서, 임시 "인가 코드(Authorization Code)"를 URL에 포함시켜 전달합니다.
  5. [우리 앱 백엔드 ↔ Google]: 우리 앱의 백엔드 서버는 이 인가 코드를 받아, 자신의 Client ID, Client Secret과 함께 Google 인증 서버로 보냅니다.
  6. [Google → 우리 앱 백엔드]: Google은 모든 정보가 유효하면, 실제 사용자 정보에 접근할 수 있는 "Access Token"을 우리 앱의 백엔드 서버에 발급해줍니다.
  7. [우리 앱 백엔드 → Google]: 우리 서버는 발급받은 Access Token을 사용하여 Google API 서버에 사용자 정보(이메일, 이름 등)를 요청합니다.
  8. [우리 앱 백엔드]: 사용자 정보를 성공적으로 받아온 후, 다음을 수행합니다.
    • (회원가입) 해당 이메일이 우리 DB에 없으면, 자동으로 회원가입 처리.
    • (로그인) DB에 정보가 있으면, 로그인 처리.
    • 마지막으로, 우리 서비스만의 독자적인 JWT를 생성하여 프론트엔드 클라이언트에게 전달합니다.
  • NestJS와 Passport.js: NestJS에서는 Passport.js 라이브러리(passport-google-oauth20, passport-kakao 등)와 그 Strategy들을 사용하여, 위와 같은 복잡한 OAuth2 흐름을 매우 구조적이고 편리하게 구현할 수 있습니다.

📌 요약

  • Refresh Token 전략은 수명이 짧은 Access Token과 수명이 긴 Refresh Token을 함께 사용하여, 보안사용자 경험을 모두 만족시키는 현대적인 인증 방식입니다.
  • OAuth2는 우리 서비스가 사용자의 외부 서비스(Google, Kakao) 비밀번호를 직접 다루지 않고, 인증을 안전하게 위임받는 "인가" 프로토콜입니다.
  • 소셜 로그인 성공 후, 우리 서버는 받아온 사용자 정보를 바탕으로 우리 서비스에 회원가입/로그인을 시키고, 최종적으로는 우리 서비스만의 독자적인 JWT를 클라이언트에게 발급하여 이후의 인증을 처리합니다.

0개의 댓글