프로젝트여정기

소셜 로그인, 그냥 버튼 하나 누르면 되는 줄 알았습니다..ㅠ

코딩하는둥이 2025. 8. 5. 11:35

1. 소셜 로그인, 도대체 뭐지?
처음에는 단순히 api로 구현되어 있을테니 간단한 작업으로 알았습니다.  
하지만 실제로 구현해보니 OAuth2, 토큰, 리디렉션, CORS, 프론트-백 분리 구조 등 복잡한 요소들이 얽혀 있었습니다🥹

 


2. 기존 로그인 방식과의 차이점

인증 정보 ID / PW 직접 입력 외부 서비스 로그인
토큰 처리 직접 발급/검증 구현 OAuth2 기반 토큰 전달
장점 커스터마이징 자유 빠른 가입, 사용자 접근성
단점 유지 보수/보안 직접 관리 OAuth 플로우 이해 필요



3. 소셜 로그인 구조 이해하기

 

1) 일반 로그인 흐름

 

1. 프론트엔드 → 백엔드

사용자가 아이디와 비밀번호를 입력하면, 프론트가 그걸 백엔드에 전달합니다.

 

 

2. 백엔드 → 프론트엔드

백엔드는 입력한 아이디/비밀번호가 맞는지 확인하고,
맞으면 다음 3가지를 보내줩니다.

  • AccessToken (열쇠 같은 거, 유효시간 짧음)
  • RefreshToken (열쇠 새로 만드는 열쇠, 유효시간 김)
  • AccessToken 만료 시간

그리고 RefreshToken은 서버(DB)에 저장합니다.
왜냐하면 나중에 새로운 AccessToken을 줄 때, 이게 진짜 사용자 건지 확인 필수!!!!

 

3. 프론트엔드 → API 요청할 때

사용자는 로그인 후 서비스 여러 기능을 이용하게 되죠?
이때마다 매번 AccessToken을 HTTP 헤더에 담아서 요청합니다.

Authorization: Bearer <AccessToken>

 

4. 백엔드 → 요청 처리

서버는 받은 AccessToken이 유효한지 확인하고, 문제가 없다면 해당 API 기능을 실행합니다.

 

 

5. AccessToken이 곧 만료될 때?

AccessToken은 보통 15분~1시간 내외로 금방 만료됩니다.
만약 남은 시간이 30초도 안 남았다면, 프론트는 이렇게 합니다:

  • "이 열쇠(AccessToken) 곧 만료되니까 새 걸 주세요!"
  • → 대신 진짜 주인이라는 걸 증명하려고 RefreshToken을 같이 보냄

6. 백엔드 → 새 AccessToken 발급

서버는 RefreshToken이 DB에 저장된 것과 같은지 확인하고, 맞으면 새로운 AccessToken + 만료시간을 만들어서 보내줩니다.

 

7. 프론트엔드 → 새 AccessToken 저장

받은 새로운 AccessToken을 저장해두고, 이후 API 요청에 다시 사용합니다.

 

비유로 설명하자면…

  • AccessToken = 건물 출입증 (단기 유효)
  • RefreshToken = 주민등록증 (신분 확인용, 유효기간 김)
  • 출입증이 곧 만료되면, 주민등록증을 보여주고 새 출입증을 받는 구조입니다.

 

4. 실제 코드로 구현한 소셜 로그인 (LiveStudy 프로젝트 기준)

.env 설정:

VITE_API_BASE_URL=https://api.live-study.com
VITE_GOOGLE_CLIENT_ID=...
VITE_KAKAO_CLIENT_ID=...
VITE_NAVER_CLIENT_ID=...

 

소셜 로그인 처리 방법 정리 (Google, Kakao, Naver)

// 소셜 로그인 URL 설정
const VITE_API_BASE_URL = import.meta.env.VITE_API_BASE_URL!;

// 구글 로그인
export const GOOGLE_AUTH_URL =
  'https://accounts.google.com/o/oauth2/v2/auth?' +
  new URLSearchParams({
    client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID!,
    redirect_uri: `${VITE_API_BASE_URL}/login/oauth2/code/google`, // 백엔드에서 지정한 구글 콜백 주소
    response_type: 'code',
    scope: 'openid profile email',
    access_type: 'offline',
    prompt: 'consent',
  });

// 카카오 로그인
export const KAKAO_AUTH_URL =
  'https://kauth.kakao.com/oauth/authorize?' +
  new URLSearchParams({
    client_id: import.meta.env.VITE_KAKAO_CLIENT_ID!,
    redirect_uri: `${VITE_API_BASE_URL}/login/oauth2/code/kakao`,
    response_type: 'code',
  });

// 네이버 로그인
export const NAVER_AUTH_URL =
  'https://nid.naver.com/oauth2.0/authorize?' +
  new URLSearchParams({
    client_id: import.meta.env.VITE_NAVER_CLIENT_ID!,
    redirect_uri: `${VITE_API_BASE_URL}/login/oauth2/code/naver`,
    response_type: 'code',
    state: 'liveStudyStateToken',
  });

 

소셜 로그인 버튼 UI 구현과 OAuth 연동 정리

  <a
    href={GOOGLE_AUTH_URL}
    className="w-full py-3 bg-gray-100 border border-gray-300 rounded-xl text-body1_R text-gray-500 hover:bg-gray-200 text-center"
  >
    Google 로그인
  </a>

  <a
    href={KAKAO_AUTH_URL}
    className="w-full py-3 bg-gray-100 border border-gray-300 rounded-xl text-body1_R text-gray-500 hover:bg-gray-200 text-center"
  >
    Kakao 로그인
  </a>

  <a
    href={NAVER_AUTH_URL}
    className="w-full py-3 bg-gray-100 border border-gray-300 rounded-xl text-body1_R text-gray-500 hover:bg-gray-200 text-center"
  >
    Naver 로그인
  </a>



5. 내가 직접 겪은 문제들...

문제 1: "구글/카카오 로그인 후 화면이 안 넘어감"
- 증상: 인증까지 했는데 이후 리디렉션이 안 됨
- 원인: Spring Security에서 success handler 또는 defaultSuccessUrl 설정 누락
- 해결: OAuth2AuthenticationSuccessHandler에 sendRedirect() 추가

문제 2: 환경변수 설정 오류
- 증상: 로컬에서는 되는데 배포 시 안 됨
- 원인: VITE_API_BASE_URL 값이 환경마다 달라야 하는데 고정돼 있었음
- 해결: .env.development, .env.production 분리하여 처리