[SOPT / 앱잼] UseCase 를 적용한 소셜 로그인 개발 회고록 (2)

박준혁 - Niro·2024년 2월 11일
1

SOPT 33기

목록 보기
2/5
post-thumbnail

안녕하세요 Niro 🚗 입니다!

🔗 [SOPT / 앱잼] Adapter Pattern 적용한 소셜 로그인 개발 회고록 (1)

위의 첫번째 글처럼 Adapter 패턴 적용에 이어서

'한단계 높힌 추상화 작업을 위한 UseCase 를 적용한 개발 회고' 통해 '어떻게 ViewController 에서 실행 시켰는지' 를 적어볼까합니다.

🔗 TOASTER 다운로드하기
아직 다운받지 않으신 분이 있다면 위의 링크를 통해 사용해보시고 많은 피드백 부탁드립니다!


⬇️ 들어가기 앞서서

먼저 첫번째 글을 요약해보자면 Adapter 패턴을 사용해서 각 소셜 로그인 Adapter 를 만들게 되었습니다.

결과적으로 보면

  1. 다른 방식의 소셜 로그인이라도 Protocol 을 통해 동일한 인터페이스를 제공
  2. 클라이언트 코드와 비즈니스 로직 간의 결합도를 감소
  3. 다른 Adapter 를 추가하거나 수정이 유용한 구조

와 같은 장점들을 얻을 수 있었습니다.

효율적인 패턴을 왜 써야하는지 고민을 하고 구현을 했을 때는 정말 뿌듯했고 더 이상 건드릴 필요가 없겠구나 생각했지만....

막상 사용을 해보니 가장 아쉬웠던 부분은

소셜 로그인의 동작을 알고 있어야 한다는 점입니다.

Protocol 의 다형성을 통해 동일한 인터페이스를 공유했지만 해당 Adapter 내부에서 어떠한 메서드를 사용해야하는지, 어떤 데이터를 반환하는지 구체적인 내용을 알아야 합니다.

  • 각 소셜 로그인의 구조를 알지 못하더라도
  • 어떠한 메서드를 사용하는지 알지 못하더라도

다른 개발자들이 사용할 수 있게 한층 더 높은 추상화를 시켜 클라이언트가 구체적인 구현에 종속되지않게 구현을 하고자 많은 고민을 하였고

UseCase 를 통해 계층을 분리하고 한층 높은 추상화를 구현하고자 했습니다!


⌨️ 어떻게 적용했어?

UseCase 의 특징 중 하나로 특정 기능이나 사용 사례에 대한 책임을 갖고 있어야 하는 단일 책임 원칙 준수 를 해야합니다.

소셜 로그인에 대한 UseCase 를 설정할 때 Login 과 Logout 에 대한 기능을 단일 책임을 갖기 위해서 따로 파일을 나누어 주었습니다.


🛠️ UseCase 예시

  • LoginUseCase
struct LoginUseCase {
    let adapter: AuthenticationAdapterProtocol
    
    var adapterType: String {
        return adapter.adapterType
    }
    
    init(adapter: AuthenticationAdapterProtocol) {
        self.adapter = adapter
    }
    
    func login() async throws -> SocialLoginTokenModel {
        return try await adapter.login()
    }
}

  • LogoutUseCase
struct LogoutUseCase {
    let adapter: AuthenticationAdapterProtocol
    
    var adapterType: String {
        return adapter.adapterType
    }
    
    init(adapter: AuthenticationAdapterProtocol) {
        self.adapter = adapter
    }
    
    func logout() async throws -> Bool {
        return try await adapter.logout()
    }
}

각 UseCase 를 보면 Adapter 에서 자주 보였던 AuthenticationAdapterProtocol 을 사용했습니다.

adapter 라는 프로퍼티를 통해 kakao, apple Adapter 를 주입 받고 Protocol 을 통해 상호작용 할 수 있게 됩니다.

UseCase 의 login, logout 메서드를 실행 시키더라도 주입 받은 Adapter 를 통해 해당 Adapter 의 login, logout 메서드를 실행시킬 수 있죠!

결과적으로 구체적인 Adapter 에 대한 의존성을 낮출 수 있고 의존성 역전 원칙을 따르는 것으로 볼 수 있습니다.

코드의 유연성과 확장성을 향상 시킬 수 있었고 Adapter 가 추가되거나 변경을 하더라도 각 UseCase 에는 영향을 주지 않는 장점도 얻을 수 있게 됩니다!

의존성 역전 원칙이란?

Login & Logout UseCase 는 구체적인 Adapter 클래스에 직접 의존하는 것이 아닌 Protocol 을 통해 추상화된 인터페이스에 의존하게 됩니다. 즉, 어떤 구체적인 Adapter 가 사용되는지 알 필요가 없게 되고 어떤 종류의 Adapter 라도 사용할 수 있게 됩니다.


📍 UseCase 사용 위치

자, 한층 더 높은 추상화을 위해 UseCase 를 만들었으니 이제 사용을 해봐야겠죠?

  • LoginViewController
// Kakao
loginUseCase = LoginUseCase(adapter: KakaoAuthenticateAdapter())

// Apple
loginUseCase = LoginUseCase(adapter: AppleAuthenticateAdapter())

LoginVC 에서 각 소셜 로그인 button 의 action 메서드에 위와 같이 선언해주었습니다.

아주 간단하죠?

그 다음은 loginUseCase 프로퍼티를 통해 login 메서드를 실행해야 합니다!


  • LoginViewController
func attemptLogin() async throws -> SocialLoginTokenModel {
		guard let loginUseCase = self.loginUseCase else {
        throw LoginError.notSettingUsecase
    }
        
    do {
        let result = try await loginUseCase.login()
        return result
    } catch let error {
        print("Login Error:", error.localizedDescription)
        throw LoginError.failedReceiveToken
    }
}

각 Button Action 에서 비동기 처리를 위한 코드가 공통적으로 사용되어 위와 같이 따로 attemptLogin 메서드를 만들어 주었습니다.

Adapter 를 잘 선언했는지 guard let 으로 확인을 하고 do catch 구문에서 결과 또는 에러를 반환 받을 수 있게 작성하였습니다.

loginUseCase.login() 에서 login 메서드는 AuthenticationAdapterProtocol 보았던 메서드이며 우리는 어떤 Adpater 를 할당하더라도 똑같은 코드인 login 메서드만 호출하면 해당 Adapter 구현부 내의 login 메서드까지 실행이 되죠!


👀 굳이 UseCase 까지 써야만 했을까?

사실 굉장히 많은 고민을 했던 부분입니다.

Adapter 를 주입 받아 로그인, 로그아웃을 구현하고
User 정보를 갖고 있는 Social Login 을 담당하는 싱글톤 객체를 만들어 관리를 할까?

라는 생각도 해보았습니다.

싱글톤 객체를 만들면 전역에서 호출하기도 편하고 User 정보를 공유할 수 있는 장점이 있기 때문입니다.

하지만!

프로젝트 내부에서 User 정보를 다양하게 필요하지 않으며 각 VC 마다 API 를 통해 User 정보를 전달 받고 있어 이중으로 User 정보를 갖고 있을 필요가 없다!

라고 생각하여 싱글톤 객체로 구현하지 않았습니다...

사실 Adapter Pattern 만을 사용해도 전혀 문제는 없습니다!

다른 개발자가 로그인 & 인증 관련 작업을 해야할 때 Adapter Pattern 으로 구현되어 있다는 것을 알아야 하기 떄문에 한번 더 추상화를 하게 되면 세부 사항을 알지 못하더라도 다른 개발자들이 편하게 사용할 수 있을거라 생각했습니다.

글의 시작부터 여러 근거를 통해 UseCase 의 특징인 하나의 기능을 독립적으로 갖고 있고 User 의 사용패턴에 맞춰 구현할 수 있어 더욱 직관적으로 사용할 수 있다는 장점으로 UseCase 까지 구현하게 되었습니다.


📌 정리해보자면...

Adapter Pattern 과 UseCase 를 활용한 소셜 로그인을 구현했던 개발 경험을 회고록 2편에 걸쳐 작성했습니다.

글을 적으면서 다시 복기해 볼 수 있는 시간이었고 기억에 남을 수 있는 좋은 시간이였습니다.

마지막 3번째 편으로 로그인을 구현하면서 느꼈던 점을 적어볼까 합니다.

긴글 읽어주셔서 감사하고 많은 피드백과 질문 대환영입니다!
다음 편도 기대해주세요!

profile
📱iOS Developer, 🍎 Apple Developer Academy @ POSTECH 1st, 💻 DO SOPT 33th iOS Part

0개의 댓글