본문으로 건너뛰기
Advertisement

12.4 OAuth2 + JWT 통합 로그인 구축 (Google, Kakao)

현대 웹/앱 서비스에서 자체 회원가입 외에 "Google로 로그인", "Kakao로 로그인" 같은 소셜 로그인은 필수 기능입니다. Spring Security의 OAuth2 Client를 활용하면 이 복잡한 인증 절차를 한 번에 캡슐화할 수 있습니다.

1. OAuth2 동작 흐름 (Authorization Code Grant)

  1. **클라이언트(브라우저)**가 "카카오 로그인" 버튼을 클릭해 카카오 인증 서버로 리다이렉트합니다.
  2. 유저가 카카오 아이디/비밀번호를 치고 권한 제공에 동의하면 카카오가 Authorization Code를 백엔드로 보내줍니다.
  3. 스프링 내부의 OAuth2LoginAuthenticationFilter가 이 코드를 가로채 카카오 서버 측과 통신하여 Access Token을 발급받습니다.
  4. 발급받은 Access Token을 이용해 카카오에서 유저 프로필(이메일, 이름 등) 정보를 가져옵니다 (DefaultOAuth2UserService 동작).
  5. 성공적으로 정보를 가져오면 OAuth2AuthenticationSuccessHandler가 동작합니다. 이곳에서 유저 정보를 DB에 저장(가입)시키고 우리 서버만의 자체 JWT 엑세스 토큰을 발급하여 브라우저로 응답합니다.

2. 의존성과 application.yml 설정

implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
spring:
security:
oauth2:
client:
registration:
google:
client-id: "구글콘솔에서_발급받은_ID"
client-secret: "비밀번호"
scope: profile, email
kakao:
client-id: "카카오_REST_API키"
client-secret: "비밀"
client-authentication-method: POST
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/{action}/oauth2/code/{registrationId}"
client-name: Kakao
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

3. CustomOAuth2UserService 작성

가져온 유저 정보를 가공하여 우리 DB 구조의 Entity와 매핑시키는 로직입니다.

@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

private final UserRepository userRepository;

@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// 부모 클래스가 카카오/구글에서 통용 프로필 정보를 가져오는 행위 시작
OAuth2User oAuth2User = super.loadUser(userRequest);

// 어떤 플랫폼인지 확인 (google, kakao 등)
String registrationId = userRequest.getClientRegistration().getRegistrationId();

// 유저 정보 파싱 (플랫폼마다 JSON 응답 구조가 다르기 때문에 Factory 패턴 권장)
OAuth2UserInfo userInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(registrationId, oAuth2User.getAttributes());

// DB에 해당 플랫폼 아이디가 있는지 조회하고, 없으면 신규 가입(save) 처리
User user = userRepository.findByEmail(userInfo.getEmail())
.orElseGet(() -> userRepository.save(User.createSocialUser(userInfo)));

// 시큐리티 세션에 담길 인증 객체 리턴 (PrincipalDetails 구현체)
return new PrincipalDetails(user, oAuth2User.getAttributes());
}
}

이후 SuccessHandler를 만들어서 강제로 리다이렉션(response.sendRedirect(...)) 시킬 때 URL의 파라미터나 헤더 공간에 우리가 생성한 JWT를 심어서 프론트엔드 측으로 전달해주는 것이 OAuth2 통합 아키텍처의 핵심입니다.

Advertisement