[JAVA] Spring Boot에서 OAuth2 소셜 로그인 구현하기 (kakao)

OAuth2는 사용자 인증을 제3자 인증 제공자(Google, Kakao, GitHub 등)에 위임하여 간편하게 로그인할 수 있는 방식입니다.
Spring Boot에서는 spring-security-oauth2-client를 활용하여 소셜 로그인을 쉽게 구현할 수 있습니다.

 

 


OAuth2 소셜 로그인 흐름

  1. 클라이언트가 소셜 로그인 버튼 클릭
  2. Spring Security가 OAuth2 인증 서버로 리디렉션
  3. 사용자가 소셜 로그인 서비스(Google, Kakao, GitHub)에 로그인
  4. 소셜 서비스에서 인증 코드를 발급하여 콜백으로 전달
  5. 서버가 토큰 발급 및 사용자 정보 가져오기
  6. 로그인 완료 후 원하는 페이지로 리다이렉트

모든 과정은 Spring Security의 필터 체인OAuth2UserService에서 처리됩니다.

 

 

의존성 추가

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
}

 

 

application.yml 설정 (Google 예시)

spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: [발급받은 ID]
            client-secret: [발급받은 Secret]
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            scope: profile, email
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/v2/auth
            token-uri: https://oauth2.googleapis.com/token
            user-info-uri: https://openidconnect.googleapis.com/v1/userinfo

Kakao, GitHub의 경우에도 registration + provider를 각각 지정해야 합니다.

Kakao 설정 예시

spring:
  security:
    oauth2:
      client:
        registration:
          kakao:
            client-id: [카카오 REST API 키]
            client-secret: [카카오 Client Secret]
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
            client-name: Kakao
            scope: profile_nickname, account_email
        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

 

 

로그인 후 사용자 정보 매핑

@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest request) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(request);

        String registrationId = request.getClientRegistration().getRegistrationId(); // google, kakao 등
        String userNameAttr = request.getClientRegistration()
                                     .getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName();

        Map<String, Object> attributes = oAuth2User.getAttributes();

        // 소셜 서비스마다 attribute 구조가 다르므로 별도 파싱 필요
        OAuthAttributes oauthAttributes = OAuthAttributes.of(registrationId, userNameAttr, attributes);

        // 회원가입 or 로그인 처리 (DB 저장 로직 포함 가능)
        return new DefaultOAuth2User(
                Collections.singleton(new SimpleGrantedAuthority("ROLE_USER")),
                oauthAttributes.getAttributes(),
                oauthAttributes.getNameAttributeKey()
        );
    }
}

 

 

Security 설정에서 커스텀 OAuth2UserService 연결

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomOAuth2UserService customOAuth2UserService;

    public SecurityConfig(CustomOAuth2UserService customOAuth2UserService) {
        this.customOAuth2UserService = customOAuth2UserService;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
          .csrf(csrf -> csrf.disable())
          .authorizeHttpRequests(auth -> auth
              .requestMatchers("/", "/login", "/css/**").permitAll()
              .anyRequest().authenticated())
          .oauth2Login(oauth2 -> oauth2
              .loginPage("/login")  // 커스텀 로그인 페이지 사용 시
              .userInfoEndpoint(userInfo -> userInfo
                  .userService(customOAuth2UserService)));

        return http.build();
    }
}

 

 

사용자 정보 파싱 클래스 예시

@Getter
public class OAuthAttributes {
    private Map<String, Object> attributes;
    private String nameAttributeKey;
    private String name;
    private String email;

    public static OAuthAttributes of(String registrationId, String userNameAttr, Map<String, Object> attributes) {
        if ("kakao".equals(registrationId)) {
            return ofKakao(userNameAttr, attributes);
        }
        // github 등 확장 가능
        return ofGoogle(userNameAttr, attributes);
    }

    private static OAuthAttributes ofGoogle(String userNameAttr, Map<String, Object> attributes) {
        return new OAuthAttributes(
            attributes,
            userNameAttr,
            (String) attributes.get("name"),
            (String) attributes.get("email")
        );
    }

    private static OAuthAttributes ofKakao(String userNameAttr, Map<String, Object> attributes) {
        Map<String, Object> kakaoAccount = (Map<String, Object>) attributes.get("kakao_account");
        Map<String, Object> profile = (Map<String, Object>) kakaoAccount.get("profile");

        return new OAuthAttributes(
            attributes,
            userNameAttr,
            (String) profile.get("nickname"),
            (String) kakaoAccount.get("email")
        );
    }

    public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email) {
        this.attributes = attributes;
        this.nameAttributeKey = nameAttributeKey;
        this.name = name;
        this.email = email;
    }
}

 

 

실무 팁 및 주의사항

  • 사용자 식별 키는 소셜 서비스마다 id 또는 sub 등 다름 → 반드시 파싱 분기 처리
  • Kakao는 이메일 제공이 필수가 아님 → 개발자센터 설정에서 동의 항목 확인
  • GitHub는 user:email scope 추가 필요
  • 회원가입 처리는 최초 로그인 시 DB 저장, 이후 로그인은 조회만

 


 

 

Spring Boot에서는 OAuth2를 통해 어렵지 않게 소셜 로그인 기능을 구현할 수 있습니다.
Google, Kakao, GitHub 등의 플랫폼에 연동해 사용자 인증을 간단히 처리하고, 자체 회원 시스템과 연동하면 보안성과 사용자 편의성을 고려하여 개발할 수 있습니다.