컴퓨터 사이언스/네트워크

[소셜 로그인] 프론트엔드 관점에서의 로그인 구현

Dachaes 2025. 4. 18. 15:02
728x90
반응형

소셜 로그인 

요즘 웹 서비스에서 소셜 로그인은 필수적인 기능이 되었습니다. 사용자는 구글이나 카카오 같은 계정으로 간편하게 로그인할 수 있고, 서비스는 복잡한 회원가입 과정을 줄일 수 있죠. 하지만 개발자 입장에서는 단순한 버튼 클릭만으로 끝나지 않습니다. OAuth 인증 흐름, 인가 코드 처리, 백엔드 연동, JWT 토큰 관리까지 고려할 요소가 많습니다.
그리고 이런 흐름은 일반 로그인과도 대부분 유사합니다. 인증 요청, 응답 처리, 토큰 저장 및 상태 관리라는 구조는 동일하죠.

이 글에서는 프론트엔드 관점에서 소셜 로그인 구현의 전체 흐름을 정리하고, React로 구글 로그인을 어떻게 구현하는지 실습 코드와 함께 설명합니다.

 


1.  전체 구조 - 소셜 로그인 흐름 (간단 정리)

  1. 프론트엔드
    • 사용자가 "구글로 로그인", "카카오로 로그인" 버튼 클릭
    • 소셜 로그인 SDK or OAuth 2.0 인증 URL로 이동
    • 인증 성공 후, 인가 코드 또는 access token을 받아옴
  2. 백엔드
    • 받은 코드/token 으로 소셜 서비스에 사용자 정보 요청
    • 사용자 정보를 바탕으로 자체 유저 DB에서 회원 가입 or 로그인 처리
    • 자체 JWT 토큰 발급 후 프론트에 전달
  3. 프론트엔드
    • 받은 JWT를 로컬 스토리지 또는 쿠키에 저장
    • 이후 API 요청 시 Authorization 헤더에 JWT 포함

 


2.  프론트엔드 관점에서 소셜 로그인

a.  소셜 로그인 버튼 구성

사용자에게 보여줄 로그인 버튼 UI입니다. 예를 들어, 구글 로그인 버튼을 만든다면 아래와 같이 코드를 적습니다.

<button onClick={handleGoogleLogin}>Google로 로그인</button>
  • 각 플랫폼에서 공식 버튼 디자인 가이드가 있는 경우, 해당 가이드를 따르는 것이 좋습니다.

b.  OAuth 인증 요청 URL 만들고 리디렉션

구글 OAuth를 예로 들면, 로그인 요청 시 브라우저를 구글 인증 페이지로 리디렉트시켜야 합니다.

const GOOGLE_CLIENT_ID = 'your_google_client_id';
const REDIRECT_URI = 'https://yourdomain.com/oauth/callback';
const SCOPE = 'profile email';
const RESPONSE_TYPE = 'code';

const handleGoogleLogin = () => {
  const url = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=${encodeURIComponent(
    REDIRECT_URI
  )}&response_type=${RESPONSE_TYPE}&scope=${encodeURIComponent(SCOPE)}`;
  window.location.href = url;
};
  • 사용자가 버튼을 클릭하면 구글 로그인 화면으로 이동합니다.
  • 사용자가 로그인하면 https://yourdomain.com/oauth/callback?code=xxxxx로 리디렉트됩니다.

c.  Redirect URI에서 인가 코드 수신, 백엔드로 코드 전달

// /oauth/callback 페이지에서 실행되는 코드
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';

const OAuthCallback = () => {
  const navigate = useNavigate();

  useEffect(() => {
    const code = new URLSearchParams(window.location.search).get('code');

    if (code) {
      axios
        .post('/api/auth/google', { code })
        .then((res) => {
          localStorage.setItem('token', res.data.token);
          navigate('/dashboard');
        })
        .catch((err) => {
          console.error(err);
        });
    }
  }, []);

  return <div>로그인 처리 중...</div>;
};
  • 백엔드는 이 코드를 받아서 구글에 사용자 정보 요청 후, 자체 JWT 발급합니다.
  • 프론트는 이 토큰을 저장하고 로그인 상태로 전환합니다.
  • 이때 백엔드는 일반적으로 Access Token 과 함께 Refresh Token 도 함께 발급합니다.  
    • Access Token : 프론트에서 인증된 API 요청에 사용
    • Refresh Token : 유효기간이 만료된 Access Token을 갱신하는 데 사용
  • 보안을 위해 Refresh TokenHttpOnly 쿠키에 저장되어, 프론트에서 직접 접근하지 않고 브라우저가 자동으로 요청에 포함하도록 합니다.

예시 - 토큰 갱신 코드 (axios 인터셉터)

// axios 인터셉터 내부
axios.interceptors.response.use(
  (res) => res,
  async (error) => {
    if (error.response.status === 401) {
      // access token 만료 → refresh token으로 재발급 시도
      try {
        const res = await axios.post('/api/auth/refresh', {}, { withCredentials: true });
        const newAccessToken = res.data.accessToken;

        localStorage.setItem('token', newAccessToken);
        error.config.headers.Authorization = `Bearer ${newAccessToken}`;

        return axios(error.config); // 원래 요청 재시도
      } catch (e) {
        // refresh token도 만료 → 로그인 페이지로 이동
        window.location.href = '/login';
      }
    }

    return Promise.reject(error);
  }
);

d.  토큰 관리 - JWT 저장 및 로그인 상태 관리

localStorage.setItem('token', token);
저장소 장점 단점
localStorage 구현 간편, 클라이언트 전용 XSS 위험
sessionStorage 탭 단위로 분리 가능 브라우저 닫으면 소멸
HTTP-only 쿠키 보안성 높음 (XSS 대응) 클라이언트에서 직접 접근 불가, 서버 설정 필요
  • JWT를 localStorage, sessionStorage, 또는 HTTP-only 쿠키에 저장
  • React에서는 주로 localStorage 또는 쿠키를 사용합니다.

e.  인증 상태 기반 UI 분기 처리 - 로그인 여부에 따라 메뉴 다르게 표시

const isLoggedIn = !!localStorage.getItem('token');

return (
  <div>
    {isLoggedIn ? (
      <button onClick={handleLogout}>로그아웃</button>
    ) : (
      <button onClick={handleGoogleLogin}>구글 로그인</button>
    )}
  </div>
);

f.  토큰을 Authorization 헤더에 자동 포함하기

  • API 요청 시 JWT를 Authorization: Bearer <token> 형태로 헤더에 포함

i.  axios의 전역 기본 헤더를 설정하는 방식

axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  • 한 번 설정하면 이후 모든 요청에 적용됩니다.
  • 단, 로그인 이후 토큰을 한 번만 설정하고, 이후 토큰이 바뀌어도 자동으로 갱신되지 않습니다.

ii.  매 요청마다 최신 토큰을 읽어와서 헤더에 삽입하는 방식 (axios 인터셉터)

// axios 인터셉터
axios.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
  • 모든 요청 전에 매번 실행됩니다.
  • 토큰 갱신(refresh) 후에도 문제 없이 작동합니다.
  • 로그인/로그아웃 상태가 바뀌어도 매번 새로 반영됩니다.

e.  로그아웃 처리

const handleLogout = () => {
  localStorage.removeItem('token');
  delete axios.defaults.headers.common['Authorization'];
  navigate('/');
};

f.  보너스 - 상태 관리 라이브러리와의 연동

Redux, Zustand, Recoil 등을 사용한다면,

  • JWT는 localStorage 에 저장
  • 로그인 후 상태 전역 관리(store)에 로그인 상태 저장
  • 앱 로딩 시 localStorage 의 JWT를 가져와 로그인 상태 복원
useEffect(() => {
  const token = localStorage.getItem('token');
  if (token) {
    setUser({ token });
  }
}, []);

 


3.  마무리

  • 프론트엔드 역할은 '소셜 로그인 요청 → 인가 코드 수신 → JWT 저장' 크게 세 가지입니다.
  • 보안과 사용자 경험을 모두 고려하려면 HTTP-only 쿠키, axios 인터셉터, 전역 상태 관리 등이 함께 설계되어야 합니다.
  • 각 소셜 플랫폼마다 로그인 방식이 조금씩 다르므로, 플랫폼 문서를 반드시 참고해야 합니다.

함께 보면 좋은 자료

블로그 글 : 

 

[OAuth] 로그인 없이 로그인하기

OAuth (Open Authorization) 현대 웹서비스에서 흔히 볼 수 있는 Google로 로그인, Kakao로 시작하기 같은 기능은 어떻게 작동할까요? 바로 이럴 때 사용하는 기술이 OAuth(Open Authorization)입니다. 이 글에서는

dachaes-devlogs.tistory.com

 

[JWT] 인증 시스템의 핵심

JWT (JSON Web Token) 웹 애플리케이션에서 로그인 후 사용자 인증 상태를 어떻게 유지할까요? 세션? 쿠키? 이 외에도 현대적인 방식으로 널리 사용되는 것이 바로 JWT(JSON Web Token)입니다.이 글에서는 J

dachaes-devlogs.tistory.com

 

[Access Token과 Refresh Token] 개발자를 위한 인증 토큰 설명서

Access Token과 Refresh Token JWT 기반 인증 시스템에서 가장 많이 나오는 개념 중 하나가 바로 Access Token과 Refresh Token입니다. 이 두 토큰은 모두 인가(Authorization)와 관련이 있지만 역할과 사용 목적이

dachaes-devlogs.tistory.com

 


 

728x90
반응형