From b3e3cb421ed758719592d887f3848c29fdbd8790 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 12 Mar 2026 16:48:53 +0900 Subject: [PATCH 1/3] =?UTF-8?q?style:=20=EB=A1=9C=EA=B7=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/Infra/Common/Logger.swift | 4 +- DevLog/Infra/Service/AuthService.swift | 24 +++- .../Service/PushNotificationService.swift | 123 +++++++++++------- .../AppleAuthenticationService.swift | 73 ++++++----- .../GithubAuthenticationService.swift | 26 ++-- .../GoogleAuthenticationService.swift | 75 ++++++----- DevLog/Infra/Service/UserService.swift | 121 ++++++++--------- 7 files changed, 265 insertions(+), 181 deletions(-) diff --git a/DevLog/Infra/Common/Logger.swift b/DevLog/Infra/Common/Logger.swift index 6a440251..e4eb1717 100644 --- a/DevLog/Infra/Common/Logger.swift +++ b/DevLog/Infra/Common/Logger.swift @@ -55,7 +55,7 @@ final class Logger { ) { var fullMessage = message if let error = error { - fullMessage += " | Error: \(error.localizedDescription)" + fullMessage += " | Error: \(error)" } log(fullMessage, type: .error, file: file, function: function, line: line) } @@ -69,7 +69,7 @@ final class Logger { ) { var fullMessage = message if let error = error { - fullMessage += " | Error: \(error.localizedDescription)" + fullMessage += " | Error: \(error)" } log(fullMessage, type: .fault, file: file, function: function, line: line) } diff --git a/DevLog/Infra/Service/AuthService.swift b/DevLog/Infra/Service/AuthService.swift index 1368e8df..b2947dde 100644 --- a/DevLog/Infra/Service/AuthService.swift +++ b/DevLog/Infra/Service/AuthService.swift @@ -50,8 +50,13 @@ final class AuthService { func deleteFirestoreUserData() async throws { logger.info("Deleting Firestore user data") - let deleteFunction = functions.httpsCallable("deleteUserFirestoreData") - _ = try await deleteFunction.call() + do { + let deleteFunction = functions.httpsCallable("deleteUserFirestoreData") + _ = try await deleteFunction.call() + } catch { + logger.error("Failed to delete Firestore user data", error: error) + throw error + } } func deleteCurrentUser() async throws { @@ -62,7 +67,12 @@ final class AuthService { throw AuthError.notAuthenticated } - try await currentUser.delete() + do { + try await currentUser.delete() + } catch { + logger.error("Failed to delete FirebaseAuth current user", error: error) + throw error + } } func clearCurrentSession() async throws { @@ -73,6 +83,12 @@ final class AuthService { } catch { logger.error("Failed to delete FCM token while clearing session", error: error) } - try Auth.auth().signOut() + + do { + try Auth.auth().signOut() + } catch { + logger.error("Failed to sign out while clearing session", error: error) + throw error + } } } diff --git a/DevLog/Infra/Service/PushNotificationService.swift b/DevLog/Infra/Service/PushNotificationService.swift index 029e041a..00a0cb96 100644 --- a/DevLog/Infra/Service/PushNotificationService.swift +++ b/DevLog/Infra/Service/PushNotificationService.swift @@ -41,22 +41,30 @@ final class PushNotificationService { /// 푸시 알림 시간 설정 func fetchPushNotificationTime() async throws -> DateComponents { + logger.info("Fetching push notification time") + guard let uid = Auth.auth().currentUser?.uid else { + logger.error("User not authenticated") throw AuthError.notAuthenticated } - let settingsRef = store.document("users/\(uid)/userData/settings") - let doc = try await settingsRef.getDocument() + do { + let settingsRef = store.document("users/\(uid)/userData/settings") + let doc = try await settingsRef.getDocument() - guard let hour = doc.data()?["pushNotificationHour"] as? Int else { - throw FirestoreError.dataNotFound("pushNotificationHour") - } + guard let hour = doc.data()?["pushNotificationHour"] as? Int else { + throw FirestoreError.dataNotFound("pushNotificationHour") + } - guard let minute = doc.data()?["pushNotificationMinute"] as? Int else { - throw FirestoreError.dataNotFound("pushNotificationMinute") - } + guard let minute = doc.data()?["pushNotificationMinute"] as? Int else { + throw FirestoreError.dataNotFound("pushNotificationMinute") + } - return DateComponents(hour: hour, minute: minute) + return DateComponents(hour: hour, minute: minute) + } catch { + logger.error("Failed to fetch push notification time", error: error) + throw error + } } /// 푸시 알림 설정 업데이트 @@ -94,35 +102,40 @@ final class PushNotificationService { _ notificationQuery: PushNotificationQuery, cursor: PushNotificationCursorDTO? ) async throws -> PushNotificationPageResponse { - guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated } + do { + guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated } - var firestoreQuery = makeQuery(uid: uid, query: notificationQuery) + var firestoreQuery = makeQuery(uid: uid, query: notificationQuery) - if let cursor { - firestoreQuery = firestoreQuery.start(after: [ - Timestamp(date: cursor.receivedAt), - cursor.documentID - ]) - } + if let cursor { + firestoreQuery = firestoreQuery.start(after: [ + Timestamp(date: cursor.receivedAt), + cursor.documentID + ]) + } - let snapshot = try await firestoreQuery - .limit(to: notificationQuery.pageSize) - .getDocuments() + let snapshot = try await firestoreQuery + .limit(to: notificationQuery.pageSize) + .getDocuments() - let items = snapshot.documents.compactMap { makeResponse(from: $0) } + let items = snapshot.documents.compactMap { makeResponse(from: $0) } - let nextCursor: PushNotificationCursorDTO? = snapshot.documents.last.map { document in - guard let receivedAt = document.data()[Key.receivedAt.rawValue] as? Timestamp else { - return nil - } + let nextCursor: PushNotificationCursorDTO? = snapshot.documents.last.map { document in + guard let receivedAt = document.data()[Key.receivedAt.rawValue] as? Timestamp else { + return nil + } - return PushNotificationCursorDTO( - receivedAt: receivedAt.dateValue(), - documentID: document.documentID - ) - } ?? nil + return PushNotificationCursorDTO( + receivedAt: receivedAt.dateValue(), + documentID: document.documentID + ) + } ?? nil - return PushNotificationPageResponse(items: items, nextCursor: nextCursor) + return PushNotificationPageResponse(items: items, nextCursor: nextCursor) + } catch { + logger.error("Failed to request notifications", error: error) + throw error + } } func observeNotifications( @@ -160,37 +173,47 @@ final class PushNotificationService { /// 푸시 알림 기록 삭제 func deleteNotification(_ notificationID: String) async throws { - guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated } + do { + guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated } - let docRef = store.collection("users/\(uid)/notifications").document(notificationID) + let docRef = store.collection("users/\(uid)/notifications").document(notificationID) - try await docRef.delete() + try await docRef.delete() + } catch { + logger.error("Failed to delete notification", error: error) + throw error + } } /// 푸시 알림 읽음/안읽음 토글 func toggleNotificationRead(_ todoId: String) async throws { logger.info("Toggling notification read for todoId: \(todoId)") - guard let uid = Auth.auth().currentUser?.uid else { - logger.error("User not authenticated") - throw AuthError.notAuthenticated - } + do { + guard let uid = Auth.auth().currentUser?.uid else { + logger.error("User not authenticated") + throw AuthError.notAuthenticated + } - let collection = store.collection("users/\(uid)/notifications") - let snapshot = try await collection.whereField("todoId", isEqualTo: todoId).getDocuments() + let collection = store.collection("users/\(uid)/notifications") + let snapshot = try await collection.whereField("todoId", isEqualTo: todoId).getDocuments() - guard let document = snapshot.documents.first else { - logger.error("Notification not found for todoId: \(todoId)") - throw FirestoreError.dataNotFound("notification") - } + guard let document = snapshot.documents.first else { + logger.error("Notification not found for todoId: \(todoId)") + throw FirestoreError.dataNotFound("notification") + } - guard let currentValue = document.data()["isRead"] as? Bool else { - logger.error("isRead not found for notification: \(document.documentID)") - throw FirestoreError.dataNotFound("isRead") - } + guard let currentValue = document.data()["isRead"] as? Bool else { + logger.error("isRead not found for notification: \(document.documentID)") + throw FirestoreError.dataNotFound("isRead") + } - try await document.reference.updateData(["isRead": !currentValue]) - logger.info("Successfully toggled notification read") + try await document.reference.updateData(["isRead": !currentValue]) + logger.info("Successfully toggled notification read") + } catch { + logger.error("Failed to toggle notification read", error: error) + throw error + } } } diff --git a/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift index 19b9f5cd..d190c161 100644 --- a/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift @@ -89,51 +89,66 @@ final class AppleAuthenticationService: AuthenticationService { } func signOut(_ uid: String) async throws { - let infoRef = store.document("users/\(uid)/userData/tokens") - let doc = try await infoRef.getDocument() + do { + let infoRef = store.document("users/\(uid)/userData/tokens") + let doc = try await infoRef.getDocument() - if doc.exists { - try await infoRef.updateData(["fcmToken": FieldValue.delete()]) - } + if doc.exists { + try await infoRef.updateData(["fcmToken": FieldValue.delete()]) + } - try await messaging.deleteToken() + try await messaging.deleteToken() - try Auth.auth().signOut() + try Auth.auth().signOut() + } catch { + logger.error("Failed to sign out with Apple", error: error) + throw error + } } func deleteAuth(_ uid: String) async throws { - let token = try await refreshAppleAccessToken() + do { + let token = try await refreshAppleAccessToken() - try await revokeAppleAccessToken(token: token) + try await revokeAppleAccessToken(token: token) + } catch { + logger.error("Failed to delete Apple auth", error: error) + throw error + } } func link(uid: String, email: String) async throws { - let response = try await authenticateWithAppleAsync() + do { + let response = try await authenticateWithAppleAsync() - let nonce = response.nonce - let credential = response.credential - let authorizationCode = response.authorizationCode - let idTokenString = response.idTokenString + let nonce = response.nonce + let credential = response.credential + let authorizationCode = response.authorizationCode + let idTokenString = response.idTokenString - let refreshToken = try await requestAppleRefreshToken(uid: uid, authorizationCode: authorizationCode) + let refreshToken = try await requestAppleRefreshToken(uid: uid, authorizationCode: authorizationCode) - guard let appleEmail = credential.email else { - try await revokeAppleAccessToken(token: refreshToken) - throw EmailFetchError.emailNotFound - } + guard let appleEmail = credential.email else { + try await revokeAppleAccessToken(token: refreshToken) + throw EmailFetchError.emailNotFound + } - if appleEmail != email { - try await revokeAppleAccessToken(token: refreshToken) - throw EmailFetchError.emailMismatch - } + if appleEmail != email { + try await revokeAppleAccessToken(token: refreshToken) + throw EmailFetchError.emailMismatch + } - let appleCredential = OAuthProvider.credential( - providerID: providerID, - idToken: idTokenString, - rawNonce: nonce - ) + let appleCredential = OAuthProvider.credential( + providerID: providerID, + idToken: idTokenString, + rawNonce: nonce + ) - try await user?.link(with: appleCredential) + try await user?.link(with: appleCredential) + } catch { + logger.error("Failed to link Apple account", error: error) + throw error + } } func unlink(_ uid: String) async throws { diff --git a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift index 77265ac1..92c5ae4d 100644 --- a/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift @@ -68,20 +68,30 @@ final class GithubAuthenticationService: NSObject, AuthenticationService { } func signOut(_ uid: String) async throws { - let infoRef = store.document("users/\(uid)/userData/tokens") - let doc = try await infoRef.getDocument() + do { + let infoRef = store.document("users/\(uid)/userData/tokens") + let doc = try await infoRef.getDocument() - if doc.exists { - try await infoRef.updateData(["fcmToken": FieldValue.delete()]) - } + if doc.exists { + try await infoRef.updateData(["fcmToken": FieldValue.delete()]) + } - try await messaging.deleteToken() + try await messaging.deleteToken() - try Auth.auth().signOut() + try Auth.auth().signOut() + } catch { + logger.error("Failed to sign out with GitHub", error: error) + throw error + } } func deleteAuth(_ uid: String) async throws { - try await revokeAccessToken() + do { + try await revokeAccessToken() + } catch { + logger.error("Failed to delete GitHub auth", error: error) + throw error + } } func link(uid: String, email: String) async throws { diff --git a/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift b/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift index 4b9ff933..cf2cd7ae 100644 --- a/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift +++ b/DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift @@ -62,54 +62,69 @@ final class GoogleAuthenticationService: AuthenticationService { } func signOut(_ uid: String) async throws { - let infoRef = store.document("users/\(uid)/userData/tokens") - let doc = try await infoRef.getDocument() + do { + let infoRef = store.document("users/\(uid)/userData/tokens") + let doc = try await infoRef.getDocument() - if doc.exists { - try await infoRef.updateData(["fcmToken": FieldValue.delete()]) - } + if doc.exists { + try await infoRef.updateData(["fcmToken": FieldValue.delete()]) + } - GIDSignIn.sharedInstance.signOut() - try await GIDSignIn.sharedInstance.disconnect() + GIDSignIn.sharedInstance.signOut() + try await GIDSignIn.sharedInstance.disconnect() - try await messaging.deleteToken() + try await messaging.deleteToken() - try Auth.auth().signOut() + try Auth.auth().signOut() + } catch { + logger.error("Failed to sign out with Google", error: error) + throw error + } } func deleteAuth(_ uid: String) async throws { - GIDSignIn.sharedInstance.signOut() - try await GIDSignIn.sharedInstance.disconnect() + do { + GIDSignIn.sharedInstance.signOut() + try await GIDSignIn.sharedInstance.disconnect() + } catch { + logger.error("Failed to delete Google auth", error: error) + throw error + } } @MainActor func link(uid: String, email: String) async throws { - guard let topViewController = provider.topViewController() else { - throw UIError.notFoundTopViewController - } + do { + guard let topViewController = provider.topViewController() else { + throw UIError.notFoundTopViewController + } - if GIDSignIn.sharedInstance.hasPreviousSignIn() { - GIDSignIn.sharedInstance.signOut() - } + if GIDSignIn.sharedInstance.hasPreviousSignIn() { + GIDSignIn.sharedInstance.signOut() + } - let signIn = try await GIDSignIn.sharedInstance.signIn(withPresenting: topViewController) + let signIn = try await GIDSignIn.sharedInstance.signIn(withPresenting: topViewController) - guard let googleEmail = signIn.user.profile?.email else { - throw EmailFetchError.emailNotFound - } + guard let googleEmail = signIn.user.profile?.email else { + throw EmailFetchError.emailNotFound + } - if googleEmail != email { - throw EmailFetchError.emailMismatch - } + if googleEmail != email { + throw EmailFetchError.emailMismatch + } - guard let idToken = signIn.user.idToken?.tokenString else { - throw URLError(.badServerResponse) - } + guard let idToken = signIn.user.idToken?.tokenString else { + throw URLError(.badServerResponse) + } - let accessToken = signIn.user.accessToken.tokenString - let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken) + let accessToken = signIn.user.accessToken.tokenString + let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken) - try await user?.link(with: credential) + try await user?.link(with: credential) + } catch { + logger.error("Failed to link Google account", error: error) + throw error + } } func unlink(_ uid: String) async throws { diff --git a/DevLog/Infra/Service/UserService.swift b/DevLog/Infra/Service/UserService.swift index 68f05dcf..f9b21f2d 100644 --- a/DevLog/Infra/Service/UserService.swift +++ b/DevLog/Infra/Service/UserService.swift @@ -23,71 +23,76 @@ final class UserService { throw AuthError.notAuthenticated } - let userRef = store.document("users/\(user.uid)") - let infoRef = store.document("users/\(user.uid)/userData/info") - let tokensRef = store.document("users/\(user.uid)/userData/tokens") - let settingsRef = store.document("users/\(user.uid)/userData/settings") - - // 사용자 기본 정보 - var userField: [String: Any] = [ - "currentProvider": response.providerID - ] - - // 공급자 이슈로 인한 nil 방지 - if let email = user.email { - userField["email"] = email - } - - if let displayName = user.displayName, displayName != "" { - userField["name"] = displayName - } + do { + let userRef = store.document("users/\(user.uid)") + let infoRef = store.document("users/\(user.uid)/userData/info") + let tokensRef = store.document("users/\(user.uid)/userData/tokens") + let settingsRef = store.document("users/\(user.uid)/userData/settings") + + // 사용자 기본 정보 + var userField: [String: Any] = [ + "currentProvider": response.providerID + ] + + // 공급자 이슈로 인한 nil 방지 + if let email = user.email { + userField["email"] = email + } + + if let displayName = user.displayName, displayName != "" { + userField["name"] = displayName + } - // Apple은 최초 새 이름 설정 시에만 이름을 제공 - if response.providerID == "apple.com" && - user.displayName != nil && user.displayName != "" { - userField["appleName"] = user.displayName - } + // Apple은 최초 새 이름 설정 시에만 이름을 제공 + if response.providerID == "apple.com" && + user.displayName != nil && user.displayName != "" { + userField["appleName"] = user.displayName + } - let userDocument = try await userRef.getDocument() - if !userDocument.exists { - userField["statusMsg"] = "" - } + let userDocument = try await userRef.getDocument() + if !userDocument.exists { + userField["statusMsg"] = "" + } - var settingField = ["fcmToken": response.fcmToken] + var settingField = ["fcmToken": response.fcmToken] - // 깃헙 로그인 시 추가 정보 저장 - if response.providerID == "github.com", let accessToken = response.accessToken { - settingField["githubAccessToken"] = accessToken - } + // 깃헙 로그인 시 추가 정보 저장 + if response.providerID == "github.com", let accessToken = response.accessToken { + settingField["githubAccessToken"] = accessToken + } - // Reference to capture ~ in concurrently-executing code; Swift 6 lang mode의 경고 해결 - let userFieldSnapshot = userField - let settingFieldSnapshot = settingField - // ----------------------------------------------------- - - async let userUpdate: Void = userRef.setData( - ["updatedAt": FieldValue.serverTimestamp()], - merge: true - ) - async let infoUpdate: Void = infoRef.setData(userFieldSnapshot, merge: true) - async let tokensUpdate: Void = tokensRef.setData(settingFieldSnapshot, merge: true) - - let settingsDocument = try await settingsRef.getDocument() - var settingsField: [String: Any] = [ - "timeZone": TimeZone.autoupdatingCurrent.identifier - ] - if !settingsDocument.exists { - settingsField["allowPushNotification"] = true - settingsField["pushNotificationHour"] = 9 - settingsField["pushNotificationMinute"] = 0 - } + // Reference to capture ~ in concurrently-executing code; Swift 6 lang mode의 경고 해결 + let userFieldSnapshot = userField + let settingFieldSnapshot = settingField + // ----------------------------------------------------- + + async let userUpdate: Void = userRef.setData( + ["updatedAt": FieldValue.serverTimestamp()], + merge: true + ) + async let infoUpdate: Void = infoRef.setData(userFieldSnapshot, merge: true) + async let tokensUpdate: Void = tokensRef.setData(settingFieldSnapshot, merge: true) + + let settingsDocument = try await settingsRef.getDocument() + var settingsField: [String: Any] = [ + "timeZone": TimeZone.autoupdatingCurrent.identifier + ] + if !settingsDocument.exists { + settingsField["allowPushNotification"] = true + settingsField["pushNotificationHour"] = 9 + settingsField["pushNotificationMinute"] = 0 + } - let settingsFieldSnapshot = settingsField - async let settingsUpdate: Void = settingsRef.setData(settingsFieldSnapshot, merge: true) + let settingsFieldSnapshot = settingsField + async let settingsUpdate: Void = settingsRef.setData(settingsFieldSnapshot, merge: true) - _ = try await (userUpdate, infoUpdate, tokensUpdate, settingsUpdate) - - logger.info("Successfully upserted user: \(user.uid)") + _ = try await (userUpdate, infoUpdate, tokensUpdate, settingsUpdate) + + logger.info("Successfully upserted user: \(user.uid)") + } catch { + logger.error("Failed to upsert user", error: error) + throw error + } } func fetchUserProfile() async throws -> UserProfileResponse { From 98fdc56e2c3b1b1478ee8fd48aa1e451d83c2197 Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 12 Mar 2026 17:13:13 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B2=84=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=EC=97=90=EC=84=9C=20Auth=EA=B0=80=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=EB=90=98=EB=A9=B4=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20Firestore=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EA=B0=80=20=EC=A0=9C=EA=B1=B0=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthenticationRepositoryImpl.swift | 1 - DevLog/Infra/Service/AuthService.swift | 14 ------- Firebase/functions/src/index.ts | 4 +- Firebase/functions/src/user/delete.ts | 37 +++++++------------ 4 files changed, 16 insertions(+), 40 deletions(-) diff --git a/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift b/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift index ebb1b651..f2ab79e9 100644 --- a/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift +++ b/DevLog/Data/Repository/AuthenticationRepositoryImpl.swift @@ -80,7 +80,6 @@ final class AuthenticationRepositoryImpl: AuthenticationRepository { } } - try await authService.deleteFirestoreUserData() try await authService.deleteCurrentUser() try await authService.clearCurrentSession() } diff --git a/DevLog/Infra/Service/AuthService.swift b/DevLog/Infra/Service/AuthService.swift index b2947dde..488e448f 100644 --- a/DevLog/Infra/Service/AuthService.swift +++ b/DevLog/Infra/Service/AuthService.swift @@ -7,12 +7,10 @@ import FirebaseAuth import FirebaseFirestore -import FirebaseFunctions import FirebaseMessaging final class AuthService { private let store = Firestore.firestore() - private let functions = Functions.functions(region: "asia-northeast3") private let messaging = Messaging.messaging() private let logger = Logger(category: "AuthService") @@ -47,18 +45,6 @@ final class AuthService { } } - func deleteFirestoreUserData() async throws { - logger.info("Deleting Firestore user data") - - do { - let deleteFunction = functions.httpsCallable("deleteUserFirestoreData") - _ = try await deleteFunction.call() - } catch { - logger.error("Failed to delete Firestore user data", error: error) - throw error - } - } - func deleteCurrentUser() async throws { logger.info("Deleting FirebaseAuth current user") diff --git a/Firebase/functions/src/index.ts b/Firebase/functions/src/index.ts index ed58f05f..c4f884e8 100644 --- a/Firebase/functions/src/index.ts +++ b/Firebase/functions/src/index.ts @@ -21,7 +21,7 @@ import { // } from "./auth/google"; import { - deleteUserFirestoreData + cleanupDeletedUserFirestoreData } from "./user/delete"; import { @@ -65,7 +65,7 @@ export { // Google 인증 함수들 (나중에 구현되면 추가) export { - deleteUserFirestoreData + cleanupDeletedUserFirestoreData }; // FCM 관련 함수들 내보내기 diff --git a/Firebase/functions/src/user/delete.ts b/Firebase/functions/src/user/delete.ts index 80ee4d98..006085a4 100644 --- a/Firebase/functions/src/user/delete.ts +++ b/Firebase/functions/src/user/delete.ts @@ -1,33 +1,24 @@ -import { onCall, HttpsError } from "firebase-functions/v2/https"; +import * as functions from "firebase-functions/v1"; import * as admin from "firebase-admin"; import * as logger from "firebase-functions/logger"; -export const deleteUserFirestoreData = onCall({ - cors: true, - maxInstances: 10, - region: "asia-northeast3" - }, - async (request) => { - if (!request.auth?.uid) { - logger.error("deleteUserFirestoreData called without authenticated uid", { - auth: request.auth ?? null - }); - throw new HttpsError("unauthenticated", "로그인 필요"); - } - - const uid = request.auth.uid; +export const cleanupDeletedUserFirestoreData = functions + .region("asia-northeast3") + .auth + .user() + .onDelete(async (user) => { + const uid = user.uid; try { const userDocRef = admin.firestore().doc(`users/${uid}`); await admin.firestore().recursiveDelete(userDocRef); - - // Firestore의 recursiveDelete API 사용 (firebase-tools v9.12.0+) - // 실제로는 admin SDK엔 없고, 아래처럼 functions에서 사용할 수 있음 - // https://firebase.google.com/docs/firestore/solutions/delete-collections?hl=ko#cloud-functions - // @ts-ignore - return { success: true } - } catch (err: any) { - throw new HttpsError("internal", `삭제 중 오류: ${err.message || err}`); + logger.info("Deleted Firestore user data after Auth user deletion", { uid }); + } catch (error) { + logger.error("Failed to delete Firestore user data after Auth user deletion", { + uid, + error + }); + throw error; } } ); From ab33acb8450acc3ebc32ce43b4aba0c81ef1c9da Mon Sep 17 00:00:00 2001 From: opficdev Date: Thu, 12 Mar 2026 17:29:10 +0900 Subject: [PATCH 3/3] =?UTF-8?q?test:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=82=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastlane/Fastfile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 5ac92db6..9b78ed6c 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -73,12 +73,6 @@ platform :ios do build_number: latest_testflight_build_number + 1 ) - match( - api_key: api_key, - type: "development", - readonly: ENV["CI"] == "true" - ) - match( api_key: api_key, type: "appstore",