전체 흐름을 설명해드릴게요:
사용자가 "카카오로 로그인" 클릭
↓
카카오 로그인 페이지로 이동 (Spring Security가 자동 처리)
↓
사용자가 카카오에서 로그인 성공
↓
카카오가 우리 서버로 인가코드 전달 (Spring Security가 자동 처리)
↓
Spring Security가 인가코드로 카카오에서 액세스토큰 받아옴 (자동 처리)
↓
CustomOAuth2UserService.loadUser() 호출 ← 여기서부터 우리 코드
→ 카카오에서 유저 정보 가져옴 (이메일, 닉네임 등)
→ saveOrUpdate() 실행
→ 기존 회원이면 조회해서 반환
→ 신규 회원이면 DB에 저장 후 반환
↓
OAuth2AuthenticationSuccessHandler.onAuthenticationSuccess() 호출 ← 우리 코드
→ JWT accessToken, refreshToken 발급
→ 프론트로 redirect (토큰 전달)
↓
프론트가 토큰 받아서 저장 후 사용
핵심은 카카오/네이버/구글과의 통신은 Spring Security가 다 알아서 해줘요. 우리가 작성한 건:
CustomOAuth2UserService → 유저 정보로 DB 저장/조회OAuth2AuthenticationSuccessHandler → 우리 서비스용 JWT 발급카카오 OAuth2 토큰이랑 우리 서비스 JWT는 별개예요. 카카오 토큰은 카카오 API 쓸 때 필요한 거고, 우리 JWT는 우리 서비스 인증용이에요.

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} → 서버 주소
http://localhost:8080https://api.sofly.co.kr{registrationId} → provider 이름
kakao, naver, google그래서 실제로는: