OAuth 구현과 다형성 (Spring boot)

yeon·2021년 9월 1일
0

kakao와 naver 로그인을 통해 ITDA 서비스 로그인 기능을 구현한 내용을 정리해보았다.

코드의 중복을 줄이고, 나중에 다른 소셜 로그인이 추가된다고 하더라도 코드의 수정을 최소화 할 수 있게 구현 하는 것을 목표로 했다.

사용 기술: Spring boot, Spring Data JPA

일반 로그인이 JWT 기반으로 구현되어 있다. 어떠한 리소스 서버를 이용하던간에 리소스 서버에 접근해서 user 정보 받기 → (회원가입 하기) → 로그인해서 JWT 반환하기 과정을 거치게 되고, 해당 과정을 처리하는 SocialLoginService를 따로 생성했다.

네이버로 부터 access token을 받고 access token을 이용해서 user 정보를 담은 api를 얻어오는 작업은 해당되는 서비스 계층에서 이뤄진다. (NaverLoginService)

@Service
@RequiredArgsConstructor
public class SocialLoginService {

    private final TokenProvider tokenProvider;
    private final UserRepository userRepository;
    private final NaverLoginService naverLoginService;

    private TokenResponseDto login(String code) {

        UserInfo userInfo = naverLoginService.getUserInfoByCode(code);
        String email = userInfo.getEmail();

        if (isNewUser(email)) {
            signUp(userInfo);
        }

        User user = userRepository.findByEmail(email).orElseThrow(UserNotFoundException::new);
        String token = tokenProvider.createToken(user.getId());

        return new TokenResponseDto(token);
    }

    private boolean isNewUser(String email) {
        int count = userRepository.countByEmail(email);
        return count == 0;
    }

    private User signUp(UserInfo userInfo) {
        return userRepository.save(userInfo.toUser());
    }
}

위의 코드는 naver 소셜 로그인만 구현한 상태이다. 위의 코드는 neverLoginService에 의존하고 있다. kakao 등 다른 소셜 로그인을 추가했을 때 어떻게 유연하게 동작하도록 수정할 수 있을까를 고민했다.

객체 지향 특징의 다형성을 이용해보기로 했다.

위와 같이 OauthProvider 라는 상위 인터페이스를 생성하고 SocialLoginService의 login 메서드에서 OauthProvider에 의존하도록 구조를 변경하였다.

구조를 변경한 결과 코드는 아래와 같이 변경되었다.

@Service
@RequiredArgsConstructor
public class SocialLoginService {

    private final TokenProvider tokenProvider;
    private final UserRepository userRepository;
    private final NaverLoginService naverLoginService;
    private final KakaoLoginService kakaoLoginService;

    public TokenResponseDto naverLogin(String code) {
        return login(code, naverLoginService);
    }

    public TokenResponseDto kakaoLogin(String code) {
        return login(code, kakaoLoginService);
    }

    private TokenResponseDto login(String code, OauthProvider oauthProvider) {

        UserInfo userInfo = oauthProvider.requestUserInfo(code);
        String email = userInfo.getEmail();

        if (isNewUser(email)) {
            signUp(userInfo);
        }

        User user = userRepository.findByEmail(email).orElseThrow(UserNotFoundException::new);
        String token = tokenProvider.createToken(user.getId());

        return new TokenResponseDto(token);
    }

    private boolean isNewUser(String email) {
        int count = userRepository.countByEmail(email);
        return count == 0;
    }

    private User signUp(UserInfo userInfo) {
        return userRepository.save(userInfo.toUser());
    }
}

이제 어떤 소셜 로그인을 추가하더라도 login 메서드를 활용할 수 있다.
코드를 재사용할 수 있고 확장성이 높아졌다.


GitHub 소스코드 👉 https://github.com/Team-IT-DA/Backend/tree/master/src/main/java/com/itda/apiserver/oauth

0개의 댓글