서비스 초기 단계에서 인증 시스템을 구축할 때, OAuth2 소셜 로그인은 사용자의 진입 장벽을 낮추는 최고의 선택입니다. 여기에 **JWT(JSON Web Token)**를 더하면 세션(Session) 기반 인증의 한계를 넘어, 확장성 있는 무상태(Stateless) 인증 시스템을 만들 수 있습니다.
이번 글에서는 **Spring Security 6 (Lambda DSL)**와 OAuth2 Client를 연동하여 카카오 로그인을 구현하고, 최종적으로 우리 서비스 전용의 JWT를 발급하는 과정을 다룹니다.
JWT에 관한 내용은 아래 글을 참고해주세요.
1. 전체 아키텍처 및 흐름
사용자가 "카카오 로그인" 버튼을 눌렀을 때, 내부적으로 어떤 일이 일어나는지 알아보겠습니다. 이는 Kakao developers의 공식 문서에서 제공하는 OAuth2 인증 시퀀스 다이어그램을 기반으로 하였습니다.
- Client: 카카오 로그인 요청 (백엔드 특정 URI로 접근).
- Kakao Auth Server: 사용자 인증 후
Authorization Code를 백엔드로 전달. - Spring Boot: Code를 받아 Access Token을 요청하고, 이를 통해 **사용자 정보(UserInfo)**를 획득.
- Service Layer: 받아온 정보로 회원을 가입시키거나 정보를 업데이트 (
DB 저장). - SuccessHandler: 인증이 완료되면 **자체 JWT(Access/Refresh)**를 발급하여 Client로 전달.
2. 사전 준비 (설정 및 의존성)
2.1. build.gradle
Spring Boot 3.x 버전을 기준으로 필요한 의존성을 추가합니다.
2.2. application.yml
카카오 개발자 센터에서 발급받은 키와 Redirect URI를 설정합니다. 이 부분이 설정되어 있지 않으면 OAuth2 흐름 자체가 시작되지 않습니다.
3. 사용자 정보 표준화 (OAuth2UserInfo)
구글, 네이버, 카카오 등 다양한 공급자(Provider)는 각기 다른 JSON 응답 구조를 가집니다. 이를 하나의 인터페이스로 묶어 다형성을 활용합니다.
3.1. Interface 정의
3.2. Kakao 구현체
카카오는 kakao_account 내부에 프로필 정보가 중첩된 구조이므로, 아래와 같이 파싱이 필요합니다.
4. 핵심 로직
4.1. CustomOAuth2User (DTO)
OAuth2User를 상속받아 Spring Security가 인식할 수 있는 사용자 객체로 변환합니다.
4.2. CustomOAuth2UserService
DefaultOAuth2UserService를 상속받아, 카카오에서 받은 사용자 정보를 **우리 서비스의 DB에 저장(회원가입/갱신)**하는 로직을 담당합니다.
CustomOAuth2User는 OAuth2User를 구현한 DTO 클래스입니다. 내부적으로 우리 서비스의 User 엔티티를 필드로 가지고 있으면 Handler에서 꺼내 쓰기 편합니다.5. 토큰 발급: OAuth2SuccessHandler
인증이 성공하면 호출되는 핸들러입니다. 여기서 우리 서비스 전용 JWT를 생성하고 프론트엔드로 전달합니다.
6. SecurityConfig 설정
마지막으로 위에서 만든 컴포넌트들을 Spring Security 설정에 조립합니다. Spring Security 6.1 이상부터 권장되는 Lambda DSL 방식을 사용했습니다.
7. 테스트 및 마무리
이제 서버를 실행하고 브라우저에서 http://localhost:8080/oauth2/authorization/kakao로 접속해 봅니다.
로그인을 완료하면, 설정한 CustomOAuth2UserService가 DB에 유저를 저장하고, OAuth2SuccessHandler가 JWT를 생성하여 프론트엔드 URL로 리다이렉트 시켜주는 것을 확인할 수 있습니다.