diff --git a/project/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency.swift b/project/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency.swift index 560c3619..e21f5356 100644 --- a/project/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency.swift +++ b/project/Plugins/DependencyPlugin/ProjectDescriptionHelpers/Dependency.swift @@ -24,6 +24,10 @@ public enum ModuleDependency { public static let PresentationCore: TargetDependency = .project(target: "PresentationCore", path: .relativeToRoot("Projects/Presentation/PresentationCore")) } + public enum Module { + public static let Logger: TargetDependency = .project(target: "Logger", path: .relativeToRoot("Projects/Module/Logger")) + } + public static let Core: TargetDependency = .project(target: "Core", path: .relativeToRoot("Projects/Core")) public static let Testing: TargetDependency = .project(target: "Testing", path: .relativeToRoot("Projects/Testing")) diff --git a/project/Projects/App/Sources/DIAssembly/LoggerAssembly.swift b/project/Projects/App/Sources/DIAssembly/LoggerAssembly.swift index 8d5fa0c9..3c383d04 100644 --- a/project/Projects/App/Sources/DIAssembly/LoggerAssembly.swift +++ b/project/Projects/App/Sources/DIAssembly/LoggerAssembly.swift @@ -6,44 +6,26 @@ // import Foundation + import RootFeature import AuthFeature import PresentationCore import CenterMainPageFeature +import Logger + import Swinject public struct LoggerAssembly: Assembly { public func assemble(container: Container) { - // MARK: Message pusher - container.register(LoggerMessagePublisher.self) { _ in - #if DEBUG || QA - return DebugLogger() + container.register(Logger.self) { _ in + #if DEBUG + return MockLogger() #endif + return AmplitudeLogger() } .inObjectScope(.container) - - // MARK: Overall logger - container.register(OverallLogger.self) { _ in - DefaultOverallLogger() - } - .inObjectScope(.container) - - container.register(CenterRegisterLogger.self) { resolver in - let overallLogger = resolver.resolve(OverallLogger.self)! - return overallLogger - } - - container.register(WorkerRegisterLogger.self) { resolver in - let overallLogger = resolver.resolve(OverallLogger.self)! - return overallLogger - } - - container.register(PostRegisterLogger.self) { resolver in - let overallLogger = resolver.resolve(OverallLogger.self)! - return overallLogger - } } } diff --git a/project/Projects/Module/Logger/Project.swift b/project/Projects/Module/Logger/Project.swift new file mode 100644 index 00000000..6cc62df0 --- /dev/null +++ b/project/Projects/Module/Logger/Project.swift @@ -0,0 +1,39 @@ +// +// Project.swift +// ProjectDescriptionHelpers +// +// Created by choijunyeong on 2024/10/30. +// + +import ProjectDescription +import ConfigurationPlugin +import DependencyPlugin +import Foundation + +let project = Project( + name: "Logger", + settings: .settings( + configurations: IdleConfiguration.emptyConfigurations + ), + targets: [ + .target( + name: "Logger", + destinations: DeploymentSettings.platforms, + product: .framework, + bundleId: "$(PRODUCT_BUNDLE_IDENTIFIER)", + deploymentTargets: DeploymentSettings.deployment_iOS_version, + sources: [ + "Sources/**", + SecretSource.amplitudeConfig, + ], + resources: ["Resources/**",], + dependencies: [ + // External + D.ThirdParty.Amplitude, + ], + settings: .settings( + configurations: IdleConfiguration.presentationConfigurations + ) + ), + ] +) diff --git a/project/Projects/Module/Logger/Resources/empty.txt b/project/Projects/Module/Logger/Resources/empty.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/project/Projects/Module/Logger/Resources/empty.txt @@ -0,0 +1 @@ + diff --git a/project/Projects/Module/Logger/Sources/AccountRegisteration/CenterAccountRegisterationLogBuilder.swift b/project/Projects/Module/Logger/Sources/AccountRegisteration/CenterAccountRegisterationLogBuilder.swift new file mode 100644 index 00000000..ede8dae8 --- /dev/null +++ b/project/Projects/Module/Logger/Sources/AccountRegisteration/CenterAccountRegisterationLogBuilder.swift @@ -0,0 +1,43 @@ +// +// AccountRegisterationLogBuilder.swift +// Logger +// +// Created by choijunios on 10/30/24. +// + +import Foundation + +public struct AccountRegisterationLogObject: LoggingObject { + + public var eventType: String = "AccountRegister" + + public var properties: [String : Any] { + [ + "step": step, + "stepName": stepName + ] + } + + let step: Int + let stepName: String + + init(step: Int, stepName: String) { + self.step = step + self.stepName = stepName + } +} + +public class AccountRegisterationLogBuilder: LogObjectBuilder { + + let step: Int + let stepName: String + + public init(step: Int, stepName: String) { + self.step = step + self.stepName = stepName + } + + public func build() -> LoggingObject { + AccountRegisterationLogObject(step: step, stepName: stepName) + } +} diff --git a/project/Projects/Module/Logger/Sources/AmplitudeLogger.swift b/project/Projects/Module/Logger/Sources/AmplitudeLogger.swift new file mode 100644 index 00000000..10b59132 --- /dev/null +++ b/project/Projects/Module/Logger/Sources/AmplitudeLogger.swift @@ -0,0 +1,49 @@ +// +// AmplitudeLogger.swift +// Logger +// +// Created by choijunios on 10/30/24. +// + +import Foundation +import Combine + + +import AmplitudeSwift + +public class AmplitudeLogger: Logger { + + private let objectPublisher: PassthroughSubject = .init() + + private var bag: Set = .init() + + private let amplitude: Amplitude + + public init() { + + self.amplitude = Amplitude( + configuration: Configuration( + apiKey: AmplitudeConfig.apiKey + ) + ) + + objectPublisher + .throttle(for: 0.3, scheduler: DispatchQueue.main, latest: true) + .sink { [weak self] object in + + let eventType = object.eventType + let eventProperties = object.properties + + self?.amplitude.track(eventType: eventType, eventProperties: eventProperties) + } + .store(in: &bag) + } + + public func setUserId(id: String) { + amplitude.setUserId(userId: id) + } + + public func send(_ object: any LoggingObject) { + objectPublisher.send(object) + } +} diff --git a/project/Projects/Module/Logger/Sources/CreatePost/CreatePostLogBuilder.swift b/project/Projects/Module/Logger/Sources/CreatePost/CreatePostLogBuilder.swift new file mode 100644 index 00000000..70579c51 --- /dev/null +++ b/project/Projects/Module/Logger/Sources/CreatePost/CreatePostLogBuilder.swift @@ -0,0 +1,43 @@ +// +// CreatePostLogBuilder.swift +// Logger +// +// Created by choijunios on 10/30/24. +// + +import Foundation + +public struct CreatePostLogObject: LoggingObject { + + public var eventType: String = "CreatePost" + + public var properties: [String : Any] { + [ + "step": step, + "stepName": stepName + ] + } + + let step: Int + let stepName: String + + init(step: Int, stepName: String) { + self.step = step + self.stepName = stepName + } +} + +public class CreatePostLogBuilder: LogObjectBuilder { + + let step: Int + let stepName: String + + public init(step: Int, stepName: String) { + self.step = step + self.stepName = stepName + } + + public func build() -> LoggingObject { + CreatePostLogObject(step: step, stepName: stepName) + } +} diff --git a/project/Projects/Module/Logger/Sources/LogObjectBuilder.swift b/project/Projects/Module/Logger/Sources/LogObjectBuilder.swift new file mode 100644 index 00000000..1ff1d9b6 --- /dev/null +++ b/project/Projects/Module/Logger/Sources/LogObjectBuilder.swift @@ -0,0 +1,14 @@ +// +// LogObjectBuilder.swift +// Logger +// +// Created by choijunios on 10/31/24. +// + +import Foundation + +public protocol LogObjectBuilder { + + /// 로깅 오브젝트를 생성한다. + func build() -> LoggingObject +} diff --git a/project/Projects/Module/Logger/Sources/Logger.swift b/project/Projects/Module/Logger/Sources/Logger.swift new file mode 100644 index 00000000..b6e71678 --- /dev/null +++ b/project/Projects/Module/Logger/Sources/Logger.swift @@ -0,0 +1,20 @@ +// +// Logger.swift +// Logger +// +// Created by choijunios on 10/30/24. +// + +import Foundation + + +public protocol LoggingObject { + var eventType: String { get } + var properties: [String: Any] { get } +} + +public protocol Logger { + + func send(_ object: LoggingObject) +} + diff --git a/project/Projects/Module/Logger/Sources/MockLogger.swift b/project/Projects/Module/Logger/Sources/MockLogger.swift new file mode 100644 index 00000000..01a10083 --- /dev/null +++ b/project/Projects/Module/Logger/Sources/MockLogger.swift @@ -0,0 +1,17 @@ +// +// MockLogger.swift +// Logger +// +// Created by choijunios on 10/30/24. +// + +import Foundation + +public class MockLogger: Logger { + + public init() { } + + public func send(_ object: any LoggingObject) { + print("[\(object.eventType)] \(object.properties)") + } +} diff --git a/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift b/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift index e5eab911..c226d1ec 100644 --- a/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift +++ b/project/Projects/Presentation/DSKit/Sources/Component/TextField/MultiLineTextField.swift @@ -96,7 +96,7 @@ public class MultiLineTextField: UITextView { placeHolderLabel.topAnchor.constraint(equalTo: frameGuide.topAnchor, constant: textContainerInset.top), placeHolderLabel.leftAnchor.constraint(equalTo: frameGuide.leftAnchor, constant: textContainerInset.left), placeHolderLabel.rightAnchor.constraint(equalTo: frameGuide.rightAnchor, constant: -textContainerInset.right), - + placeHolderLabel.bottomAnchor.constraint(equalTo: frameGuide.bottomAnchor, constant: -textContainerInset.bottom), ]) } diff --git a/project/Projects/Presentation/Feature/Auth/ExampleApp/Sources/SceneDelegate.swift b/project/Projects/Presentation/Feature/Auth/ExampleApp/Sources/SceneDelegate.swift index c109150c..965f8386 100644 --- a/project/Projects/Presentation/Feature/Auth/ExampleApp/Sources/SceneDelegate.swift +++ b/project/Projects/Presentation/Feature/Auth/ExampleApp/Sources/SceneDelegate.swift @@ -25,7 +25,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = scene as? UIWindowScene else { return } DependencyInjector.shared.assemble(MockAssemblies) - DependencyInjector.shared.register(CenterRegisterLogger.self, CenterAuthLogger()) authCoordinator = .init() @@ -63,12 +62,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { authCoordinator?.start() } } - -class CenterAuthLogger: CenterRegisterLogger { - - func logCenterRegisterStep(stepName: String, stepIndex: Int) { } - - func startCenterRegister() { } - - func logCenterRegisterDuration() { } -} diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/CenterAccountRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/CenterAccountRegisterCoordinator.swift index 899a5dbf..9b9164f3 100644 --- a/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/CenterAccountRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/CenterAccountRegisterCoordinator.swift @@ -6,37 +6,12 @@ // import UIKit + import BaseFeature +import Logger import PresentationCore import Core -enum CenterAccountRegisterStage: Int { - - case registerFinished - case name - case phoneNumber - case businessOwner - case idPassword - case finish - - var screenName: String { - switch self { - case .registerFinished: - "" - case .name: - "name" - case .phoneNumber: - "phoneNumber" - case .businessOwner: - "businessOwner" - case .idPassword: - "idPassword" - case .finish: - "" - } - } -} - public enum CenterAccountRegisterCoordinatorDestination { case centerMainPage } @@ -45,7 +20,7 @@ public class CenterAccountRegisterCoordinator: Coordinator { // Injected @Injected var router: RouterProtocol - @Injected var logger: CenterRegisterLogger + @Injected var logger: Logger public var onFinish: (() -> ())? @@ -74,9 +49,12 @@ public class CenterAccountRegisterCoordinator: Coordinator { vm.presentCompleteScreen = { [weak self] in + guard let self else { return } + // MARK: 센터 계정 회원가입 완료 로깅 - self?.logger.logCenterRegisterDuration() + logCurrentStage(stage: .finish) + // MARK: 완료화면으로 이동 let object: AnonymousCompleteVCRenderObject = .init( titleText: "센터관리자 로그인을\n완료했어요!", descriptionText: "로그인 정보는 마지막 접속일부터\n180일간 유지될 예정이에요.", @@ -87,7 +65,7 @@ public class CenterAccountRegisterCoordinator: Coordinator { } // 완료화면으로 이동 - self?.router.presentAnonymousCompletePage(object) + router.presentAnonymousCompletePage(object) } vm.presentAlert = { [weak self] object in @@ -124,10 +102,11 @@ public class CenterAccountRegisterCoordinator: Coordinator { self?.onFinish?() } - excuteStage(.name, moveTo: .next) + // MARK: 센터 계정 회원가입 시작 로깅 + logCurrentStage(stage: .start) - // MARK: 센터 계정등록 시작 로깅 - logger.startCenterRegister() + // 첫시작 페이지로 이동 + excuteStage(.name, moveTo: .next) } } @@ -160,20 +139,15 @@ extension CenterAccountRegisterCoordinator { private func excuteStage(_ stage: CenterAccountRegisterStage, moveTo: MovingDirection) { currentStage = stage switch stage { - case .registerFinished: + case .start: router.popModule(animated: true) case .finish: return default: - // MARK: 등록 스테이지 이동 로깅 - if moveTo == .next { - logger.logCenterRegisterStep( - stepName: stage.screenName, - stepIndex: stage.rawValue-1 - ) - } - + // 로깅 + logCurrentStage() + let vc = stageViewControllers[stage.rawValue-1] showPageViewControllerStage(viewController: vc, moveTo: moveTo) } @@ -186,6 +160,16 @@ extension CenterAccountRegisterCoordinator { animated: true ) } + + func logCurrentStage(stage: CenterAccountRegisterStage? = nil) { + let logObject = AccountRegisterationLogBuilder( + step: (stage ?? currentStage).step, + stepName: (stage ?? currentStage).screenKorName + ) + .build() + + logger.send(logObject) + } } extension Notification.Name { diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/CenterAccountRegisterStage.swift b/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/CenterAccountRegisterStage.swift new file mode 100644 index 00000000..d3be203e --- /dev/null +++ b/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/CenterAccountRegisterStage.swift @@ -0,0 +1,37 @@ +// +// CenterAccountRegisterStage.swift +// AuthFeature +// +// Created by choijunios on 10/30/24. +// + +import Foundation + +enum CenterAccountRegisterStage: Int { + + case start + case name + case phoneNumber + case businessOwner + case idPassword + case finish + + var screenKorName: String { + switch self { + case .start: + "센터관리자 회원가입 시작" + case .name: + "이름 입력" + case .phoneNumber: + "전화번호 입력" + case .businessOwner: + "사업자 인증번호 입력" + case .idPassword: + "아이디 패스워드 입력" + case .finish: + "가입완료" + } + } + + var step: Int { self.rawValue } +} diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/asd.swift b/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/PasswordValidationCase.swift similarity index 96% rename from project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/asd.swift rename to project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/PasswordValidationCase.swift index 47f934a3..d70cdf62 100644 --- a/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/asd.swift +++ b/project/Projects/Presentation/Feature/Auth/Sources/Center/AccountRegister/Model/PasswordValidationCase.swift @@ -1,5 +1,5 @@ // -// asd.swift +// PasswordValidationCase.swift // AuthFeature // // Created by choijunios on 10/22/24. diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Center/CenterRegisterLogger.swift b/project/Projects/Presentation/Feature/Auth/Sources/Center/CenterRegisterLogger.swift index f873792a..f2a44606 100644 --- a/project/Projects/Presentation/Feature/Auth/Sources/Center/CenterRegisterLogger.swift +++ b/project/Projects/Presentation/Feature/Auth/Sources/Center/CenterRegisterLogger.swift @@ -7,15 +7,15 @@ import Foundation -public protocol CenterRegisterLogger { - - /// 센터 회원가입의 각단계 집입시 로깅합니다. - func logCenterRegisterStep(stepName: String, stepIndex: Int) - - /// 센터 회원가입의 시작시간을 기록합니다. - func startCenterRegister() - - /// 센터 회원가입의 완료시간을 기록하고 걸린시간을 산출합니다. - func logCenterRegisterDuration() -} +//public protocol CenterRegisterLogger { +// +// /// 센터 회원가입의 각단계 집입시 로깅합니다. +// func logCenterRegisterStep(stepName: String, stepIndex: Int) +// +// /// 센터 회원가입의 시작시간을 기록합니다. +// func startCenterRegister() +// +// /// 센터 회원가입의 완료시간을 기록하고 걸린시간을 산출합니다. +// func logCenterRegisterDuration() +//} diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Worker/Model/WorkerAccountRegisterStage.swift b/project/Projects/Presentation/Feature/Auth/Sources/Worker/Model/WorkerAccountRegisterStage.swift new file mode 100644 index 00000000..1e00156c --- /dev/null +++ b/project/Projects/Presentation/Feature/Auth/Sources/Worker/Model/WorkerAccountRegisterStage.swift @@ -0,0 +1,34 @@ +// +// WorkerAccountRegisterStage.swift +// AuthFeature +// +// Created by choijunios on 10/30/24. +// + +import Foundation + +enum WorkerAccountRegisterStage: Int { + + case start + case phoneNumber + case info + case address + case finish + + var screenKorName: String { + switch self { + case .start: + "요양보호사 회원가입 시작" + case .phoneNumber: + "전화번호 입력" + case .info: + "개인정보 입력" + case .address: + "거주지 입력" + case .finish: + "가입완료" + } + } + + var step: Int { self.rawValue } +} diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerAccountRegisterCoordinator.swift b/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerAccountRegisterCoordinator.swift index 244aa368..613ee3a2 100644 --- a/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerAccountRegisterCoordinator.swift +++ b/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerAccountRegisterCoordinator.swift @@ -6,34 +6,12 @@ // import UIKit + import BaseFeature +import Logger import PresentationCore import Core -enum WorkerAccountRegisterStage: Int { - - case registerFinished - case phoneNumber - case info - case address - case finish - - var screenName: String { - switch self { - case .registerFinished: - "" - case .phoneNumber: - "phoneNumber" - case .info: - "personalInfo" - case .address: - "address" - case .finish: - "" - } - } -} - public enum WorkerAccountRegisterCoordinatorDestination { case workerMainPage } @@ -44,7 +22,7 @@ public class WorkerAccountRegisterCoordinator: Coordinator { // Injected @Injected var router: RouterProtocol - @Injected var logger: WorkerRegisterLogger + @Injected var logger: Logger // startFlow public var startFlow: ((WorkerAccountRegisterCoordinatorDestination) -> ())! @@ -61,6 +39,12 @@ public class WorkerAccountRegisterCoordinator: Coordinator { let viewModel = WorkerRegisterViewModel() viewModel.presentCompletePage = { [weak self] in + + guard let self else { return } + + // 회원가입 완료 + logCurrentStage(stage: .finish) + let object: AnonymousCompleteVCRenderObject = .init( titleText: "요양보호사 로그인을\n완료했어요!", descriptionText: "로그인 정보는 마지막 접속일부터\n180일간 유지될 예정이에요.", @@ -71,7 +55,7 @@ public class WorkerAccountRegisterCoordinator: Coordinator { } // 완료화면 표시 - self?.router.presentAnonymousCompletePage(object) + router.presentAnonymousCompletePage(object) } viewModel.presentNextPage = { [weak self] in @@ -110,10 +94,10 @@ public class WorkerAccountRegisterCoordinator: Coordinator { self?.onFinish?() } - excuteStage(.phoneNumber, moveTo: .next) + // 회원가입 시작 + logCurrentStage(stage: .start) - // MARK: 요양보호사 회원가입 시작 ㄹ깅 - logger.startWorkerRegister() + excuteStage(.phoneNumber, moveTo: .next) } } @@ -146,19 +130,14 @@ extension WorkerAccountRegisterCoordinator { private func excuteStage(_ stage: WorkerAccountRegisterStage, moveTo: MovingDirection) { currentStage = stage switch stage { - case .registerFinished: + case .start: router.popModule(animated: true) case .finish: return default: // MARK: 화면 전환 로깅 - if moveTo == .next { - logger.logWorkerRegisterStep( - stepName: stage.screenName, - stepIndex: stage.rawValue-1 - ) - } + logCurrentStage() let vc = stageViewControllers[stage.rawValue-1] showStage(viewController: vc, moveTo: moveTo) @@ -173,6 +152,16 @@ extension WorkerAccountRegisterCoordinator { animated: true ) } + + func logCurrentStage(stage: WorkerAccountRegisterStage? = nil) { + let logObject = AccountRegisterationLogBuilder( + step: (stage ?? currentStage).step, + stepName: (stage ?? currentStage).screenKorName + ) + .build() + + logger.send(logObject) + } } // MARK: Notification for status UI diff --git a/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerRegisterLogger.swift b/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerRegisterLogger.swift deleted file mode 100644 index 1b7d8d8b..00000000 --- a/project/Projects/Presentation/Feature/Auth/Sources/Worker/WorkerRegisterLogger.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// WorkerRegisterLogger.swift -// AuthFeature -// -// Created by choijunios on 9/18/24. -// - -import Foundation - -public protocol WorkerRegisterLogger { - - /// 요양보호사 회원가입 각 화면 진입시 로깅 - func logWorkerRegisterStep(stepName: String, stepIndex: Int) - - /// 요양보호사 회원가입의 시작시간을 기록합니다. - func startWorkerRegister() - - /// 요양보호사 회원가입의 완료시간을 기록하고 걸린시간을 산출합니다. - func logWorkerRegisterDuration() -} - diff --git a/project/Projects/Presentation/Feature/Base/Project.swift b/project/Projects/Presentation/Feature/Base/Project.swift index 7c45d0da..7171d48c 100644 --- a/project/Projects/Presentation/Feature/Base/Project.swift +++ b/project/Projects/Presentation/Feature/Base/Project.swift @@ -28,11 +28,10 @@ let project = Project( resources: ["Resources/**"], dependencies: [ - // Data + // Internal D.Data.Repository, - - // Presentation D.Presentation.DSKit, + D.Module.Logger, // ThirdParty D.ThirdParty.NaverMapSDKForSPM, diff --git a/project/Projects/Presentation/Feature/CenterMainPage/Sources/Logger/PostRegisterLogger.swift b/project/Projects/Presentation/Feature/CenterMainPage/Sources/Logger/PostRegisterLogger.swift deleted file mode 100644 index c3735072..00000000 --- a/project/Projects/Presentation/Feature/CenterMainPage/Sources/Logger/PostRegisterLogger.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// PostRegisterLogger.swift -// CenterFeature -// -// Created by choijunios on 9/18/24. -// - -import Foundation - -public protocol PostRegisterLogger { - - /// 공고 등록의 각화면 진입시 로깅합니다. - func logPostRegisterStep(stepName: String, stepIndex: Int) - - /// 센터 회원가입의 시작시간을 기록합니다. - func startPostRegister() - - /// 센터 회원가입의 완료시간을 기록하고 걸린시간을 산출합니다. - func logPostRegisterDuration() -} diff --git a/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/Model/RegisterRecruitmentPage.swift b/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/Model/RegisterRecruitmentPage.swift new file mode 100644 index 00000000..5496b62c --- /dev/null +++ b/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/Model/RegisterRecruitmentPage.swift @@ -0,0 +1,72 @@ +// +// RegisterRecruitmentPage.swift +// CenterMainPageFeature +// +// Created by choijunios on 10/30/24. +// + +import Foundation + +enum RegisterRecruitmentPage: Int, CaseIterable { + case start = 0 + case workTimeAndPayment = 1 + case workPlaceAddress = 2 + case customerInformation = 3 + case customerRequirement = 4 + case additionalInfo = 5 + case overview = 6 + case editPage = 7 + case finish = 8 + + var screenKorName: String { + switch self { + case .start: + "센터 구인공고 등록 시작" + case .workTimeAndPayment: + "근무 시간 및 급여" + case .workPlaceAddress: + "근무지 주소입력" + case .customerInformation: + "수급자 정보 입력" + case .customerRequirement: + "수급자 요구사항 입력" + case .additionalInfo: + "추가 지원정보 입력" + case .overview: + "오버뷰 화면" + case .editPage: + "전체 수정화면" + case .finish: + "구인공고 등록완료" + } + } + + var step: Int { self.rawValue } + + static var stages: [RegisterRecruitmentPage] { + [ + .workTimeAndPayment, + .workPlaceAddress, + .customerInformation, + .customerRequirement, + .additionalInfo, + ] + } + + var stageIndex: Int { + switch self { + case .workTimeAndPayment: + 0 + case .workPlaceAddress: + 1 + case .customerInformation: + 2 + case .customerRequirement: + 3 + case .additionalInfo: + 4 + default: + -1 + } + } +} diff --git a/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/View/CreatePostViewController.swift b/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/View/CreatePostViewController.swift index a3cf7600..972f9ce2 100644 --- a/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/View/CreatePostViewController.swift +++ b/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePost/View/CreatePostViewController.swift @@ -7,6 +7,7 @@ import UIKit import BaseFeature +import Logger import PresentationCore import Domain import DSKit @@ -16,29 +17,6 @@ import Core import RxCocoa import RxSwift -enum RegisterRecruitmentPage: Int, CaseIterable { - case workTimeAndPayment = 0 - case workPlaceAddress = 1 - case customerInformation = 2 - case customerRequirement = 3 - case additionalInfo = 4 - - var screenName: String { - switch self { - case .workTimeAndPayment: - "workTimeAndPayment" - case .workPlaceAddress: - "workPlaceAddress" - case .customerInformation: - "customerInformation" - case .customerRequirement: - "customerRequirement" - case .additionalInfo: - "applyInformation" - } - } -} - public protocol RegisterRecruitmentPostVMBindable { func bind(viewModel: RegisterRecruitmentPostViewModelable) } @@ -57,7 +35,7 @@ public protocol RegisterRecruitmentPostViewModelable: public class CreatePostViewController: BaseViewController { - @Injected var logger: PostRegisterLogger + @Injected var logger: Logger /// 현재 스크린의 넓이를 의미합니다. private var screenWidth: CGFloat { @@ -70,7 +48,7 @@ public class CreatePostViewController: BaseViewController { private var pageViews: [RegisterRecruitmentPostViews] = [] private var pagesAreSetted = false - var currentIndex: Int = 0 + var currentStage: RegisterRecruitmentPage = .workTimeAndPayment let exitEvent: PublishSubject = .init() @@ -150,7 +128,7 @@ public class CreatePostViewController: BaseViewController { } private func createPages() { - self.pageViews = RegisterRecruitmentPage.allCases.map { page in + self.pageViews = RegisterRecruitmentPage.stages.map { page in switch page { case .workTimeAndPayment: WorkTimeAndPayView() @@ -162,6 +140,8 @@ public class CreatePostViewController: BaseViewController { CustomerRequirementView() case .additionalInfo: ApplicationDetailView(viewController: self) + default: + fatalError("센터공고등록 지정되지 않은 화면") } } } @@ -185,12 +165,6 @@ public class CreatePostViewController: BaseViewController { // 첫번째 뷰를 최상단으로 view.bringSubviewToFront(pageViews.first!) - // MARK: 로깅 - logger.logPostRegisterStep( - stepName: RegisterRecruitmentPage(rawValue: currentIndex)!.screenName, - stepIndex: currentIndex - ) - // 옵저버블 설정 let nextButtonClickedObservables = pageViews .map { $0.nextButtonClicked } @@ -220,52 +194,59 @@ public class CreatePostViewController: BaseViewController { } // 첫번째 뷰를 표시 pageViews.first?.transform = .identity + + // 로깅 + logCurrentStage() } private func next(animated: Bool = true) { - if let nextStage = RegisterRecruitmentPage(rawValue: currentIndex+1) { - - let nextIndex = nextStage.rawValue + if let nextStage = RegisterRecruitmentPage(rawValue: currentStage.rawValue+1) { - // MARK: 로깅 - logger.logPostRegisterStep( - stepName: nextStage.screenName, - stepIndex: nextIndex - ) + if nextStage == .overview { + + guard let vm = viewModel as? RegisterRecruitmentPostViewModelable else { return } + + // 오버뷰화면으로 이동 + vm.showOverView() + + return + } // Status바 이동 statusBar.moveToSignal.onNext(.next) - let prevView: UIView? = currentIndex != -1 ? pageViews[currentIndex] : nil - let willShowView = pageViews[nextIndex] + let currentStageIndex: Int = currentStage.stageIndex + let prevView: UIView? = currentStageIndex != -1 ? pageViews[currentStageIndex] : nil + let willShowView = pageViews[nextStage.stageIndex] - currentIndex = nextIndex + currentStage = nextStage + + // MARK: Logging stage + logCurrentStage() UIView.animate(withDuration: animated ? 0.35 : 0.0) { [screenWidth, prevView, willShowView] in prevView?.transform = .init(translationX: -screenWidth, y: 0) willShowView.transform = .identity } - } else { - - guard let vm = viewModel as? RegisterRecruitmentPostViewModelable else { return } - - // 오버뷰화면으로 이동 - vm.showOverView() } } private func prev(animated: Bool = true) { - if let nextIndex = RegisterRecruitmentPage(rawValue: currentIndex-1)?.rawValue { + + if let nextStage = RegisterRecruitmentPage(rawValue: currentStage.rawValue-1) { // Status바 이동 statusBar.moveToSignal.onNext(.prev) - let prevView = pageViews[currentIndex] - let willShowView = pageViews[nextIndex] + let prevView = pageViews[currentStage.stageIndex] + let willShowView = pageViews[nextStage.stageIndex] - currentIndex = nextIndex + currentStage = nextStage + + // MARK: Logging stage + logCurrentStage() UIView.animate(withDuration: animated ? 0.35 : 0.0) { [screenWidth, prevView, willShowView] in @@ -295,6 +276,16 @@ public class CreatePostViewController: BaseViewController { view.bind(viewModel: viewModel) } } + + func logCurrentStage(stage: RegisterRecruitmentPage? = nil) { + let logObject = CreatePostLogBuilder( + step: (stage ?? currentStage).step, + stepName: (stage ?? currentStage).screenKorName + ) + .build() + + logger.send(logObject) + } } protocol RegisterRecruitmentPostViews: UIView, RegisterRecruitmentPostVMBindable { diff --git a/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePostCoordinator.swift b/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePostCoordinator.swift index b5e12733..f50c89f5 100644 --- a/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePostCoordinator.swift +++ b/project/Projects/Presentation/Feature/CenterMainPage/Sources/PostRegisterPage/CreatePostCoordinator.swift @@ -9,6 +9,7 @@ import UIKit import PresentationCore import Domain import BaseFeature +import Logger import Core public class CreatePostCoordinator: Coordinator { @@ -19,7 +20,7 @@ public class CreatePostCoordinator: Coordinator { // Injected @Injected var router: RouterProtocol - @Injected var logger: PostRegisterLogger + @Injected var logger: Logger weak var presentingModule: Module? @@ -38,12 +39,15 @@ public class CreatePostCoordinator: Coordinator { self?.router.popModule(animated: true) } viewModel.presentPostOverviewPage = { [weak self] viewModel in + self?.logStage(stage: .overview) self?.presentOverviewPage(viewModel: viewModel) } viewModel.presentEditPostPage = { [weak self] viewModel in + self?.logStage(stage: .editPage) self?.presentEditPostPage(viewModel: viewModel) } viewModel.presentCompletePage = { [weak self] in + self?.logStage(stage: .finish) self?.presentCompletePage() } @@ -55,7 +59,17 @@ public class CreatePostCoordinator: Coordinator { } // MARK: 공고등록 시작 로깅 - logger.startPostRegister() + logStage(stage: .start) + } + + func logStage(stage: RegisterRecruitmentPage) { + let logObject = CreatePostLogBuilder( + step: stage.step, + stepName: stage.screenKorName + ) + .build() + + logger.send(logObject) } } diff --git a/project/Projects/Presentation/Feature/Root/Project.swift b/project/Projects/Presentation/Feature/Root/Project.swift index e06efebc..a2225750 100644 --- a/project/Projects/Presentation/Feature/Root/Project.swift +++ b/project/Projects/Presentation/Feature/Root/Project.swift @@ -26,7 +26,6 @@ let project = Project( deploymentTargets: DeploymentSettings.deployment_iOS_version, sources: [ "Sources/**", - SecretSource.amplitudeConfig, ], resources: ["Resources/**"], dependencies: [ @@ -43,7 +42,6 @@ let project = Project( D.Presentation.NotificationPageFeature, // ThirParty - D.ThirdParty.Amplitude, D.ThirdParty.FirebaseMessaging, D.ThirdParty.FirebaseRemoteConfig, D.ThirdParty.FirebaseCrashlytics diff --git a/project/Projects/Presentation/Feature/Root/Sources/Logger/OverallLogger/OverallLogger.swift b/project/Projects/Presentation/Feature/Root/Sources/Logger/OverallLogger/OverallLogger.swift deleted file mode 100644 index d6197492..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Logger/OverallLogger/OverallLogger.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// OverallLogger.swift -// ConcreteLogger -// -// Created by choijunios on 9/18/24. -// - -import Foundation - -import PresentationCore -import AuthFeature -import CenterMainPageFeature -import Domain -import Core - -public protocol OverallLogger: - CenterRegisterLogger, - WorkerRegisterLogger, - PostRegisterLogger { - -} - -public class DefaultOverallLogger { - - @Injected var publisher: LoggerMessagePublisher - - public init() { } -} - -extension DefaultOverallLogger: OverallLogger { - - // MARK: Auth - public func logCenterRegisterStep(stepName: String, stepIndex: Int) { - - let event = LoggingEvent(type: .screenView) - .addValue(.screenName, value: "center_signup") - .addValue(.actionName, value: "center_signup" + stepName) - .addValue(.step, value: stepIndex+1) - - publisher.send(event: event) - } - - public func startCenterRegister() { - publisher.startTimer( - screenName: "center_signup", - actionName: "completion_duration" - ) - } - - public func logCenterRegisterDuration() { - publisher.endTimer( - screenName: "center_signup", - actionName: "completion_duration", - isSuccess: true - ) - } - - public func logWorkerRegisterStep(stepName: String, stepIndex: Int) { - let event = LoggingEvent(type: .screenView) - .addValue(.screenName, value: "worker_signup") - .addValue(.actionName, value: "worker_signup" + stepName) - .addValue(.step, value: stepIndex+1) - - publisher.send(event: event) - } - - public func startWorkerRegister() { - publisher.startTimer( - screenName: "worker_signup", - actionName: "completion_duration" - ) - } - - public func logWorkerRegisterDuration() { - publisher.endTimer( - screenName: "worker_signup", - actionName: "completion_duration", - isSuccess: true - ) - } - - // MARK: Recruitment post - public func logPostRegisterStep(stepName: String, stepIndex: Int) { - let event = LoggingEvent(type: .screenView) - .addValue(.screenName, value: "post_job_posting") - .addValue(.actionName, value: "post_job_posting" + stepName) - .addValue(.step, value: stepIndex+1) - - publisher.send(event: event) - } - - public func startPostRegister() { - publisher.startTimer( - screenName: "post_job_posting", - actionName: "completion_duration" - ) - } - - public func logPostRegisterDuration() { - publisher.endTimer( - screenName: "post_job_posting", - actionName: "completion_duration", - isSuccess: true - ) - } -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Logger/Publisher/AmplitudeLogger.swift b/project/Projects/Presentation/Feature/Root/Sources/Logger/Publisher/AmplitudeLogger.swift deleted file mode 100644 index 65c05fa9..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Logger/Publisher/AmplitudeLogger.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// AmplitudeLogger.swift -// ConcreteLogger -// -// Created by choijunios on 9/16/24. -// - -import Foundation -import PresentationCore -import Domain - -import AmplitudeSwift - -public class AmplitudeLogger: LoggerMessagePublisher { - - public var timerDict: [String : Date] = [:] - public var timerQueue: DispatchQueue = .global(qos: .utility) - - let amplitude: Amplitude - - public init() { - - self.amplitude = Amplitude( - configuration: Configuration( - apiKey: AmplitudeConfig.apiKey - ) - ) - - // 유저 아이디 설정 - setUserId(id: getAnonymousUserId()) - } - - public func send(event: LoggingEvent) { - var eventProperties: [String: Any] = [:] - - event.properties.forEach { (key, value) in - eventProperties[key.rawValue] = value - } - - amplitude - .track(eventType: event.type.rawValue, eventProperties: eventProperties) - } - - public func setUserId(id: String) { - amplitude.setUserId(userId: id) - } -} diff --git a/project/Projects/Presentation/Feature/Root/Sources/Logger/Publisher/DebugLogger.swift b/project/Projects/Presentation/Feature/Root/Sources/Logger/Publisher/DebugLogger.swift deleted file mode 100644 index 983931b8..00000000 --- a/project/Projects/Presentation/Feature/Root/Sources/Logger/Publisher/DebugLogger.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// DebugLogger.swift -// ConcreteLogger -// -// Created by choijunios on 9/16/24. -// - -import Foundation -import PresentationCore -import Domain - -public class DebugLogger: LoggerMessagePublisher { - - public private(set) var currentUser: String? - - /// key = screenName+actionName - public var timerDict: [String: Date] = [:] - public let timerQueue = DispatchQueue.global(qos: .utility) - - public init() { } - - public func send(event: LoggingEvent) { - print(""" - [Mock Logger] - - 현재 유저id: \(currentUser ?? "정보 없음") - - 이벤트 타입: \(event.type.rawValue) - - 프로퍼티 키: - \( - { - let propList = event.properties.map({ key, value in - "\(key) : \(value)" - }).joined(separator: "\n") - - return propList - }() ?? "없음" - ) - """) - } - - public func setUserId(id: String) { - self.currentUser = id - } -} diff --git a/project/Projects/Presentation/PresentationCore/Sources/LoggerInterface/source/LoggerMessagePublisher.swift b/project/Projects/Presentation/PresentationCore/Sources/LoggerInterface/source/LoggerMessagePublisher.swift deleted file mode 100644 index 863290f4..00000000 --- a/project/Projects/Presentation/PresentationCore/Sources/LoggerInterface/source/LoggerMessagePublisher.swift +++ /dev/null @@ -1,100 +0,0 @@ -// -// LoggerMessagePublisher.swift -// LoggerInterface -// -// Created by choijunios on 9/16/24. -// - -import Foundation -import Domain - - -public protocol LoggerMessagePublisher: AnyObject { - - var timerDict: [String: Date] { get set } - var timerQueue: DispatchQueue { get } - - /// 이벤트를 로깅합니다. - func send(event: LoggingEvent) - - /// 이벤트의 주체를 설정합니다. - func setUserId(id: String) -} - -public extension LoggerMessagePublisher { - - /// 특정 스크린 진입 이벤트를 로깅합니다. - func sendScreen(screenName: String) { - let event = LoggingEvent(type: .screenView) - .addValue(.screenName, value: screenName) - - send(event: event) - } - - /// 특정버튼 클릭이벤트를 로깅합니다. - func sendButtonClick(screenName: String, buttonId: String) { - let event = LoggingEvent(type: .buttonClick) - .addValue(.screenName, value: screenName) - .addValue(.buttonId, value: buttonId) - - send(event: event) - } - - /// 이벤트의 시작시간을 알립니다. - func startTimer(screenName: String, actionName: String) { - timerQueue.sync { - let key = screenName + actionName - timerDict[key] = Date() - } - } - - /// 이벤트의 종료를 알리며, 지속시간을 기록합니다. - func endTimer(screenName: String, actionName: String, isSuccess: Bool) { - timerQueue.sync { - let key = screenName + actionName - var duration: Int = -1 - - if let startTime = timerDict.removeValue(forKey: key) { - - let endTime = Date() - - duration = Calendar.current.dateComponents([.second], from: startTime, to: endTime).second ?? -1 - } - - self.logActionDuration( - screenName: screenName, - actionName: actionName, - isSuccess: isSuccess, - duration: Double(duration) - ) - } - } - - /// 특정 동작의 지속시간을 로깅합니다. - private func logActionDuration(screenName: String, actionName: String, isSuccess: Bool, duration: Double) { - let event = LoggingEvent(type: .action) - .addValue(.screenName, value: screenName) - .addValue(.actionName, value: actionName) - .addValue(.actionResult, value: isSuccess) - .addValue(.duration, value: duration) - - send(event: event) - } - - /// 피로깅의 주체를 인식하는 식별자를 반환합니다. - func getAnonymousUserId() -> String { - - let key = "AnonymousUserIdForDevice" - - if let cachedId = UserDefaults.standard.string(forKey: key) { - - return cachedId - } - - let id = UUID().uuidString - - UserDefaults.standard.setValue(id, forKey: key) - - return id - } -} diff --git a/project/graph.png b/project/graph.png index 0a11ca7f..a9aab56b 100644 Binary files a/project/graph.png and b/project/graph.png differ