D-W-X-Clerker에서 진행중인 프로젝트 개발을 했다.

구글 소셜 로그인 삭제 기능(소셜회원 탈퇴 기능)을 구현하였다.

Oauth2 업체와의 로그인 연동(연결)을 해제하려면 공통적으로 access token을 요구한다. 이를 위해 DB에 access token을 저장하는 칼럼을 따로 추가해주었다. 내가 구상한 소셜 회원 탈퇴 절차는 다음과 같다.

1. (클라) 유저가 회원 탈퇴 버튼 CLICK
2. (서버) 해당 유저의 db에 저장된 auth access token을 통해 oauth2 업체 측에 회원탈퇴 요청
    a. access token이 validate 할 경우 : 3번으로
    b. refresh token이 validate 하지 않은 경우
        i. 유저에게 소셜 재로그인을 요구하고 access token 발급 받아 db에 저장
        ii. 메세지 등을 띄워서 회원탈퇴 버튼을 다시 누르도록 유도함 (1번으로)
3. (서버) oauth2 업체에게 회원탈퇴(revoke) 요청
4. (서버) revoke 완료 응답 수신
5. (클라) 유저에게 회원탈퇴가 정상적으로 완료되었음을 display
// SecurityConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorizeRequests ->
                authorizeRequests
                    .antMatchers("/").permitAll()
                    .anyRequest().authenticated()
            )
            .oauth2Login(oauth2Login ->
                oauth2Login
                    .defaultSuccessUrl("/home", true)
                    .permitAll()
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/")  // 로그아웃 후 리디렉션할 URL
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .deleteCookies("JSESSIONID")  // 세션 쿠키 삭제
            );

        return http.build();
    }
}
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void deleteUserAccount(OAuth2AuthenticationToken authentication) {
        OAuth2User oAuth2User = authentication.getPrincipal();
        String email = oAuth2User.getAttribute("email");

        // 데이터베이스에서 이메일로 사용자 찾기
        User user = userRepository.findByEmail(email);
        if (user != null) {
            // 사용자 삭제
            userRepository.delete(user);
        }
    }
}
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class AccountController {

    private final UserService userService;

    public AccountController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/deleteAccount")
    public String deleteAccount(OAuth2AuthenticationToken authentication) {
        // 사용자 계정 삭제 처리
        userService.deleteUserAccount(authentication);

        // 로그아웃 후 메인 페이지로 리디렉션
        return "redirect:/logout";
    }
}

Revoke Controller

소셜 회원 탈퇴 요청들을 처리해주는 컨트롤러이다. 헤더의 Authorization에서 IMAD에서 사용하고 있는 JWT의 access token을 파싱한다.

@RestController
@RequiredArgsConstructor
public class RevokeController {
    private final RevokeService revokeService;

    @DeleteMapping("/api/oauth2/revoke/google")
    public ApiResponse<?> revokeGoogleAccount(@RequestHeader("Authorization") String accessToken) {
        revokeService.deleteGoogleAccount(accessToken);
        return ApiResponse.createSuccessWithNoContent(ResponseCode.USER_DELETE_SUCCESS);
    }

나는 구글 로그인만 구현하였기에 naver, kakao, apple 등은 만들지 않았다.

✏️ 소감