From bef3196df29ef7e11b56b9aa3dc3043f1bffdbc8 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:15:53 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[Chore]=20UserDefaultsManager=20->=20Util?= =?UTF-8?q?s=EB=A1=9C=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EB=B3=80=EA=B2=BD=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 15 ++++++------ .../Global/Protocols/Serviceable.swift | 4 ++-- .../ACON-iOS/Global/Service/AuthManager.swift | 24 +++++++++---------- .../Global/Utils/Enums/HeaderType.swift | 4 ++-- .../UserDefaultsUtils.swift} | 8 +++++-- .../LocalVerificationViewController.swift | 4 ++-- .../VerificationReminderViewController.swift | 2 +- .../LocalVerificationViewModel.swift | 2 +- .../Login/ViewModel/LoginViewModel.swift | 9 +++---- .../View/PreferenceViewController.swift | 2 +- .../ViewModel/PreferenceViewModel.swift | 2 +- .../View/ProfileSettingViewController.swift | 2 +- .../Profile/ViewModel/SettingViewModel.swift | 4 ++-- .../Splash/View/SplashViewController.swift | 4 ++-- .../View/SpotListViewController.swift | 2 +- .../TutorialContainerViewController.swift | 2 +- ...WithdrawalConfirmationViewController.swift | 2 +- .../ViewModel/WithdrawalViewModel.swift | 4 ++-- 18 files changed, 51 insertions(+), 45 deletions(-) rename ACON-iOS/ACON-iOS/Global/{Service/UserDefaultsManager.swift => Utils/UserDefaultsUtils.swift} (91%) diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index d0558be8..564c2ec7 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -47,7 +47,7 @@ 15304FC22E33C57A00EFCDEF /* CafeFeatureSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15304FC12E33C57A00EFCDEF /* CafeFeatureSelectionViewController.swift */; }; 15304FC82E33EFA600EFCDEF /* SpotUploadSizeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15304FC72E33EFA600EFCDEF /* SpotUploadSizeType.swift */; }; 1530CC792DDFC0D100EB4AEC /* SpotNoImageContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1530CC782DDFC0D100EB4AEC /* SpotNoImageContentView.swift */; }; - 153B78252E74090700B772F9 /* UserDefaultsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153B78242E74090700B772F9 /* UserDefaultsManager.swift */; }; + 153B78252E74090700B772F9 /* UserDefaultsUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153B78242E74090700B772F9 /* UserDefaultsUtils.swift */; }; 15432E7D2E41ECCA0005DB90 /* PostSpotUploadRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15432E7C2E41ECCA0005DB90 /* PostSpotUploadRequest.swift */; }; 15432E802E41ECDD0005DB90 /* SpotUploadTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15432E7F2E41ECDD0005DB90 /* SpotUploadTargetType.swift */; }; 15432E822E41ECF40005DB90 /* SpotUploadService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15432E812E41ECF40005DB90 /* SpotUploadService.swift */; }; @@ -374,7 +374,7 @@ 15304FC12E33C57A00EFCDEF /* CafeFeatureSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CafeFeatureSelectionViewController.swift; sourceTree = ""; }; 15304FC72E33EFA600EFCDEF /* SpotUploadSizeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotUploadSizeType.swift; sourceTree = ""; }; 1530CC782DDFC0D100EB4AEC /* SpotNoImageContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotNoImageContentView.swift; sourceTree = ""; }; - 153B78242E74090700B772F9 /* UserDefaultsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsManager.swift; sourceTree = ""; }; + 153B78242E74090700B772F9 /* UserDefaultsUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsUtils.swift; sourceTree = ""; }; 15432E7C2E41ECCA0005DB90 /* PostSpotUploadRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostSpotUploadRequest.swift; sourceTree = ""; }; 15432E7F2E41ECDD0005DB90 /* SpotUploadTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotUploadTargetType.swift; sourceTree = ""; }; 15432E812E41ECF40005DB90 /* SpotUploadService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpotUploadService.swift; sourceTree = ""; }; @@ -1340,7 +1340,6 @@ 741429662D5D550400B69528 /* AppVersionManager.swift */, 7462D0242D4262EF00580464 /* AuthManager.swift */, 1515CE0C2E00823B00A559A2 /* DeepLinkManager.swift */, - 153B78242E74090700B772F9 /* UserDefaultsManager.swift */, 150B4B292E7EE4BB00C34C76 /* PhotoManager.swift */, ); path = Service; @@ -1597,15 +1596,17 @@ 748D6F792D2BCCEF007690B4 /* Utils */ = { isa = PBXGroup; children = ( - 74C782182E01504B0053AA86 /* NavigatonUtils.swift */, - 152F8D162DF252560064022B /* MapRedirect */, + 74054ECD2D32549800D1CDE4 /* ACLocationManager.swift */, 746F5BF12DE00CE90081569B /* GlassBorderAttributes.swift */, 74E38D332D69B468009449D4 /* LocationUtils.swift */, - 74054ECD2D32549800D1CDE4 /* ACLocationManager.swift */, + 152F8D162DF252560064022B /* MapRedirect */, 74054EC92D32533800D1CDE4 /* MultitaskDelegate.swift */, + 74C782182E01504B0053AA86 /* NavigatonUtils.swift */, + 748D6F7F2D2BCD8F007690B4 /* ObservablePattern.swift */, 748D6F812D2BCDB0007690B4 /* ScreenUtils.swift */, 155BC2912DF09EB200E1744E /* ShadowColorCache.swift */, 748D6F7F2D2BCD8F007690B4 /* ObservablePattern.swift */, + 153B78242E74090700B772F9 /* UserDefaultsUtils.swift */, 748D6F7B2D2BCCFF007690B4 /* Enums */, ); path = Utils; @@ -2332,7 +2333,7 @@ 740EE8282E059875007A5DCF /* GetAppUpdateRequest.swift in Sources */, 15304FC02E33BAA900EFCDEF /* ValueRatingViewController.swift in Sources */, 15F45F842D6664C900BD6B9C /* LoginLockOverlayView.swift in Sources */, - 153B78252E74090700B772F9 /* UserDefaultsManager.swift in Sources */, + 153B78252E74090700B772F9 /* UserDefaultsUtils.swift in Sources */, 746261692D3EA33300A4E84F /* PostReviewRequest.swift in Sources */, 15CD257E2D3FF4F200320006 /* SpotListTargetType.swift in Sources */, 15A246142DE79D7E00469272 /* NoMatchingSpotType.swift in Sources */, diff --git a/ACON-iOS/ACON-iOS/Global/Protocols/Serviceable.swift b/ACON-iOS/ACON-iOS/Global/Protocols/Serviceable.swift index 531f68d4..62f2f396 100644 --- a/ACON-iOS/ACON-iOS/Global/Protocols/Serviceable.swift +++ b/ACON-iOS/ACON-iOS/Global/Protocols/Serviceable.swift @@ -23,13 +23,13 @@ extension Serviceable { print("❄️ 기존 서버통신 다시 성공") retryAction() } else { - UserDefaultsManager.removeTokens() + UserDefaultsUtils.removeTokens() NavigationUtils.navigateToSplash() } } } catch { DispatchQueue.main.async { - UserDefaultsManager.removeTokens() + UserDefaultsUtils.removeTokens() NavigationUtils.navigateToSplash() } } diff --git a/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift b/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift index 50c2912a..36ddea01 100644 --- a/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift +++ b/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift @@ -16,44 +16,44 @@ final class AuthManager { private let refreshInterval: TimeInterval = 3 * 60 * 60 var hasToken: Bool { - return UserDefaultsManager.get(String.self, forKey: .accessToken) != nil + return UserDefaultsUtils.get(String.self, forKey: .accessToken) != nil } var hasVerifiedArea: Bool { - return UserDefaultsManager.get(Bool.self, forKey: .hasVerifiedArea) ?? false + return UserDefaultsUtils.get(Bool.self, forKey: .hasVerifiedArea) ?? false } var hasPreference: Bool { - return UserDefaultsManager.get(Bool.self, forKey: .hasPreference) ?? false + return UserDefaultsUtils.get(Bool.self, forKey: .hasPreference) ?? false } var hasSeenTutorial: Bool { - return UserDefaultsManager.get(Bool.self, forKey: .hasSeenTutorial) ?? false + return UserDefaultsUtils.get(Bool.self, forKey: .hasSeenTutorial) ?? false } var hasSeenLocalVerification: Bool { - return UserDefaultsManager.get(Bool.self, forKey: .hasSeenLocalVerification) ?? false + return UserDefaultsUtils.get(Bool.self, forKey: .hasSeenLocalVerification) ?? false } var hasSeenPreference: Bool { - return UserDefaultsManager.get(Bool.self, forKey: .hasSeenPreference) ?? false + return UserDefaultsUtils.get(Bool.self, forKey: .hasSeenPreference) ?? false } func handleTokenRefresh() async throws -> Bool { - let refreshToken = UserDefaultsManager.get(String.self, forKey: .refreshToken) ?? "" + let refreshToken = UserDefaultsUtils.get(String.self, forKey: .refreshToken) ?? "" return try await withCheckedThrowingContinuation { continuation in ACService.shared.authService.postReissue(PostReissueRequest(refreshToken: refreshToken)) { response in switch response { case .success(let data): print("❄️ token refreshed success") - UserDefaultsManager.set(data.accessToken, forKey: .accessToken) - UserDefaultsManager.set(data.refreshToken, forKey: .refreshToken) + UserDefaultsUtils.set(data.accessToken, forKey: .accessToken) + UserDefaultsUtils.set(data.refreshToken, forKey: .refreshToken) AuthManager.shared.updateLastTokenRefreshDate() continuation.resume(returning: true) case .requestErr(let error): if error.code == 40088 { print("❄️ remove token") - UserDefaultsManager.removeTokens() + UserDefaultsUtils.removeTokens() continuation.resume(returning: false) } default: @@ -65,12 +65,12 @@ final class AuthManager { func updateLastTokenRefreshDate() { let now = Date() - UserDefaultsManager.set(now, forKey: .lastTokenRefreshDate) + UserDefaultsUtils.set(now, forKey: .lastTokenRefreshDate) } // NOTE: access token이 만료되었으면 true func needsTokenRefresh() -> Bool { - guard let lastRefresh = UserDefaultsManager.get(Date.self, forKey: .lastTokenRefreshDate) else { + guard let lastRefresh = UserDefaultsUtils.get(Date.self, forKey: .lastTokenRefreshDate) else { return true } let elapsed = Date().timeIntervalSince(lastRefresh) diff --git a/ACON-iOS/ACON-iOS/Global/Utils/Enums/HeaderType.swift b/ACON-iOS/ACON-iOS/Global/Utils/Enums/HeaderType.swift index eef2e60b..a5be1c3e 100644 --- a/ACON-iOS/ACON-iOS/Global/Utils/Enums/HeaderType.swift +++ b/ACON-iOS/ACON-iOS/Global/Utils/Enums/HeaderType.swift @@ -18,7 +18,7 @@ enum HeaderType { } static func headerWithToken() -> [String: String] { - if let token = UserDefaultsManager.get(String.self, forKey: .accessToken) { + if let token = UserDefaultsUtils.get(String.self, forKey: .accessToken) { return ["Content-Type" : "application/json", "Authorization" : "Bearer " + token] } else { return basicHeader @@ -26,7 +26,7 @@ enum HeaderType { } static func tokenOnly() -> [String:String] { - if let token = UserDefaultsManager.get(String.self, forKey: .accessToken) { + if let token = UserDefaultsUtils.get(String.self, forKey: .accessToken) { return ["Authorization" : "Bearer " + token] } else { return noHeader diff --git a/ACON-iOS/ACON-iOS/Global/Service/UserDefaultsManager.swift b/ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift similarity index 91% rename from ACON-iOS/ACON-iOS/Global/Service/UserDefaultsManager.swift rename to ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift index 442ff7c7..f1553f36 100644 --- a/ACON-iOS/ACON-iOS/Global/Service/UserDefaultsManager.swift +++ b/ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift @@ -1,5 +1,5 @@ // -// UserDefaultsManager.swift +// UserDefaultsUtils.swift // ACON-iOS // // Created by 김유림 on 9/12/25. @@ -7,7 +7,7 @@ import Foundation -struct UserDefaultsManager { +struct UserDefaultsUtils { enum Keys: String, CaseIterable { case accessToken @@ -22,6 +22,8 @@ struct UserDefaultsManager { case lastTokenRefreshDate case lastLocalVerificationAlertDate + + case tokenLogs } @@ -60,12 +62,14 @@ struct UserDefaultsManager { remove(forKey: key) } + TokenLogger.shared.log(.cleared) } static func removeTokens() { [Keys.accessToken, Keys.refreshToken].forEach { remove(forKey: $0) } + TokenLogger.shared.log(.cleared) } } diff --git a/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/LocalVerificationViewController.swift b/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/LocalVerificationViewController.swift index 2c1f3e30..36c75ee0 100644 --- a/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/LocalVerificationViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/LocalVerificationViewController.swift @@ -41,7 +41,7 @@ class LocalVerificationViewController: BaseNavViewController { if self.localVerificationViewModel.flowType == .onboarding { self.setSkipButton() { let now = Date() - UserDefaultsManager.set(now, forKey: .lastLocalVerificationAlertDate) + UserDefaultsUtils.set(now, forKey: .lastLocalVerificationAlertDate) NavigationUtils.naviateToOnboardingPreference() } @@ -58,7 +58,7 @@ class LocalVerificationViewController: BaseNavViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - UserDefaultsManager.set(true, forKey: .hasSeenLocalVerification) + UserDefaultsUtils.set(true, forKey: .hasSeenLocalVerification) } diff --git a/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/VerificationReminderViewController.swift b/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/VerificationReminderViewController.swift index b83625c7..4202f0c9 100644 --- a/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/VerificationReminderViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/LocalVerification/View/VerificationReminderViewController.swift @@ -38,7 +38,7 @@ private extension VerificationReminderViewController { @objc func cancelButtonTapped() { let now = Date() - UserDefaultsManager.set(now, forKey: .lastLocalVerificationAlertDate) + UserDefaultsUtils.set(now, forKey: .lastLocalVerificationAlertDate) dismiss(animated: true) } diff --git a/ACON-iOS/ACON-iOS/Presentation/LocalVerification/ViewModel/LocalVerificationViewModel.swift b/ACON-iOS/ACON-iOS/Presentation/LocalVerification/ViewModel/LocalVerificationViewModel.swift index 6fba6414..97dbfac2 100644 --- a/ACON-iOS/ACON-iOS/Presentation/LocalVerification/ViewModel/LocalVerificationViewModel.swift +++ b/ACON-iOS/ACON-iOS/Presentation/LocalVerification/ViewModel/LocalVerificationViewModel.swift @@ -68,7 +68,7 @@ class LocalVerificationViewModel: Serviceable { case .success: self?.onPostLocalAreaSuccess.value = true if !AuthManager.shared.hasVerifiedArea { - UserDefaultsManager.set(true, forKey: .hasVerifiedArea) + UserDefaultsUtils.set(true, forKey: .hasVerifiedArea) } case .requestErr(let error): self?.onPostLocalAreaSuccess.value = false diff --git a/ACON-iOS/ACON-iOS/Presentation/Login/ViewModel/LoginViewModel.swift b/ACON-iOS/ACON-iOS/Presentation/Login/ViewModel/LoginViewModel.swift index 77ecb742..7c1f65ae 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Login/ViewModel/LoginViewModel.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Login/ViewModel/LoginViewModel.swift @@ -54,12 +54,13 @@ class LoginViewModel: Serviceable { ACService.shared.authService.postLogin(PostLoginRequest(socialType: socialType, idToken: idToken)){ [weak self] response in switch response { case .success(let data): - UserDefaultsManager.set(data.accessToken, forKey: .accessToken) - UserDefaultsManager.set(data.refreshToken, forKey: .refreshToken) - UserDefaultsManager.set(data.hasVerifiedArea, forKey: .hasVerifiedArea) - UserDefaultsManager.set(data.hasPreference, forKey: .hasPreference) + UserDefaultsUtils.set(data.accessToken, forKey: .accessToken) + UserDefaultsUtils.set(data.refreshToken, forKey: .refreshToken) + UserDefaultsUtils.set(data.hasVerifiedArea, forKey: .hasVerifiedArea) + UserDefaultsUtils.set(data.hasPreference, forKey: .hasPreference) AuthManager.shared.updateLastTokenRefreshDate() + TokenLogger.shared.log(.saved(tokenPrefix: String(data.accessToken.prefix(10)))) AmplitudeManager.shared.setUserID(data.externalUUID) AmplitudeManager.shared.setUserProperty(userProperties: ["id": data.externalUUID]) diff --git a/ACON-iOS/ACON-iOS/Presentation/Preference/View/PreferenceViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Preference/View/PreferenceViewController.swift index 63571380..adb5867c 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Preference/View/PreferenceViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Preference/View/PreferenceViewController.swift @@ -54,7 +54,7 @@ class PreferenceViewController: BaseViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - UserDefaultsManager.set(true, forKey: .hasSeenPreference) + UserDefaultsUtils.set(true, forKey: .hasSeenPreference) } diff --git a/ACON-iOS/ACON-iOS/Presentation/Preference/ViewModel/PreferenceViewModel.swift b/ACON-iOS/ACON-iOS/Presentation/Preference/ViewModel/PreferenceViewModel.swift index 65cd23f3..7ce11065 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Preference/ViewModel/PreferenceViewModel.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Preference/ViewModel/PreferenceViewModel.swift @@ -23,7 +23,7 @@ class PreferenceViewModel: Serviceable { case .success: onPutPreferenceSuccess.value = true if !AuthManager.shared.hasPreference { - UserDefaultsManager.set(true, forKey: .hasPreference) + UserDefaultsUtils.set(true, forKey: .hasPreference) } case .reIssueJWT: self.handleReissue { diff --git a/ACON-iOS/ACON-iOS/Presentation/Profile/View/ProfileSettingViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Profile/View/ProfileSettingViewController.swift index d39a54ed..e17011a9 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Profile/View/ProfileSettingViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Profile/View/ProfileSettingViewController.swift @@ -82,7 +82,7 @@ extension ProfileSettingViewController { NavigationUtils.navigateToSplash() } else { self?.showServerErrorAlert { - UserDefaultsManager.removeTokens() + UserDefaultsUtils.removeTokens() NavigationUtils.navigateToSplash() } } diff --git a/ACON-iOS/ACON-iOS/Presentation/Profile/ViewModel/SettingViewModel.swift b/ACON-iOS/ACON-iOS/Presentation/Profile/ViewModel/SettingViewModel.swift index a663bca7..41493c48 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Profile/ViewModel/SettingViewModel.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Profile/ViewModel/SettingViewModel.swift @@ -12,13 +12,13 @@ final class SettingViewModel: Serviceable { var onPostLogoutSuccess: ObservablePattern = ObservablePattern(nil) func postLogout() { - let refreshToken = UserDefaultsManager.get(String.self, forKey: .refreshToken) ?? "" + let refreshToken = UserDefaultsUtils.get(String.self, forKey: .refreshToken) ?? "" ACService.shared.authService.postLogout( PostLogoutRequest(refreshToken: refreshToken)) { result in switch result { case .success: - UserDefaultsManager.resetAppUserDefaults() + UserDefaultsUtils.resetAppUserDefaults() AmplitudeManager.shared.reset() self.onPostLogoutSuccess.value = true case .reIssueJWT: diff --git a/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift index 33f4e1c9..dbf1b3b6 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift @@ -186,13 +186,13 @@ private extension SplashViewController { if success { print("❄️ 토큰 갱신 성공") } else { - UserDefaultsManager.resetAppUserDefaults() + UserDefaultsUtils.resetAppUserDefaults() NavigationUtils.navigateToSplash() } } } catch { DispatchQueue.main.async { - UserDefaultsManager.resetAppUserDefaults() + UserDefaultsUtils.resetAppUserDefaults() NavigationUtils.navigateToSplash() } } diff --git a/ACON-iOS/ACON-iOS/Presentation/SpotList/View/SpotListViewController.swift b/ACON-iOS/ACON-iOS/Presentation/SpotList/View/SpotListViewController.swift index f6444bf7..8548560c 100644 --- a/ACON-iOS/ACON-iOS/Presentation/SpotList/View/SpotListViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/SpotList/View/SpotListViewController.swift @@ -753,7 +753,7 @@ private extension SpotListViewController { guard AuthManager.shared.hasToken else { return } guard !AuthManager.shared.hasVerifiedArea else { return } - let lastAlertTime = UserDefaultsManager.get(Date.self, forKey: .lastLocalVerificationAlertDate) + let lastAlertTime = UserDefaultsUtils.get(Date.self, forKey: .lastLocalVerificationAlertDate) let now = Date() if let lastTime = lastAlertTime { diff --git a/ACON-iOS/ACON-iOS/Presentation/Tutorial/View/TutorialContainerViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Tutorial/View/TutorialContainerViewController.swift index 08152d9e..891ef6f0 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Tutorial/View/TutorialContainerViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Tutorial/View/TutorialContainerViewController.swift @@ -44,7 +44,7 @@ class TutorialContainerViewController: BaseViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - UserDefaultsManager.set(true, forKey: .hasSeenTutorial) + UserDefaultsUtils.set(true, forKey: .hasSeenTutorial) } diff --git a/ACON-iOS/ACON-iOS/Presentation/Withdrawal/View/WithdrawalConfirmationViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Withdrawal/View/WithdrawalConfirmationViewController.swift index edfd59d6..16ed8e18 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Withdrawal/View/WithdrawalConfirmationViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Withdrawal/View/WithdrawalConfirmationViewController.swift @@ -43,7 +43,7 @@ extension WithdrawalConfirmationViewController { AmplitudeManager.shared.reset() } else { self.showServerErrorAlert { - UserDefaultsManager.removeTokens() + UserDefaultsUtils.removeTokens() NavigationUtils.navigateToSplash() } } diff --git a/ACON-iOS/ACON-iOS/Presentation/Withdrawal/ViewModel/WithdrawalViewModel.swift b/ACON-iOS/ACON-iOS/Presentation/Withdrawal/ViewModel/WithdrawalViewModel.swift index 6bfb851f..28919742 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Withdrawal/ViewModel/WithdrawalViewModel.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Withdrawal/ViewModel/WithdrawalViewModel.swift @@ -42,7 +42,7 @@ final class WithdrawalViewModel: Serviceable { } func postWithdrawal() { - let refreshToken = UserDefaultsManager.get(String.self, forKey: .refreshToken) ?? "" + let refreshToken = UserDefaultsUtils.get(String.self, forKey: .refreshToken) ?? "" guard let reasonText = selectedOption.value else { return } @@ -50,7 +50,7 @@ final class WithdrawalViewModel: Serviceable { WithdrawalRequest(reason: reasonText, refreshToken: refreshToken)) { result in switch result { case .success: - UserDefaultsManager.resetAppUserDefaults() + UserDefaultsUtils.resetAppUserDefaults() self.onSuccessPostWithdrawal.value = true case .reIssueJWT: self.handleReissue { [weak self] in From 29c81b823b4d593ebbe6e398c608559aee388fd6 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:32:21 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[Add,=20Feat]=20Token=20Logger=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 5 +- .../ACON-iOS/Global/Utils/TokenLogger.swift | 99 +++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index 564c2ec7..dca92453 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -137,6 +137,7 @@ 15E71E422DF5A44B0020689D /* OpeningTimeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15E71E412DF5A44A0020689D /* OpeningTimeView.swift */; }; 15F15E832DBFD17A002F81E2 /* Pretendard-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 15F15E822DBFD17A002F81E2 /* Pretendard-Light.otf */; }; 15F15E852DBFD206002F81E2 /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F15E842DBFD206002F81E2 /* LanguageType.swift */; }; + 15F1F6C42E9A5AB30014669E /* TokenLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */; }; 15F45F842D6664C900BD6B9C /* LoginLockOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */; }; 15F45F862D66877200BD6B9C /* LoadingAnimatedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */; }; 4C6B2F332D65A15D0089BCB6 /* WithdrawalTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6B2F312D65A15D0089BCB6 /* WithdrawalTargetType.swift */; }; @@ -462,6 +463,7 @@ 15E71E412DF5A44A0020689D /* OpeningTimeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpeningTimeView.swift; sourceTree = ""; }; 15F15E822DBFD17A002F81E2 /* Pretendard-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Light.otf"; sourceTree = ""; }; 15F15E842DBFD206002F81E2 /* LanguageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageType.swift; sourceTree = ""; }; + 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogger.swift; sourceTree = ""; }; 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginLockOverlayView.swift; sourceTree = ""; }; 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingAnimatedButton.swift; sourceTree = ""; }; 4C6B2F2E2D65A15D0089BCB6 /* WithdrawalRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalRequest.swift; sourceTree = ""; }; @@ -1605,7 +1607,7 @@ 748D6F7F2D2BCD8F007690B4 /* ObservablePattern.swift */, 748D6F812D2BCDB0007690B4 /* ScreenUtils.swift */, 155BC2912DF09EB200E1744E /* ShadowColorCache.swift */, - 748D6F7F2D2BCD8F007690B4 /* ObservablePattern.swift */, + 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */, 153B78242E74090700B772F9 /* UserDefaultsUtils.swift */, 748D6F7B2D2BCCFF007690B4 /* Enums */, ); @@ -2329,6 +2331,7 @@ 1576806E2DFA06A70079B255 /* SpotListSkeletonHeader.swift in Sources */, 74BF92122D391FFE00B923E3 /* LocalMapViewController.swift in Sources */, D6E8168E2D6228F5001E4EBF /* WithdrawalViewController.swift in Sources */, + 15F1F6C42E9A5AB30014669E /* TokenLogger.swift in Sources */, 740968B72DEC3CA700AFF624 /* VerifiedAreasCollectionViewCell.swift in Sources */, 740EE8282E059875007A5DCF /* GetAppUpdateRequest.swift in Sources */, 15304FC02E33BAA900EFCDEF /* ValueRatingViewController.swift in Sources */, diff --git a/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift b/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift new file mode 100644 index 00000000..42f21848 --- /dev/null +++ b/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift @@ -0,0 +1,99 @@ +// +// TokenLogger.swift +// ACON-iOS +// +// Created by 김유림 on 10/11/25. +// + +import Foundation + +final class TokenLogger { + + static let shared = TokenLogger() + private init() {} + + private let maxLogCount = 100 + + + // MARK: - Public + + func log(_ event: TokenLogEvent) { + var logs = loadLogs() + let timestamp = formattedDate() + let entry = "[\(timestamp)] \(event.message)" + logs.append(entry) + + // NOTE: 오래된 로그 제거 + if logs.count > maxLogCount { + logs = Array(logs.suffix(maxLogCount)) + } + + saveLogs(logs) + print("🪵 TokenLog:", entry) + } + + func loadLogs() -> [String] { + UserDefaultsUtils.get([String].self, forKey: .tokenLogs) ?? [] + } + + func clearLogs() { + UserDefaultsUtils.remove(forKey: .tokenLogs) + } + + + // MARK: - Private + + private func saveLogs(_ logs: [String]) { + UserDefaultsUtils.set(logs, forKey: .tokenLogs) + } + + private func formattedDate() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" + return formatter.string(from: Date()) + } + +} + + +// MARK: - Enum + +enum TokenLogEvent { + + // 상태 로그 + case valid + case noToken + case tokenExpired + + // 액션 로그 + case saved(tokenPrefix: String) + case cleared + case refreshStarted + case refreshSucceeded(tokenPrefix: String) + case refreshFailed(error: String?) + + var message: String { + switch self { + // 상태 로그 + case .valid: + return "✅ has valid access token" + case .noToken: + return "⚠️ no token found" + case .tokenExpired: + return "⏱️ access token expired — requesting refresh" + + // 액션 로그 + case .saved(let tokenPrefix): + return "💾 saved new access token (prefix: \(tokenPrefix))" + case .cleared: + return "🗑️ Access token cleared" + case .refreshStarted: + return "🔄 starting token refresh..." + case .refreshSucceeded(let tokenPrefix): + return "💾 token refresh succeeded (prefix: \(tokenPrefix))" + case .refreshFailed(let error): + return "❌ Token refresh failed: \(error ?? "unknown error")" + } + } + +} From 479678aed79294c91ecde45e5d0b1c1c800d30c4 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:32:56 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[Add,=20Feat]=20TokenLogVC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 4 + .../Login/View/TokenLogViewController.swift | 78 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 ACON-iOS/ACON-iOS/Presentation/Login/View/TokenLogViewController.swift diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index dca92453..91c1f9f4 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -138,6 +138,7 @@ 15F15E832DBFD17A002F81E2 /* Pretendard-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 15F15E822DBFD17A002F81E2 /* Pretendard-Light.otf */; }; 15F15E852DBFD206002F81E2 /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F15E842DBFD206002F81E2 /* LanguageType.swift */; }; 15F1F6C42E9A5AB30014669E /* TokenLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */; }; + 15F1F6C62E9A5ED80014669E /* TokenLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */; }; 15F45F842D6664C900BD6B9C /* LoginLockOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */; }; 15F45F862D66877200BD6B9C /* LoadingAnimatedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */; }; 4C6B2F332D65A15D0089BCB6 /* WithdrawalTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6B2F312D65A15D0089BCB6 /* WithdrawalTargetType.swift */; }; @@ -464,6 +465,7 @@ 15F15E822DBFD17A002F81E2 /* Pretendard-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Light.otf"; sourceTree = ""; }; 15F15E842DBFD206002F81E2 /* LanguageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageType.swift; sourceTree = ""; }; 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogger.swift; sourceTree = ""; }; + 15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogViewController.swift; sourceTree = ""; }; 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginLockOverlayView.swift; sourceTree = ""; }; 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingAnimatedButton.swift; sourceTree = ""; }; 4C6B2F2E2D65A15D0089BCB6 /* WithdrawalRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalRequest.swift; sourceTree = ""; }; @@ -1697,6 +1699,7 @@ 748ECA6D2D3196ED00BBC981 /* LoginViewController.swift */, 15CD25832D40364F00320006 /* LoginModalView.swift */, 15CD25812D40364400320006 /* LoginModalViewController.swift */, + 15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */, ); path = View; sourceTree = ""; @@ -2359,6 +2362,7 @@ 74C914D12D64DBBB00BC13E1 /* ImageType.swift in Sources */, 74BF92152D391FFE00B923E3 /* LocalVerificationView.swift in Sources */, 151FBFBA2DBD2F1B005F4D0D /* ACFontType.swift in Sources */, + 15F1F6C62E9A5ED80014669E /* TokenLogViewController.swift in Sources */, 15AA6D1A2D68C4AD008021C6 /* ReviewVerificationErrorType.swift in Sources */, 741429652D5D470200B69528 /* SettingViewModel.swift in Sources */, 15CD256B2D3D61F500320006 /* GlassmorphismView.swift in Sources */, diff --git a/ACON-iOS/ACON-iOS/Presentation/Login/View/TokenLogViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Login/View/TokenLogViewController.swift new file mode 100644 index 00000000..a7463ec0 --- /dev/null +++ b/ACON-iOS/ACON-iOS/Presentation/Login/View/TokenLogViewController.swift @@ -0,0 +1,78 @@ +// +// TokenLogViewController.swift +// ACON-iOS +// +// Created by 김유림 on 10/11/25. +// + +import UIKit + +final class TokenLogViewController: BaseViewController { + + // MARK: - UI Properties + + private let textView = UITextView() + private let clearButton = UIButton(type: .system) + + + // MARK: - Life Cycles + + override func viewDidLoad() { + super.viewDidLoad() + + addTarget() + loadLogs() + } + + // MARK: - UI Settings + + override func setHierarchy() { + super.setHierarchy() + + view.addSubviews(clearButton, textView) + } + + override func setLayout() { + super.setLayout() + + clearButton.snp.makeConstraints { + $0.top.equalToSuperview().offset(12) + $0.trailing.equalToSuperview().inset(ScreenUtils.horizontalInset) + } + + textView.snp.makeConstraints { + $0.top.equalTo(clearButton.snp.bottom).offset(8) + $0.horizontalEdges.equalToSuperview().inset(12) + $0.bottom.equalTo(view.safeAreaLayoutGuide) + } + } + + override func setStyle() { + super.setStyle() + + textView.do { + $0.isEditable = false + $0.font = .monospacedSystemFont(ofSize: 13, weight: .regular) + } + + clearButton.setTitle("Clear Logs", for: .normal) + } + + private func addTarget() { + clearButton.addTarget(self, action: #selector(clearLogs), for: .touchUpInside) + } + + @objc private func clearLogs() { + TokenLogger.shared.clearLogs() + loadLogs() + } + + + // MARK: - Helper + + private func loadLogs() { + let logs = TokenLogger.shared.loadLogs().reversed() + textView.text = logs.joined(separator: "\n\n") + } + +} From 050abf797975fd1d0ee191d8dcdbd79a99dfad8b Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:39:06 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[Chore]=20TokenLogEvent=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift b/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift index 42f21848..ee3af337 100644 --- a/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift +++ b/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift @@ -68,7 +68,6 @@ enum TokenLogEvent { // 액션 로그 case saved(tokenPrefix: String) case cleared - case refreshStarted case refreshSucceeded(tokenPrefix: String) case refreshFailed(error: String?) @@ -87,8 +86,6 @@ enum TokenLogEvent { return "💾 saved new access token (prefix: \(tokenPrefix))" case .cleared: return "🗑️ Access token cleared" - case .refreshStarted: - return "🔄 starting token refresh..." case .refreshSucceeded(let tokenPrefix): return "💾 token refresh succeeded (prefix: \(tokenPrefix))" case .refreshFailed(let error): From 58d4f8cf152cbe64fb10219b992a94221db82eee Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:39:46 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[Chore]=20UserDefaults=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=20=EB=A1=9C=EC=A7=81=EC=97=90=EC=84=9C=20tok?= =?UTF-8?q?enLogs=20=EC=A0=9C=EC=99=B8=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift b/ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift index f1553f36..832f7a2e 100644 --- a/ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift +++ b/ACON-iOS/ACON-iOS/Global/Utils/UserDefaultsUtils.swift @@ -23,7 +23,7 @@ struct UserDefaultsUtils { case lastTokenRefreshDate case lastLocalVerificationAlertDate - case tokenLogs + case tokenLogs // NOTE: 초기화되면 안 됨 } @@ -54,11 +54,14 @@ struct UserDefaultsUtils { /// - `hasSeenTutorial` /// - `hasSeenLocalVerification` /// - `hasSeenPreference` + /// - 토큰 로그는 유지됩니다. + /// - `tokenLogs` static func resetAppUserDefaults() { for key in Keys.allCases { if key == .hasSeenTutorial || key == .hasSeenLocalVerification - || key == .hasSeenPreference { continue } + || key == .hasSeenPreference + || key == .tokenLogs { continue } remove(forKey: key) } From cdef8969507949a376c3a6d9adff59e95dd5cf5d Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:40:19 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[Feat]=20=EB=A1=9C=EA=B7=B8=20=EC=82=BD?= =?UTF-8?q?=EC=9E=85=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift | 4 ++++ .../Splash/View/SplashViewController.swift | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift b/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift index 36ddea01..e9344a88 100644 --- a/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift +++ b/ACON-iOS/ACON-iOS/Global/Service/AuthManager.swift @@ -49,14 +49,17 @@ final class AuthManager { UserDefaultsUtils.set(data.accessToken, forKey: .accessToken) UserDefaultsUtils.set(data.refreshToken, forKey: .refreshToken) AuthManager.shared.updateLastTokenRefreshDate() + TokenLogger.shared.log(.refreshSucceeded(tokenPrefix: String(data.accessToken.prefix(10)))) continuation.resume(returning: true) case .requestErr(let error): if error.code == 40088 { print("❄️ remove token") UserDefaultsUtils.removeTokens() + TokenLogger.shared.log(.refreshFailed(error: error.localizedDescription)) continuation.resume(returning: false) } default: + TokenLogger.shared.log(.refreshFailed(error: "unknown")) continuation.resume(returning: false) } } @@ -75,6 +78,7 @@ final class AuthManager { } let elapsed = Date().timeIntervalSince(lastRefresh) print("❄️ token elapsed: \(elapsed) / \(refreshInterval) seconds") + TokenLogger.shared.log(elapsed >= refreshInterval ? .tokenExpired : .valid) return elapsed >= refreshInterval } diff --git a/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift index dbf1b3b6..d8d33ba1 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift @@ -34,9 +34,16 @@ class SplashViewController: BaseViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - if AuthManager.shared.needsTokenRefresh() { - refreshToken() + if AuthManager.shared.hasToken { + if AuthManager.shared.needsTokenRefresh() { + refreshToken() + } else { + TokenLogger.shared.log(.valid) + } + } else { + TokenLogger.shared.log(.noToken) } + } override func viewDidAppear(_ animated: Bool) { From 718640f906034e04068c836460196c47f506b1df Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 03:40:48 +0900 Subject: [PATCH 07/13] =?UTF-8?q?[Feat]=20TokenLogVC=20present=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EA=B5=AC=ED=98=84=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Login/View/LoginViewController.swift | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift index 617106c5..3b18aa27 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift @@ -35,6 +35,7 @@ class LoginViewController: BaseNavViewController { ? NavigationUtils.navigateToTabBar() : NavigationUtils.navigateToTutorial() } + setTokenLogButton() } override func viewWillAppear(_ animated: Bool) { @@ -54,7 +55,10 @@ class LoginViewController: BaseNavViewController { } } } - + + + // MARK: - UI Setting + override func setHierarchy() { super.setHierarchy() @@ -182,3 +186,28 @@ extension LoginViewController: ASAuthorizationControllerDelegate { } } + + +// MARK: - Set Token Log Button + +private extension LoginViewController { + + func setTokenLogButton() { + _ = UIButton().then { + navigationBarView.addSubview($0) + $0.snp.makeConstraints { + $0.size.equalTo(20) + $0.leading.centerY.equalToSuperview() + } + $0.backgroundColor = .clear + $0.addTarget(self, action: #selector(presentTokenLogVC), for: .touchUpInside) + } + } + + @objc + func presentTokenLogVC() { + let tokenLogVC = TokenLogViewController() + self.present(tokenLogVC, animated: true) + } + +} From 26b666f23e9f88adbd78211874d7f13ead221186 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 04:28:01 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[Chore]=20TokenLogEvent=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ACON-iOS/Global/Utils/TokenLogger.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift b/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift index ee3af337..835bc879 100644 --- a/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift +++ b/ACON-iOS/ACON-iOS/Global/Utils/TokenLogger.swift @@ -73,22 +73,22 @@ enum TokenLogEvent { var message: String { switch self { - // 상태 로그 - case .valid: + // NOTE: 상태 로그 (스플래시 진입 시점 토큰 상태) + case .valid: /// 액세스토큰 유효 (-> 자동로그인) return "✅ has valid access token" - case .noToken: + case .noToken: /// 토큰 없음 (로그아웃 상태) return "⚠️ no token found" - case .tokenExpired: + case .tokenExpired: /// 액세스토큰 만료 return "⏱️ access token expired — requesting refresh" - // 액션 로그 - case .saved(let tokenPrefix): + // NOTE: 액션 로그 + case .saved(let tokenPrefix): /// 로그인 성공 시 return "💾 saved new access token (prefix: \(tokenPrefix))" - case .cleared: + case .cleared: /// 로그아웃/탈퇴 시 return "🗑️ Access token cleared" - case .refreshSucceeded(let tokenPrefix): + case .refreshSucceeded(let tokenPrefix): /// 액세스토큰 갱신 성공 시 return "💾 token refresh succeeded (prefix: \(tokenPrefix))" - case .refreshFailed(let error): + case .refreshFailed(let error): /// 액세스토큰 갱신 실패 시 return "❌ Token refresh failed: \(error ?? "unknown error")" } } From c921f6e242f75a2a328914026be6d9af61c1c301 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 04:29:29 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[Setting]=202.1.3=20=EB=B2=84=EC=A0=84?= =?UTF-8?q?=EC=97=85,=20TestFlight=202.1.3=20(build:=202025.1014.0348)=20(?= =?UTF-8?q?#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 8 ++++---- ACON-iOS/ACON-iOS/Global/Settings/Info.plist | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index 91c1f9f4..725a472e 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -2476,7 +2476,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2025.0915.0016; + CURRENT_PROJECT_VERSION = 2025.1014.0348; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF; @@ -2495,7 +2495,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.2; + MARKETING_VERSION = 2.1.3; PRODUCT_BUNDLE_IDENTIFIER = "com.ACON.ACON-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2519,7 +2519,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2025.0915.0016; + CURRENT_PROJECT_VERSION = 2025.1014.0348; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF; GENERATE_INFOPLIST_FILE = NO; @@ -2537,7 +2537,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.2; + MARKETING_VERSION = 2.1.3; PRODUCT_BUNDLE_IDENTIFIER = "com.ACON.ACON-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/ACON-iOS/ACON-iOS/Global/Settings/Info.plist b/ACON-iOS/ACON-iOS/Global/Settings/Info.plist index 5017adf3..cd6b1832 100644 --- a/ACON-iOS/ACON-iOS/Global/Settings/Info.plist +++ b/ACON-iOS/ACON-iOS/Global/Settings/Info.plist @@ -29,7 +29,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 2.1.2 + 2.1.3 CFBundleURLTypes @@ -54,7 +54,7 @@ CFBundleVersion - 2025.0915.0016 + 2025.1014.0348 GADApplicationIdentifier ${GAD_APPLICATION_IDENTIFIER} GAD_AD_UNIT_ID From 953861699f5f880f9464b8492f33a7fa186408bc Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 04:40:18 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[Add,Feat]=20BuildConfig=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 4 ++++ .../Global/Settings/Config/BuildConfig.swift | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 ACON-iOS/ACON-iOS/Global/Settings/Config/BuildConfig.swift diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index 725a472e..d18cc0e8 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -139,6 +139,7 @@ 15F15E852DBFD206002F81E2 /* LanguageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F15E842DBFD206002F81E2 /* LanguageType.swift */; }; 15F1F6C42E9A5AB30014669E /* TokenLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */; }; 15F1F6C62E9A5ED80014669E /* TokenLogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */; }; + 15F1F6C82E9D8C530014669E /* BuildConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F1F6C72E9D8C530014669E /* BuildConfig.swift */; }; 15F45F842D6664C900BD6B9C /* LoginLockOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */; }; 15F45F862D66877200BD6B9C /* LoadingAnimatedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */; }; 4C6B2F332D65A15D0089BCB6 /* WithdrawalTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6B2F312D65A15D0089BCB6 /* WithdrawalTargetType.swift */; }; @@ -466,6 +467,7 @@ 15F15E842DBFD206002F81E2 /* LanguageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageType.swift; sourceTree = ""; }; 15F1F6C32E9A5AB30014669E /* TokenLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogger.swift; sourceTree = ""; }; 15F1F6C52E9A5ED80014669E /* TokenLogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenLogViewController.swift; sourceTree = ""; }; + 15F1F6C72E9D8C530014669E /* BuildConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildConfig.swift; sourceTree = ""; }; 15F45F832D6664C900BD6B9C /* LoginLockOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginLockOverlayView.swift; sourceTree = ""; }; 15F45F852D66877200BD6B9C /* LoadingAnimatedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingAnimatedButton.swift; sourceTree = ""; }; 4C6B2F2E2D65A15D0089BCB6 /* WithdrawalRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawalRequest.swift; sourceTree = ""; }; @@ -1229,6 +1231,7 @@ 15BFF8B52D665A5B00909C4F /* Debug.xcconfig */, 15BFF8B62D665A5B00909C4F /* Release.xcconfig */, 74B25C312D2FF022008BDCB7 /* Shared.xcconfig */, + 15F1F6C72E9D8C530014669E /* BuildConfig.swift */, 74B25C2B2D2FEE8C008BDCB7 /* Config.swift */, ); path = Config; @@ -2225,6 +2228,7 @@ 74DDBA8B2DC45E3B00BF9824 /* ACButtonType.swift in Sources */, 150BA5812E56F8580058A06B /* LimitedSpotsTutorialViewController.swift in Sources */, 156AA72A2D6510E1005B8DCE /* GetNicknameValidityRequest.swift in Sources */, + 15F1F6C82E9D8C530014669E /* BuildConfig.swift in Sources */, 74D297F82D63467900DDEE31 /* ImageService.swift in Sources */, 15A147212D5B256D003793EE /* LocalVerificationFlowType.swift in Sources */, 15A167E22E3D722B00062C49 /* AlbumFlowType.swift in Sources */, diff --git a/ACON-iOS/ACON-iOS/Global/Settings/Config/BuildConfig.swift b/ACON-iOS/ACON-iOS/Global/Settings/Config/BuildConfig.swift new file mode 100644 index 00000000..115ce372 --- /dev/null +++ b/ACON-iOS/ACON-iOS/Global/Settings/Config/BuildConfig.swift @@ -0,0 +1,18 @@ +// +// BuildConfig.swift +// ACON-iOS +// +// Created by 김유림 on 10/14/25. +// + +import Foundation + +enum BuildConfig { + + #if DEBUG + static let isDebug = true + #else + static let isDebug = false + #endif + +} From 9e311d7e2d8a51aea178d74f4f5c5c9ab8cda9c8 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 04:40:59 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[Feat]=20TokenLog=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=EC=9D=84=20=EB=94=94=EB=B2=84=EA=B7=B8=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=ED=94=8C=20=EB=B9=8C=EB=93=9C=EC=97=90=EB=A7=8C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Login/View/LoginViewController.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift index 3b18aa27..b288f3f8 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Login/View/LoginViewController.swift @@ -35,7 +35,10 @@ class LoginViewController: BaseNavViewController { ? NavigationUtils.navigateToTabBar() : NavigationUtils.navigateToTutorial() } - setTokenLogButton() + + if BuildConfig.isDebug || Bundle.main.isTestFlight { + setTokenLogButton() + } } override func viewWillAppear(_ animated: Bool) { From 414e35d02c112306001ce5fc511e0dd1a6afe109 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 14:53:40 +0900 Subject: [PATCH 12/13] [Chore] Testflight build: 2025.1014.0447 (#276) --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 4 ++-- ACON-iOS/ACON-iOS/Global/Settings/Info.plist | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index 0f625203..bb99b59a 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -2480,7 +2480,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2025.1014.0348; + CURRENT_PROJECT_VERSION = 2025.1014.0447; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF; @@ -2523,7 +2523,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2025.1014.0348; + CURRENT_PROJECT_VERSION = 2025.1014.0447; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF; GENERATE_INFOPLIST_FILE = NO; diff --git a/ACON-iOS/ACON-iOS/Global/Settings/Info.plist b/ACON-iOS/ACON-iOS/Global/Settings/Info.plist index c80b6b1b..fdc4854d 100644 --- a/ACON-iOS/ACON-iOS/Global/Settings/Info.plist +++ b/ACON-iOS/ACON-iOS/Global/Settings/Info.plist @@ -54,7 +54,7 @@ CFBundleVersion - 2025.1014.0348 + 2025.1014.0447 GADApplicationIdentifier ${GAD_APPLICATION_IDENTIFIER} GAD_AD_UNIT_ID From d1a71601a647db4eceb4e98da880ab9450736515 Mon Sep 17 00:00:00 2001 From: Yurim Kim Date: Tue, 14 Oct 2025 15:18:00 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[Fix]=20=ED=86=A0=ED=81=B0=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20.valid=20=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0,?= =?UTF-8?q?=20Testflight=20=EB=B0=B0=ED=8F=AC=20(#276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj | 4 ++-- ACON-iOS/ACON-iOS/Global/Settings/Info.plist | 2 +- .../Presentation/Splash/View/SplashViewController.swift | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj index bb99b59a..17bfc138 100644 --- a/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj +++ b/ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj @@ -2480,7 +2480,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2025.1014.0447; + CURRENT_PROJECT_VERSION = 2025.1014.1518; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF; @@ -2523,7 +2523,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 2025.1014.0447; + CURRENT_PROJECT_VERSION = 2025.1014.1518; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = KX5Q77JSUF; GENERATE_INFOPLIST_FILE = NO; diff --git a/ACON-iOS/ACON-iOS/Global/Settings/Info.plist b/ACON-iOS/ACON-iOS/Global/Settings/Info.plist index fdc4854d..5a687299 100644 --- a/ACON-iOS/ACON-iOS/Global/Settings/Info.plist +++ b/ACON-iOS/ACON-iOS/Global/Settings/Info.plist @@ -54,7 +54,7 @@ CFBundleVersion - 2025.1014.0447 + 2025.1014.1518 GADApplicationIdentifier ${GAD_APPLICATION_IDENTIFIER} GAD_AD_UNIT_ID diff --git a/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift b/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift index b1c0e5bd..2c561c84 100644 --- a/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift +++ b/ACON-iOS/ACON-iOS/Presentation/Splash/View/SplashViewController.swift @@ -37,8 +37,6 @@ class SplashViewController: BaseViewController { if AuthManager.shared.hasToken { if AuthManager.shared.needsTokenRefresh() { refreshToken() - } else { - TokenLogger.shared.log(.valid) } } else { TokenLogger.shared.log(.noToken)