본문 바로가기
Framework/Spring Boot

[Spring Boot] OAuth 2.0 소셜 로그인: 카카오, 네이버, 구글 연동의 정석

반응형

인터넷 쇼핑몰이나 새로운 서비스에 접속했을 때, 여러분은 긴 회원가입 폼을 성실하게 채우시나요, 아니면 "카카오로 1초 만에 시작하기" 버튼을 누르시나요? 바쁘고 귀찮은 것을 극도로 싫어하는 현대의 유저들에게 자사몰의 독자적인 회원가입(ID/PW)을 강요하는 것은 곧 '고객 이탈'을 의미합니다. 이러한 이탈을 막기 위해 구글, 카카오, 네이버 같은 거대 IT 기업들이 유저의 신원을 대신 확인해 주고 우리 서버에 "이 사람 진짜 맞아!"라고 보증을 서주는 기술이 등장했습니다. 바로 OAuth 2.0 (Open Authorization) 프로토콜입니다. 그리고 스프링 부트(Spring Boot) 진영에는 이 복잡한 프로토콜 통신을 아주 우아하게 캡슐화해 놓은 spring-boot-starter-oauth2-client라는 강력한 무기가 존재합니다.

이번 포스팅에서는 "외계어처럼 느껴지는 OAuth 2.0의 웅장한 통신 흐름!" 카카오, 네이버, 구글 소셜 로그인 연동 실무와 JWT 발급 연계까지 다뤄보겠습니다.

🤝 1. OAuth 2.0의 4가지 핵심 등장인물

코드를 짜기 전에 이 4명의 역할 극을 완벽히 이해해야 합니다.

  • Resource Owner (유저): 카카오톡을 쓰고 있는 일반 사용자입니다. 자기 정보의 진짜 주인입니다.
  • Client (우리 서버): 카카오에게 유저의 정보를 달라고 떼쓰는(?) 우리의 스프링 부트 애플리케이션입니다.
  • Authorization Server (카카오 인증 서버): 유저에게 로그인 화면을 띄워주고, ID/PW가 맞으면 '인가 코드(Authorization Code)''액세스 토큰(Access Token)'을 발급해 주는 문지기입니다.
  • Resource Server (카카오 자원 서버): 카카오의 진짜 데이터베이스입니다. 우리 서버가 액세스 토큰을 보여주면 유저의 이름, 프로필 사진, 이메일을 내어줍니다.

⚙️ 2. application.yml 세팅: 카카오/네이버 연동의 비밀

구글이나 페이스북은 글로벌 표준이라 스프링이 client-id만 넣으면 알아서 주소를 찾아갑니다. 하지만 카카오와 네이버는 국내 전용이라 우리가 직접 '인증 주소'와 '데이터 주소'를 yml에 세팅해 주어야 합니다.

spring:
  security:
    oauth2:
      client:
        registration:
          kakao: # 우리가 부를 이름
            client-id: REST_API_KEY_입력
            client-secret: SECRET_KEY_입력
            redirect-uri: "{baseUrl}/login/oauth2/code/kakao" # 카카오가 코드를 던져줄 우리 서버 주소
            authorization-grant-type: authorization_code
            client-authentication-method: POST
            client-name: Kakao
        provider:
          kakao: # 카카오 서버의 실제 URL 정보들
            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 # 카카오 응답 JSON의 PK 키 이름

🧑‍💻 3. DefaultOAuth2UserService 오버라이딩 (핵심 로직)

이제 스프링 시큐리티가 알아서 카카오 서버와 통신해서 유저의 프로필(이메일, 이름 등)을 훔쳐(?)옵니다. 훔쳐온 직후에 발동하는 메서드를 오버라이딩하여 "우리 DB에 없는 신규 유저면 강제로 가입(INSERT)시키고, 있던 유저면 정보 갱신(UPDATE)해라!"라는 로직을 짜줍니다.

@Service
@RequiredArgsConstructor
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    private final UserRepository userRepository;

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        // 1. 스프링이 알아서 카카오 서버에서 유저 정보를 가져왔음! 그 객체를 꺼냄
        OAuth2User oAuth2User = super.loadUser(userRequest);
        
        // 2. 이게 구글 로그인인지, 카카오 로그인인지 식별자 추출 (예: "kakao")
        String registrationId = userRequest.getClientRegistration().getRegistrationId();
        
        // 3. 카카오가 준 JSON 데이터에서 이메일과 닉네임 파싱 (Provider 별로 JSON 구조가 달라서 별도 DTO 처리 필요)
        OAuthAttributes attributes = OAuthAttributes.of(registrationId, oAuth2User.getAttributes());
        
        // 4. 우리 DB에 이메일로 검색해 봄. 없으면 가입시키고, 있으면 이름만 업데이트
        User user = saveOrUpdate(attributes);
        
        // 5. 최종적으로 스프링 시큐리티 세션에 담길 유저 객체를 포장해서 반환
        return new DefaultOAuth2User(
                Collections.singleton(new SimpleGrantedAuthority(user.getRoleKey())),
                attributes.getAttributes(),
                attributes.getNameAttributeKey());
    }
}

🎟️ 4. 로그인 성공 후 JWT 발급 (OAuth2AuthenticationSuccessHandler)

우리는 15단계에서 서버 세션을 죽이고 JWT를 쓰기로 했습니다. CustomOAuth2UserService가 무사히 종료되면, 로그인 성공 핸들러(SuccessHandler)가 발동됩니다. 여기서 우리 서버만의 JWT 토큰을 구워낸 뒤, 브라우저에게 토큰을 달아 URL 리다이렉트(Redirect)를 시켜버리면 완벽한 무상태 소셜 로그인이 완성됩니다.


🎯 5. 마무리 및 다음 단계

지금까지 회원가입의 이탈률을 획기적으로 낮춰주는 마법, OAuth 2.0 프로토콜의 통신 흐름과 Spring Security를 활용한 카카오/네이버 연동, 그리고 JWT 발급까지 이어지는 완벽한 실무 아키텍처에 대해 다루어 보았습니다. 이제 여러분의 서비스는 대기업 부럽지 않은 인증 시스템을 갖추었습니다.

사용자가 로그인을 하고 쇼핑몰에서 상품을 결제했습니다. 결제 로직은 "1. 유저 포인트 차감 -> 2. 재고 감소 -> 3. 결제 완료 데이터 저장"의 순서로 진행됩니다. 그런데 2번 재고를 감소시키는 중에 DB가 펑 하고 멈췄습니다! 유저의 포인트는 깎였는데, 재고는 안 깎이고 주문도 안 들어간 끔찍한 '데이터 불일치' 대참사가 발생한 것입니다. 이때 "작업이 하나라도 실패하면, 앞선 작업들도 모두 취소하고 처음으로 되돌려라(Rollback)!"라고 지시하는 데이터베이스의 절대 수호신이 필요합니다. 이어지는 17단계 포스팅에서는 "모두 성공하거나, 아예 아무 일도 없었던 것처럼 되돌리거나!" Spring Boot @Transactional의 완벽한 롤백 매커니즘과 전파(Propagation) 속성의 함정에 대해 아주 뼈 때리게 파헤쳐 보겠습니다!

반응형