Skip to content

[feat] 로그인 기능 구현#86

Merged
soletree merged 18 commits intodevfrom
feature/#85/feat-login
Jun 26, 2025
Merged

[feat] 로그인 기능 구현#86
soletree merged 18 commits intodevfrom
feature/#85/feat-login

Conversation

@soletree
Copy link
Copy Markdown
Contributor

@soletree soletree commented May 14, 2025

🔖  Issue Number

#85

📙 작업 내역

구현 내용 및 작업 했던 내역을 적어주세요.

(중요) 프로비저닝 파일, Secrets.xcconfig 업데이트 필요

  • Sign In Apple capability를 추가하기 위해서 프로비저닝 파일을 교체해야 합니다. (apple developer에서 설정을 수정해도 기존에 파일로 다운로드한 프로비저닝에는 자동으로 반영되지 않더라구요)
  • Secrets.xcconfig에 GoogleSignIn에 필요한 clientID를 추가했습니다.
  • 노션의 환경 설정 파일 페이지에 파일을 업데이트 했습니다.

구글, 애플 로그인 구현

  • 구글 로그인에서 signIn 메서드에 MainActor를 사용했는데요. 현재 보고 있는 뷰에서 새로운 창을 띄우는 로직이 포함되어 있기 때문에 MainActor Context로 진입할 필요가 있었습니다. MainActor 설정을 하지 않으면 메인스레드 에러가 발생합니다.
// GoogleAuthManager 

@MainActor
    func signIn() async throws -> String {
        guard let rootViewController = UIApplication.shared.keyWindow?.rootViewController else {
            throw GoogleAuthError.viewHierarchyNotFound
        }
        do {
            let result = try await GIDSignIn.sharedInstance.signIn(
                withPresenting: rootViewController
        }
	// ,,, 중략 
}
  • 에러 예시

image

  • 애플 로그인 async - await 구현

애플 로그인은 기본적으로 delegate 패턴으로만 결과를 반환합니다. 프로젝트 내 사용의 편의를 위해 Swift Concurrency 형태로 래핑했습니다.

final class AppleAuthManager: NSObject, AppleAuthManageable {
/// ,,, 중략 
#if DEBUG
    private var continuation: CheckedContinuation<String, Error>?
#else
    private var continuation: UnsafeContinuation<String, Error>?
#endif 
 
 func signIn() async throws -> String {
        let request = provider.createRequest()
        authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController?.delegate = self
        authorizationController?.presentationContextProvider = self
        authorizationController?.performRequests()
#if DEBUG
        let idToken = try await withCheckedThrowingContinuation { [weak self] continuation in
            self?.continuation = continuation
        }
#else
        let idToken = try await withUnsafeThrowingContinuation { [weak self] continuation in
            self?.continuation = continuation
        }
#endif
        continuation = nil
        return idToken
    }
}

Supabase email, idToken 회원가입, 로그인 구현

  • SupabaseAuthRequest에 case가 추가되었습니다.
  • SupabaseAuthManager에 메서드가 추가되었습니다.

회원가입 이탈 대응

  • 이메일 인증 유효시간을 24시간에서 15분으로 변경했습니다.
  • Supabase cron을 설정해 30분마다 이메일 인증이 완료되지 않은 사용자를 지우도록 설정했습니다.

회원가입 분기처리

  • supabase 이메일 회원가입 (signup)은 기본적으로 Authentication에 등록된 유저라도 200번 코드를 반환합니다. 따라서 HTTP 상태코드가 아닌 다른 속성을 통해 이미 가입한 유저인지 확인해야 합니다. Supabase User의 email_verified 필드가 nil이면 이미 가입한 유저, false면 새로 가입하는 유저를 의미합니다.
/// redirectTo: 인증 메일을 클릭했을 때 리다이렉트되는 URL 주소를 입력합니다.
    /// 입력하지 않은 경우, supabase dashboard에 기입된 주소로 리다이렉트 됩니다.
    @discardableResult
    func signUp(
        parameter: SupabaseEmailAuthParameter,
        redirectTo: String? = nil
    ) async throws -> SupabaseUser {
        do {
            let response = try await networkProvider.request(
                with: SupabaseAuthRequest.signUpWithEmail(
                    parameter: parameter,
                    redirectTo: redirectTo
                )
            )
            let userResponse = try jsonDecoder.decode(
                SupabaseUserResponse.self,
                from: response.data
            )
            let user = SupabaseUser(from: userResponse)
            if user.emailVerified == nil { // 이미 Authentication에 등록된 사용자가 signUp 시도
                throw SupabaseAuthError.alreadExistEmail
            }
            return user
        } catch {
            throw SupabaseAuthError.signUpFailed
        }
    }

📝 PR 특이사항

PR을 볼 때 팀원에게 알려야 할 특이사항을 알려주세요.

  • 딥링크 및 VIPER 모듈 연결까지 구현하면 PR이 너무 커질 것 같아서 다음 PR로 잇겠습니다

👻 관련 개발일지

  • 혹시 자세한 개발일지가 궁금하신 분들은 아래 링크 참고해주세요~~

supabase 이메일 로그인 구현
supabase cron 설정
supabase idToken 로그인 구현
애플 로그인
google 로그인 구현

@soletree soletree self-assigned this May 14, 2025
@soletree soletree added the feature 🦖 New feature or request label May 14, 2025
@kelly-chui
Copy link
Copy Markdown
Contributor

코드 잘 읽었습니다. 구조가 잘 정리되어 있어서 읽기 편했어요.
프로젝트 내부 기존 코드에 콜백 방식을 그대로 이용하는 부분이 몇 있는데(특히 NI, MPC 관련 코드들) 나중에 개선 작업 할 때 지금처럼 Swift concurrency로 래핑하면 프로젝트 내부 코드 결을 더 깔끔하게 맞추는 걸 기대할 수 있을거 같숩니다.
덕분에 많이 배웠습니다! LGTM ~~👍👍👍

}

/// 네트워크로 받은 응답을 세션에 반영합니다.
private func storeSession(from response: SNMNetworkResponse) throws {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로직이 중복되니 signInAnonymously()도 이 메소드 쓰면 좋을 것 같아요


func signIn() async throws -> String {
// 중복 로그인 요청 방지 및 continuation misuse/crash 방지
guard continuation == nil else { throw AppleAuthError.signInFailed }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P4.
아래에 signInAlreadyInProgress도 정의되어 있어서 signInFailed로 하신 이유가 궁금합니다!

if let windowScene = UIApplication.shared.keyWindow?.windowScene {
ASPresentationAnchor(windowScene: windowScene)
} else {
ASPresentationAnchor()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 정말 개인적으로 한 고민인데요.
사실상 else 분기가 실행될 일이 없다고 생각하지만, 만에 하나 이런 경우엔 UI 리턴 vs. 크래시 어떤게 좋을까요? (크래시 정보 수집 vs. 크래시 안정성 유지? 인거 같아요)
windowScenenil이라는 건 둘 다 앱 정상 플로우는 기대하지 못하는 지점인데 뭐가 더 좋을지 잠깐 고민해봤어요. 🤔

Comment on lines +48 to +57
private func resumeOnce(with result: Result<String, Error>) {
guard let continuation else { return }
self.continuation = nil
switch result {
case .success(let idToken):
continuation.resume(returning: idToken)
case .failure(let error):
continuation.resume(throwing: error)
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍

@soletree soletree merged commit fa0b762 into dev Jun 26, 2025
1 check passed
@soletree soletree deleted the feature/#85/feat-login branch June 26, 2025 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 🦖 New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants