구글 로그인 기능 추가

This commit is contained in:
Yu Sung
2026-01-27 13:56:13 +09:00
parent 95cae7d0bf
commit b522b50dee
12 changed files with 191 additions and 4 deletions

View File

@@ -132,7 +132,12 @@ struct LoginView: View {
Image("ic_login_kakao")
.onTapGesture {
hideKeyboard()
AppState.shared.setAppStep(step: .signUp)
}
Image("ic_login_google")
.onTapGesture {
hideKeyboard()
viewModel.loginWithGoogle()
}
Image("ic_login_apple")

View File

@@ -12,6 +12,7 @@ import AuthenticationServices
import CryptoKit
import Security
import UIKit
import GoogleSignIn
final class LoginViewModel: NSObject, ObservableObject {
private let appViewModel = AppViewModel()
@@ -68,6 +69,35 @@ final class LoginViewModel: NSObject, ObservableObject {
controller.presentationContextProvider = self
controller.performRequests()
}
func loginWithGoogle() {
guard let presentingViewController = presentingViewController() else {
self.errorMessage = I18n.Login.Google.openFailed
self.isShowPopup = true
return
}
GIDSignIn.sharedInstance.configuration = GIDConfiguration(clientID: GID_CLIENT_ID, serverClientID: GID_SERVER_CLIENT_ID)
GIDSignIn.sharedInstance.signIn(withPresenting: presentingViewController) { result, error in
DispatchQueue.main.async {
if let error = error {
ERROR_LOG(error.localizedDescription)
self.errorMessage = I18n.Login.Google.signInFailed
self.isShowPopup = true
return
}
guard let idToken = result?.user.idToken?.tokenString else {
self.errorMessage = I18n.Login.Google.tokenMissing
self.isShowPopup = true
return
}
self.loginWithGoogle(idToken: idToken)
}
}
}
private func loginWithApple(identityToken: String, nonce: String) {
let pushToken = UserDefaults.string(forKey: .pushToken)
@@ -95,6 +125,41 @@ final class LoginViewModel: NSObject, ObservableObject {
}
.store(in: &subscription)
}
private func loginWithGoogle(idToken: String) {
let pushToken = UserDefaults.string(forKey: .pushToken)
let marketingPid = UserDefaults.string(forKey: .marketingPid)
let request = SocialLoginRequest(
container: "ios",
pushToken: pushToken.isEmpty ? nil : pushToken,
marketingPid: marketingPid.isEmpty ? nil : marketingPid,
identityToken: nil,
nonce: nil
)
isLoading = true
repository.loginGoogle(request: request, idToken: idToken)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
self.isLoading = false
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { response in
self.handleLoginResponse(response)
}
.store(in: &subscription)
}
private func presentingViewController() -> UIViewController? {
return UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.flatMap { $0.windows }
.first { $0.isKeyWindow }?
.rootViewController
}
private func handleLoginResponse(_ response: Response) {
self.isLoading = false

View File

@@ -11,6 +11,7 @@ import Moya
enum UserApi {
case login(request: LoginRequest)
case loginApple(request: SocialLoginRequest)
case loginGoogle(request: SocialLoginRequest, idToken: String)
case signUp(request: SignUpRequest)
case findPassword(request: ForgotPasswordRequest)
case searchUser(nickname: String)
@@ -50,6 +51,9 @@ extension UserApi: TargetType {
case .loginApple:
return "/member/login/apple"
case .loginGoogle:
return "/member/login/google"
case .signUp:
return "/member/signup/v2"
@@ -124,7 +128,7 @@ extension UserApi: TargetType {
var method: Moya.Method {
switch self {
case .login, .loginApple, .signUp, .findPassword, .notification, .logout, .logoutAllDevice, .signOut, .creatorFollow, .creatorUnFollow, .memberBlock, .memberUnBlock,
case .login, .loginApple, .loginGoogle, .signUp, .findPassword, .notification, .logout, .logoutAllDevice, .signOut, .creatorFollow, .creatorUnFollow, .memberBlock, .memberUnBlock,
.profileImageUpdate:
return .post
@@ -143,6 +147,9 @@ extension UserApi: TargetType {
case .loginApple(let request):
return .requestJSONEncodable(request)
case .loginGoogle(let request, _):
return .requestJSONEncodable(request)
case .signUp(let request):
return .requestJSONEncodable(request)
@@ -208,6 +215,9 @@ extension UserApi: TargetType {
switch self {
case .login, .loginApple, .signUp, .findPassword:
return nil
case .loginGoogle(_, let idToken):
return ["Authorization": "Bearer \(idToken)"]
default:
return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"]

View File

@@ -20,6 +20,10 @@ final class UserRepository {
func loginApple(request: SocialLoginRequest) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.loginApple(request: request))
}
func loginGoogle(request: SocialLoginRequest, idToken: String) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.loginGoogle(request: request, idToken: idToken))
}
func signUp(request: SignUpRequest) -> AnyPublisher<Response, MoyaError> {
return api.requestPublisher(.signUp(request: request))