Spring

Spring OAuth2 소셜 로그인 — Google, Kakao, Naver 연동까지

OAuth2 Authorization Code Grant 흐름부터 Spring Security OAuth2 Client 설정, CustomOAuth2UserService 구현, 회원가입/로그인 분기 처리까지 인터랙티브 시각화로 완전 정복합니다.

2026-03-22 · 약 13분 읽기

Q. "Spring Boot에서 OAuth2 소셜 로그인을 구현하는 방법을 설명하고, Google·Kakao·Naver를 연동할 때 플랫폼별 차이점은 무엇인지 말씀해주세요."

예상 꼬리질문

답변 가이드

OAuth2 소셜 로그인은 사용자가 자격증명을 직접 제공하지 않고, Google·Kakao·Naver 같은 외부 서비스에 인증을 위임하는 방식입니다. Spring Boot에서는 spring-boot-starter-oauth2-client 의존성 하나로 복잡한 OAuth2 흐름 대부분이 자동 처리됩니다.

핵심 흐름은 Authorization Code Grant 방식으로, 사용자가 소셜 플랫폼에서 인증 후 받은 인가 코드(Authorization Code)를 서버가 백채널(back-channel)로 Access Token과 교환합니다. 이 방식은 Access Token이 브라우저에 직접 노출되지 않아 보안에 유리합니다.CustomOAuth2UserService를 구현하여 각 플랫폼의 응답 구조 차이를OAuthAttributes라는 통합 객체로 변환하고,provider + providerId 조합으로 사용자를 식별하여 회원가입과 로그인을 분기합니다.

실무에서는 Google은 CommonOAuth2Provider로 자동 설정되지만, Kakao와 Naver는 Spring Security 기본 제공 Provider가 없어 application.yml에 authorization-uri, token-uri, user-info-uri를 직접 정의해야 합니다. 특히 Naver는 응답 구조가 response 키 하위에 중첩되어 있어 OAuthAttributes 파싱 로직에서 별도 처리가 필요합니다.

"Google로 로그인" 버튼 하나가 어떻게 동작하는지 정확히 설명할 수 있나요? 면접관이 이 질문을 할 때 원하는 건 단순 구현 방법이 아니라OAuth2 프로토콜의 보안 설계 의도를 이해하고 있는지입니다. 이 아티클에서는 8단계 Authorization Code 흐름을 인터랙티브 시뮬레이션으로 체험하고, Spring Security가 이 흐름을 어떻게 추상화하는지 코드 레벨까지 살펴봅니다.


1. OAuth2 Authorization Code Grant 흐름

꼬리질문: "Authorization Code Grant 흐름을 단계별로 설명해주세요."

소셜 로그인의 핵심은 인가 코드(Authorization Code) 방식입니다. 사용자가 소셜 플랫폼에서 직접 인증하고, 우리 서버는 인가 코드를 받아백채널(브라우저가 아닌 서버 간 통신)로 Access Token을 교환합니다. Access Token이 브라우저에 노출되지 않는다는 점이 핵심 보안 장치입니다.

흐름은 총 8단계로, Spring Security OAuth2 Client가 3~6단계를 자동으로 처리합니다. 개발자가 직접 구현해야 하는 부분은 7단계(사용자 정보 파싱)와 8단계(회원가입/로그인 분기)입니다.

브라우저

(사용자)

우리 서버

(Spring Client)

소셜 플랫폼

(Auth Server)

사용자 정보 서버

(Resource Server)

로그인 요청
단계 1 / 8

사용자가 "Google로 로그인" 버튼 클릭

GET /oauth2/authorize?response_type=code&client_id=xxx&redirect_uri=xxx&scope=email,profile&state=난수
Spring Security 자동 처리 (3~6단계)직접 구현 필요

2. Spring Security OAuth2 Client 설정 — Google vs Kakao vs Naver

꼬리질문: "Google, Kakao, Naver 연동 설정에서 차이점은 무엇인가요?"

세 플랫폼 연동의 설정 복잡도가 다릅니다.Google은 Spring Boot가 자동으로 Provider 정보를 알고 있어client-id와 client-secret만 설정하면 됩니다. 반면 Kakao와 Naver는 CommonOAuth2Provider에 포함되지 않아 authorization-uri, token-uri, user-info-uri를 모두 직접 정의해야 합니다.

Naver는 추가로 user-name-attribute: response를 설정해야 합니다. Naver의 API 응답이 응답 객체 하위에 중첩되어 있어, Spring Security가 사용자 식별 키로 response 객체를 사용하도록 지정해야 합니다.

Google OAuth2 설정

CommonOAuth2Provider 내장 — provider 섹션 불필요

자동 설정 (설정 불필요)

Provider 정의 (CommonOAuth2Provider 내장)
authorization-uri: accounts.google.com/o/oauth2/v2/auth
token-uri: oauth2.googleapis.com/token
user-info-uri: googleapis.com/oauth2/v3/userinfo

직접 설정 필요

client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
scope: openid, email, profile

3. CustomOAuth2UserService와 OAuthAttributes 구현

꼬리질문: "각 소셜 플랫폼의 사용자 정보를 어떻게 통합 처리하나요?"

세 플랫폼의 사용자 정보 응답 구조가 모두 다릅니다. Google은 최상위 레벨에 name, email, picture가 있고, Kakao는 kakao_account.profile.nickname 구조로 2단계 중첩, Naver는 response.name 구조로 1단계 중첩입니다. 이 차이를 흡수하는 것이 OAuthAttributes 값 객체입니다.

JSON 필드에 마우스를 올리면 OAuthAttributes 매핑 대상이 하이라이트됩니다.

파서: OAuthAttributes.ofGoogle()최상위 레벨에서 직접 파싱 — 가장 단순한 구조

API 응답 구조

{
"sub": "118400414981491",
"name": "홍길동",
"email": "hong@gmail.com",
"picture": "https://lh3.googleusercontent.com/..."
}

OAuthAttributes 매핑

attributes["sub"].providerId
attributes["name"].name
attributes["email"].email
attributes["picture"].picture

중첩 깊이

depth 0 — 최상위 필드
depth 1 — 1단계 중첩
depth 2 — 2단계 중첩

JSON 필드에 마우스를 올리면 매핑 대상이 하이라이트됩니다


4. 회원가입/로그인 분기 — provider + providerId 식별 전략

꼬리질문: "email이 아닌 provider+providerId로 식별하는 이유는 무엇인가요?"

saveOrUpdate()의 핵심은 email이 아닌 provider + providerId 조합으로 사용자를 식별한다는 점입니다. email을 주 식별자로 쓰면 두 가지 문제가 생깁니다. 같은 이메일로 Google과 Kakao에 각각 가입한 경우 동일인으로 잘못 처리될 수 있고, Kakao처럼 이메일 제공이 선택사항인 플랫폼에서는 email이 null일 수 있습니다.

시나리오 버튼을 클릭하여 신규 가입과 기존 로그인 흐름을 비교해보세요.

시나리오:

loadUser() 호출

OAuth2UserService.loadUser()

registrationId 판별

"google" / "kakao" / "naver"

OAuthAttributes.of()

플랫폼별 파서로 통합 객체 생성

DB 조회

provider+providerId

없음
있음

회원가입

toEntity() — Role.USER 부여

로그인 처리

update() — 이름·사진 최신화

userRepository.save()

신규/업데이트 모두 저장

DefaultOAuth2User 반환

GrantedAuthority 포함

JWT 발급 + Redirect

OAuth2SuccessHandler

공통 단계신규 사용자 경로기존 사용자 경로

코드 상세

노드에 마우스를 올리면
코드 예시가 표시됩니다


자주 발생하는 문제

실무에서 자주 마주치는 소셜 로그인 트러블슈팅 3가지


마무리

  • - Authorization Code Grant: Access Token이 브라우저에 노출되지 않는 안전한 흐름. State 파라미터로 CSRF 방어
  • - Google: CommonOAuth2Provider 자동 설정, Kakao·Naver: provider 섹션에 URI 직접 정의 필요
  • - OAuthAttributes: 플랫폼별 응답 구조 차이를 통합 처리, provider + providerId로 사용자 식별
  • - 소셜 로그인 성공 후 OAuth2SuccessHandler에서 JWT를 발급하여 Stateless 인증 구현
  • - 더 알아볼 주제: OAuth2 PKCE 확장, OpenID Connect(OIDC)와 OAuth2의 차이, 소셜 계정 연동(계정 머징), Refresh Token 자동 갱신 전략

이해도 체크 퀴즈

OAuth2 소셜 로그인 핵심 개념을 코드와 시나리오로 확인해보세요.

문제 1

Authorization Code 전달 경로

// "Google로 로그인" 버튼 클릭 후 OAuth2 흐름 진행 중
// Authorization Code는 어떤 경로로 전달되나요?

// 선택지를 읽고 올바른 흐름을 고르세요.
문제 2

소셜 로그인 사용자 식별 전략

// 홍길동 씨는 동일한 이메일(hong@example.com)로
// Google과 Kakao 계정을 모두 사용합니다.
// 소셜 로그인 구현 시 사용자 식별 전략으로 올바른 것은?
문제 3

Naver API 응답에서 이메일 추출

// Naver 로그인 후 다음 응답을 받았습니다.
// Map<String, Object> attributes 로 주입된 상태입니다.
{
  "resultcode": "00",
  "message": "success",
  "response": {
    "id": "abc123",
    "name": "홍길동",
    "email": "hong@naver.com"
  }
}

// ofNaver()에서 이메일을 올바르게 추출하는 코드는?
문제 4

Kakao client-authentication-method 설정 이유

# application.yml — Kakao 설정
kakao:
  client-id: ${KAKAO_CLIENT_ID}
  client-secret: ${KAKAO_CLIENT_SECRET}
  client-authentication-method: client_secret_post
  authorization-grant-type: authorization_code

// client-authentication-method: client_secret_post 를
// 설정하는 이유는?
문제 5

OAuth2 State 파라미터가 방어하는 공격

// OAuth2 인가 요청 시 state 파라미터에 난수를 생성하여 전송
GET /oauth2/authorize?
  response_type=code
  &client_id=xxx
  &redirect_uri=xxx
  &state=랜덤_난수_값  // ← 이 파라미터의 역할은?

// 콜백에서 동일한 state 값이 돌아오는지 검증합니다.
// 이 메커니즘이 방어하는 공격 유형은?

추가 학습 자료를 공유합니다.


의견을 들려주세요

서비스 개선에 큰 도움이 됩니다. 익명으로 자유롭게 남겨주세요.

0 / 1000