김경록의 앱 개발 여정

[Swift] 코디네이터 패턴과 JWT 기반 오토로그인 본문

Trouble Shooting

[Swift] 코디네이터 패턴과 JWT 기반 오토로그인

Kim Roks 2025. 1. 8. 19:18

STEP 1

토큰 관리 객체에 대한 아이디어

  1. 키체인을 따로 별도 관리해주는 객체를 정의
  2. 해당 객체는 CRUD를 포함하고 싱글톤 객체로서 외부에서 편하게 접근 가능하도록 설정
  3. isLoggedIn:Bool 을 계산속성으로 정의, 해당 속성은 getToken을 통해 값을 업데이트
  4. public var isLoggedIn: Bool { get { // 실질적 통신에 사용되는 엑세스 토큰만을 확인 return getToken(type: .access) != nil } }

오토로그인에 대한 아이디어

  1. 키체인 혹은 유저 디폴트에 JWT 토큰을 받아 저장
  2. 로그인이 필요한 작업의 경우 해당 토큰을 꺼내서 헤더에 포함(Alamofire RequestInterceptor 사용)
  3. 서버로부터 토큰이 만료되었다는 안내를 받으면 기존 저장중이던 토큰을 삭제하여 업데이트
    func retry(_ request: Request, for session: Session, dueTo error: any Error, completion: @escaping (RetryResult) -> Void) {
        ...
        let tokenManager = TokenKeyChainManager.shared
        tokenManager.deleteToken(type: .access)
        tokenManager.deleteToken(type: .refresh)
        ...
    }

STEP 2

사용한 코디네이터 프로토콜

public protocol Coordinator: AnyObject {
    var parentCoordinator: Coordinator? { get set }
    var childCoordinators: [Coordinator] { get set }
    var navigationController: UINavigationController { get set }

    func start()
    func didFinish()
}

앱의 전반에서 로그인을 요구하는 경우

  • SceneDelegate에서 (혹은 AppDelegate) 에서 토큰에 대한 유효성을 체크, 코디네이터를 통해 분기처리를 해주면 됨

일부 회원제인 경우

이번 포스팅의 주된 목적이자 겪었던 메인 트러블 슈팅 과정

로그인이 필요한 경우를 나누는 경우는 크게 두가지가 있다고 판단.

  1. 화면 전체가 회원이 아니면 접근이 불가능한 경우(ex: 마이페이지 접근)
  2. 화면엔 접근이 가능하지만, 일부 기능만을 이용할 수 없는 경우(ex: 댓글 작성)

모든 경우에 일일히

if 엑세스토큰 유효하지않음 {  
coordinator?.pushToLoginView  
} else {  
coordinator?.pushToMyInfoView  
}  

따위의 반복 작업으로 처리 가능한 부분임

하지만, 위와 같은 반복 작업시에 많은 단점 존재

  1. 단순히 반복 작업만으로도 충분히 피곤하고, 유지보수성을 낮춤
  2. 개선된 사용자 경험을 위해 Alert등으로 안내해줘야하는데,
    위에서 언급한 두가지 경우에서 Alert을 호출하는 위치가 일관적이지 못함(coordinator일수도, VC일수도, VM일수도.. )
  3. Alert코드 또한 반복되어야만 함
  4. 부모 자식 코디네이터의 관리가 어려워짐

개선 아이디어

  1. 로그인이 필요한 '동작' 이 포함된 모든 뷰가 가지게 될 (채택하게 될) 인증 전용 코디네이터를 선언
  2. protocol AuthenticationCoordinator: Coordinator { func checkLoginBeforeAction(onLoggedIn: @escaping () -> Void) func pushToLoginView() func presentLoginRequiredAlert() }
  3. 프로토콜의 기본구현을 통해 반복 작업을 최소화
func checkLoginBeforeAction(onLoggedIn: @escaping () -> Void) {
        let isLoggedIn = TokenKeyChainManager.shared.isLoggedIn

        if !isLoggedIn {
            presentLoginRequiredAlert()
        } else {
            onLoggedIn()
        }
    }

로그인 요청 알럿과 로그인뷰로 이동하는 메서드는 생략(알럿에서 이동하기 누르면 로그인 뷰 push)
클로저를 통해 로그인이 된 경우에만 사용처에서 원하는 동작을 설정하도록 작성

사용처 예시 코드

      // TestViewController.swift
      private func pushToOnlyUserView() {
          coordinator?.checkLoginBeforeAction(onLoggedIn: { [weak self] in
              self?.coordinator?.pushToOnlyUserView()
          })
      }

    // TestCoordinator.swift 
    func pushToOnlyUserView() {
    //코디네이터 부모 자식 설정 코드 독립적으로 설정 가능
    }

기대할 수 있는 이점

  1. 로그인 유도 Alert과 이동 코드 중복 감소
  2. 기존 매번 변하던 로그인 여부 확인 위치를 일괄적으로 유저 액션에 적용하여 작성가능
  3. 부모 자식 코디네이터의 설정을 직관적이게 설정 가능