티스토리 뷰

개인 프로젝트에 OAuth2를 적용해봤다

Spring Security를 이용한 로그인, 회원가입 과정을 대체하기 위해서는 아니고 학습 목적이다

학습 목적이라 해보고 싶은 것 다 하려고 google, facebook, github, naver, kakao 다 연결시켰다

SpringBoot 프로젝트에 OAuth2 적용하는 과정은 자세히 설명해준 글을 첨부한다

 

- 1편

 

 

Spring Boot OAuth2 Social Login with Google, Facebook, and Github - Part 1

In this article, You'll learn how to add social as well as email and password based login to your spring boot application using Spring Security and Spring Security's OAuth2 client. You'll build a full stack application with Spring Boot and React containing

www.callicoder.com

 

- 2편

 

Spring Boot OAuth2 Social Login with Google, Facebook, and Github - Part 2

Integrate social login with Facebook, Google, and Github in your spring boot application using Spring Security's OAuth2 functionalities. You'll also add email and password based login along with social login.

www.callicoder.com

 

 

위 블로그 글을 참고하여 내 프로젝트에 필요한 부분은 도입하고 필요하지 않거나 리팩토링이 가능한 부분은 고쳐서 사용했다

if 문으로 층층이 검사하는 코드에서 switch문을 사용하도록 변경했다

이 조차도 다형성을 이용하는 객체지향적 코드로 변경할 수 있을 것 같은데 이 외에도 할 게 많아서 조금 미뤄두기로 했다

- 블로그 하단에 수정버전이 있다

(좌) 블로그의 방식 (우) 고쳐서 적용한 방식

 

오른쪽 코드에서 method 옆에 Complexity is 5 Everything is cool! 이라고 적힌 것은 인텔리제이의 플러그인이다

CodeMetrics라는 플러그인으로 코드의 복잡도를 계산해서 알려준다

특히 if문, 중첩 if문에 점수가 크게 배정되므로 if문 덩어리에 때려 박는 코드를 지양할 수 있게 해 준다

if문을 싫어하는 것을 보면 복잡도의 기준은 아무래도 클린 코드의 영향을 받은 것 같다

요즘 읽고 있는 클린 코드에서도 if문, switch문을 객체지향적인 코드로 대체하라고 강조하고 있다

한번 작성하고 다시는 돌아보지 않을 코드라면 돌아가기만 하면 되겠지만

자신의 작품이기도 하고 나는 꾸준히 리팩토링을 해 볼 생각이라 가독성을 좋게 만들려 노력하고 있다

 

(좌) CodeMetrics (우) 복잡도를 상세히 알려줌

 

 

 

아래 사진은 OAuth2를 적용 후 테스트를 돌리다 만난 에러다 로그 트레이스는 상당히 길지만 핵심적인 부분만 올렸다

 

아래 코드에서 clientId가 null이 나오기 때문에 터진 에러이고

내가 작성한 application.yml을 다시 살펴보고 application-oauth2.yml도 살펴봤는데 이상은 없었다

또한 SpringBootApplication을 돌릴 때 에러가 터지지 않는 것을 생각해보면 설정 파일에는 문제가 없었고

다시 한번 문제를 살폈는데 문제는 test 폴더에 있었다

private ClientRegistration getRegistration(String client) {
        String clientId = env.getProperty(CLIENT_PROPERTY_KEY + client + ".client-id");
        String clientSecret = env.getProperty(CLIENT_PROPERTY_KEY + client + ".client-secret");

        switch (client) {
            case "kakao":
                return CustomOAuth2Provider.KAKAO.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
            case "naver":
                return CustomOAuth2Provider.NAVER.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
            case "google":
                return CommonOAuth2Provider.GOOGLE.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
            case "github":
                return CommonOAuth2Provider.GITHUB.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
            case "facebook":
                return CommonOAuth2Provider.FACEBOOK.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
            default:
                return null;
        }
}

 

 

나는 test를 애플리케이션과 독립적인 환경에서 돌리고 싶었기 때문에 test를 위한 application.yml을 따로 작성해두었고

폴더 구조는 좌측과 같고 application.yml의 대략적인 모습은 우측과 같다

 

@SpringBootTest는 test 폴더 안에 application.yml이 존재한다면 메인 application.yml보다 우선권을 가지고 읽어온다

테스트용 application.yml에 oauth2 설정에 해당하는 내용이 없기 때문에 에러가 터질 수밖에 없는 구조다

테스트 폴더의 application.yml은 github에 올려둔 상태고 나머지 *. yml 파일들은. gitignore에 등록해두었기 때문에

application 폴더에 가지고 있던 application-oauth2.yml을 복붙 하는 걸로 문제 해결되었다

OAuth2 적용을 설명해주는 블로그가 많기에 자료는 많고 나와 같은 문제를 만나는 분이 계시다면 해결에 참고하시라 올려본다

 

 


2022.01.08 수정 버전

(좌) 리팩토링 이전 (우) 리팩토링 이후

위쪽부터 살펴보자면 가독성을 위해 메서드 참조를 사용하지 않았는데 람다 사용에 조금 익숙해져서 리팩토링 때 수정했고

이 부분을 수정하는데 객체지향적인 사고 같은 거창한 건 필요 없고 Enum 의 valueOf 메서드만 알면 됐던 문제다

Enum.valueOf("문자열") 의 형태로 사용하며 문자열이 유효하다면 문자열에 맞는 타입으로 변환된다

예를 들어 google 로 넘어왔다면 우선 대문자로 변환 후 CustomOAuth2Provider.GOOGLE 로 변환되는 것이다

"google".toUpperCase() -> "GOOGLE"

CustomOAuth2Provider.valueOf("GOOGLE") -> CustomOAuth2Provider.GOOGLE

위 코드를 리팩토링하던 도중 switch문을 사용하는 비슷한 흐름의 코드 또한 리팩토링했다

 

 

리팩토링 이전 버전

public static OAuth2Attributes of(String registrationId, String userNameAttributeName, Map<String, Object> attributes) {
  switch (registrationId) {
    case "kakao":
      return ofKakao(userNameAttributeName, attributes);
    case "naver":
      return ofNaver(userNameAttributeName, attributes);
    case "google":
      return ofGoogle(userNameAttributeName, attributes);
    case "github":
      return ofGithub(userNameAttributeName, attributes);
    case "facebook":
      return ofFacebook(userNameAttributeName, attributes);
    default:
      return null;
  }
}

 

리팩토링 이후 버전

리팩토링 이후 버전

 

이건 솔직히 이러한 형태가 낫다고 할 수 있을지 모르겠다

switch 문 대신 Map 자료구조를 통해 BiFunction을 꺼내고 apply로 적용시키도록 하여 코드는 짧아졌으나

코드를 작성한 내가 아닌 남이 봤을 때 직관적이고, 쉽게 이해할 수 있는 것은 switch 문 일것 같다

근데 사실 저 정도의 코드는 어려운 축에도 끼지 못 할거 같고 내가 봤을 때 깔끔하기 때문에 리팩토링이 저렇게 완성됐다

댓글
링크
글 보관함
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Total
Today
Yesterday