전체 흐름을 설명해드릴게요:

사용자가 "카카오로 로그인" 클릭
        ↓
카카오 로그인 페이지로 이동 (Spring Security가 자동 처리)
        ↓
사용자가 카카오에서 로그인 성공
        ↓
카카오가 우리 서버로 인가코드 전달 (Spring Security가 자동 처리)
        ↓
Spring Security가 인가코드로 카카오에서 액세스토큰 받아옴 (자동 처리)
        ↓
CustomOAuth2UserService.loadUser() 호출 ← 여기서부터 우리 코드
  → 카카오에서 유저 정보 가져옴 (이메일, 닉네임 등)
  → saveOrUpdate() 실행
    → 기존 회원이면 조회해서 반환
    → 신규 회원이면 DB에 저장 후 반환
        ↓
OAuth2AuthenticationSuccessHandler.onAuthenticationSuccess() 호출 ← 우리 코드
  → JWT accessToken, refreshToken 발급
  → 프론트로 redirect (토큰 전달)
        ↓
프론트가 토큰 받아서 저장 후 사용

핵심은 카카오/네이버/구글과의 통신은 Spring Security가 다 알아서 해줘요. 우리가 작성한 건:

  1. CustomOAuth2UserService → 유저 정보로 DB 저장/조회
  2. OAuth2AuthenticationSuccessHandler → 우리 서비스용 JWT 발급

카카오 OAuth2 토큰이랑 우리 서비스 JWT는 별개예요. 카카오 토큰은 카카오 API 쓸 때 필요한 거고, 우리 JWT는 우리 서비스 인증용이에요.

Screenshot 2026-04-11 at 6.17.38 PM.png

  security:
    oauth2:
      client:
        registration:
          google:
            client-id: ${GOOGLE_CLIENT_ID}
            client-secret: ${GOOGLE_CLIENT_SECRET}
            scope:
              - email
              - profile
          kakao:
            client-id: ${KAKAO_CLIENT_ID}
            client-secret: ${KAKAO_CLIENT_SECRET}
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            authorization-grant-type: authorization_code
            client-authentication-method: client_secret_post
            client-name: Kakao
            scope:
              - account_email
              - profile_nickname
          naver:
            client-id: ${NAVER_CLIENT_ID}
            client-secret: ${NAVER_CLIENT_SECRET}
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            authorization-grant-type: authorization_code
            client-name: Naver
            scope:
              - name
              - email
              - profile_image
        provider:
          kakao:
            authorization-uri: <https://kauth.kakao.com/oauth/authorize>
            token-uri: <https://kauth.kakao.com/oauth/token>
            user-info-uri: <https://kapi.kakao.com/v2/user/me>
            user-name-attribute: id
          naver:
            authorization-uri: <https://nid.naver.com/oauth2.0/authorize>
            token-uri: <https://nid.naver.com/oauth2.0/token>
            user-info-uri: <https://openapi.naver.com/v1/nid/me>
            user-name-attribute: response


{baseUrl}{registrationId}

이건 Spring Security가 런타임에 자동으로 채워주는 변수예요:

{baseUrl} → 서버 주소

{registrationId} → provider 이름

그래서 실제로는: