OAuth2 사용 이유와 Kakao, Google 소셜 로그인 적용기 (에러 정리 포함)

dejeong·2025년 8월 8일

에러 해결 일지

목록 보기
6/7
post-thumbnail

칭구칭구 프로젝트에서 기존의 ID/PW 로그인 방식과 함께 OAuth2 기반 소셜 로그인(Google, Kakao)을 도입했다.

OAuth2는 사용자의 비밀번호를 직접 관리하지 않고, 외부 인증 제공자(OAuth Provider) 에게 인증을 위임하는 방식으로 칭구칭구에서는 이 인증 결과만 받아 JWT를 발급하여 내부 인증에 활용하는 구조로 설계했다.


🖐️ OAuth2 란?

OAuth2는 인증(Authentication) 과 인가(Authorization) 를 분리하여 처리할 수 있도록 하는 표준 프로토콜이다.

즉, 서비스가 직접 사용자 비밀번호를 검증하지 않고, 신뢰할 수 있는 외부 서비스(Google, Kakao 등)에 인증을 위임하는 방식이다.

구성 요소설명
Resource Owner로그인하려는 사용자
Client인증을 요청하는 애플리케이션 (우리 서비스)
Authorization Server인증을 처리하는 서버 (Google, Kakao 등)
Resource Server사용자 정보를 보유한 서버
Access Token인증 후 클라이언트에게 부여되는 접근 권한 토큰

이 구조를 통해 서비스는 사용자 비밀번호를 직접 다루지 않아도 되고,
Access Token을 기반으로 필요한 정보에 접근할 수 있다.


🤔 OAuth2 사용 이유

  1. 보안성 강화

    • 사용자 비밀번호를 직접 저장하거나 검증하지 않는다.
    • OAuth Provider(Google, Kakao)가 인증을 수행하므로 비밀번호 유출 위험이 줄어든다.
  2. 사용자 경험 향상

    • 별도의 회원가입 없이 간편하게 로그인할 수 있다.
    • 모바일 환경에서도 인증 절차가 단순해져 접근성이 좋아진다.
  3. 유지보수 및 확장성

    • OAuth2는 표준화된 프로토콜이므로, 다른 소셜 로그인(Naver, Apple 등)을 쉽게 추가할 수 있다.
    • JWT 기반 인증과도 자연스럽게 연결되어 서비스 확장이 용이하다.

✍️ OAuth2 인증 과정

OAuth2의 기본적인 동작 절차는 다음과 같다.

  1. 사용자가 “소셜 로그인” 버튼을 클릭하면, 클라이언트(우리 서비스)는
    인증 요청을 해당 Provider(Google, Kakao)로 리다이렉트한다.

  2. 사용자는 Provider 로그인 페이지에서 인증을 수행한다.

  3. 인증이 성공하면, Provider는 Authorization Code를 우리 서버로 전달한다.

  4. 서버는 Authorization Code를 이용해 Access Token을 요청한다.

  5. Access Token을 이용해 Provider의 사용자 정보 API(UserInfo) 에 접근한다.

  6. 응답받은 사용자 정보를 기반으로 신규 사용자는 회원가입 처리, 기존 사용자는 로그인 처리를 수행한다.

  7. 이후 서비스 내부에서는 자체 JWT 토큰을 발급하여 인증 상태를 유지한다.

즉, OAuth2는 외부 인증을 담당하고,
JWT는 내부 인증을 담당하는 구조로 역할이 분리 된다.


✅ 프로젝트에서의 적용 방식

[사용자] → [프론트엔드] → [백엔드(Spring Boot)] → [OAuth2 Provider]
        ↓                                      ↓
   JWT 발급 및 응답  <---  Access Token + UserInfo

칭구칭구 프로젝트에서는 Spring Security를 기반으로 OAuth2 Login 기능과 JWT 인증을 결합하여 구현했다.

  • OAuth2 인증: Google, Kakao가 사용자의 신원을 확인하고 Access Token을 발급
  • JWT 발급: OAuth2 인증 성공 시 서버가 자체 JWT를 생성해 프론트엔드에 전달
  • API 접근: 이후 모든 요청은 Authorization: Bearer <JWT> 헤더로 접근 제어

이 구조를 사용하면 로그인 상태 유지가 간단해지고,
OAuth Provider가 만료되더라도 내부 세션(JWT)은 독립적으로 관리된다.


🐞 소셜 로그인 로직 추가 후 변경사항

OAuth2 로그인 구조를 적용한 뒤, 기존의 ID/PW 로그인 로직과 충돌하는 부분이 있어 일부 수정이 필요했다.

  • 회원 탈퇴 시 비밀번호 확인 절차 생략
    -> 소셜 로그인 사용자는 비밀번호가 없기 때문
  • 친구 검색 시 ID 검색 제외 (닉네임/이름만 검색)
    -> 소셜 로그인한 상태면 아이디가 임의로 생성되는데, 프론트 설계상 마이홈에 아이디가 노출 되는 부분이 따로 없어서 검색 시 혼란이 발생했기 때문

더 좋은 대처 방법이 있을 수 있겠지만 당장 생각나는 방법으로 진행했다.
리팩토링할 때 더 좋은 방법이 있는지 찾아보기..⭐


🥅 JWT와 OAuth2의 역할 구분

구분OAuth2JWT
역할외부 서비스의 인증 위임내부 서비스의 인가(Authorization)
토큰 발급 주체Google, Kakao 등 Provider우리 서버
사용 목적사용자 신원 확인API 접근 권한 검증
수명Provider 정책에 따라 짧음서비스 설정에 따라 조정 가능
보안 관리Provider가 담당우리 서버가 담당

🔗 Kakao, Google 로그인 연동 과정

어리바리스타하면서 연동을 진행한 덕분에 중간중간 캡쳐하는 것을 잊었다.. 하하하! 간략하게 작성하자면 아래와 같다.

Kakao

  1. 카카오 디벨로퍼스에서 애플리케이션 등록
  2. Redirect URI 설정
  3. Authorization Code → Access Token → UserInfo 호출
  4. id, email, nickname 등을 수신 후 내부 DB에 저장

Google

  1. Google Cloud Console에서 OAuth 클라이언트 ID 발급
  2. profile, email 스코프 요청
  3. Access Token으로 https://www.googleapis.com/oauth2/v2/userinfo 호출
  4. 사용자 정보 추출 및 내부 회원 매핑

카카오 소셜 로그인은 다행스럽게도 무난하게 적용 되었는데, Google 소셜 로그인 연동할 때는 꽤 많은 오류를 겪었다.. (눈에서 땀이..)

⚠️ Google OAuth2 로그인 당시 발생했던 주요 오류

구분증상원인해결 방법
500 Internal Server Error (Vercel 프론트 요청 시)백엔드 서버에서 Google 인증 후 리디렉션 URI 불일치 (redirect_uri_mismatch)Google Cloud Console의 OAuth2 클라이언트 설정에서 승인된 리디렉션 URI를 실제 프론트 배포 주소(https://chinguchingu.vercel.app/login/oauth2/code/google)로 수정
"invalid_grant" 또는 "invalid_client" 오류- 환경 변수(Google Client ID/Secret) 불일치
- 이미 사용된 Authorization Code를 재요청
Google OAuth 환경 변수를 동일하게 맞추고, 인증 과정에서 한 번 사용된 Code를 재요청하지 않도록 수정
Security FilterChain에서 인증이 중단됨JwtAuthenticationFilterOAuth2LoginFilter보다 먼저 실행되어 OAuth2 요청도 JWT 검증 대상으로 처리함SecurityConfig에서 addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) 로 위치 재조정 및 permitAll() 경로에 /oauth2/** 추가
로그인 성공 후 리디렉션 실패OAuth2SuccessHandler 에서 Redirect URL이 하드코딩되거나 환경변수 누락frontend.urlapplication.properties 환경 변수로 등록하고, response.sendRedirect(frontendUrl + "/oauth-success?token=" + jwtToken) 형식으로 수정
UserInfo 응답 파싱 에러 (NullPointerException)Google과 Kakao의 응답 필드명이 달라 공통 파서가 null 필드를 참조함 (Google→ sub, Kakao→ id, 중첩 JSON 구조 차이)Provider별 DTO(GoogleUserInfo, KakaoUserInfo) 구현 후 OAuth2UserInfoFactory로 추상화. 이때 응답을 공통 형태로 매핑하는 CustomOAuth2User 를 도입하여 provider, providerId, email, nickname, profileImage 필드를 표준화. 이 구조로 NPE 및 필드 불일치 문제 해결

이 중 가장 복잡했던 문제는 ⑤ UserInfo 파싱 에러였다.
Google 응답은 sub, name, picture 같이 단일 필드로 오지만,
Kakao는 kakao_account.emailproperties.nickname 처럼 중첩 JSON 구조를 사용한다.

두 Provider를 같은 로직으로 처리하면서 NullPointerException 이 발생했으나,
이후 CustomOAuth2User 를 설계하여 안정적으로 사용자 정보를 통합할 수 있게 되었다.


🙇‍♀️ 배운 점

처음에는 OAuth2를 깊게 생각하지 않고 단순히 소셜 로그인 기능 을 적용할 때 사용 하는 것으로 여겼었지만 이번 프로젝트를 통해 그것이 “인증 책임을 위임하는 구조적 설계” 임을 이해할 수 있게 되었다.

Spring Security의 인증 흐름을 따라가며
OAuth2 → Access Token 교환 → 사용자 정보 → JWT 발급까지의 과정을 직접 구성하면서,
인증과 인가를 분리하는 것이 서비스 안정성과 확장성을 높이는 방법이라는 것을 알 수 있었다.

Kakao와 Google처럼 응답 스펙이 다른 Provider를 통합하기 위해
공통 인터페이스를 설계하면서, 유지보수성과 유연성을 동시에 고려하는 설계 능력이 중요하다는 것을 깨달을 수 있었다.

이번에는 오류를 수정하는데 급급했지만 다음에 또 기회가 된다면, 이론적으로 좀 더 공부하며 차분하게 진행해보고 싶다.

profile
룰루

0개의 댓글