Skip to content

Commit 2a483d1

Browse files
authored
[#453] Firebase Analytics를 적용한다 (#498)
* feat: FirebaseAnalyticsCore 의존성 추가 * feat: Analytics 계층 구성 * feat: 화면 분석 연결 * feat: 생성 이벤트 및 Todo 완료 분석 연결 * feat: 푸시 오픈 분석 연결 * test: 웹페이지 삭제 테스트 보정 * chore: 자동 화면 추적 비활성화 * refactor: handler에 MainActor 추가 Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * refactor: handler에서 MainActor 제거
1 parent aa46061 commit 2a483d1

27 files changed

Lines changed: 279 additions & 10 deletions

Application/DevLogApp/Sources/App/Assembler/AppLayerAssembler.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,10 @@ final class AppLayerAssembler: Assembler {
3131
userService: container.resolve(UserService.self)
3232
)
3333
}
34+
container.register(PushNotificationOpenHandler.self) {
35+
PushNotificationOpenHandler(
36+
trackAnalyticsEventUseCase: container.resolve(TrackAnalyticsEventUseCase.self)
37+
)
38+
}
3439
}
3540
}

Application/DevLogApp/Sources/App/Delegate/AppDelegate.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
5252

5353
// 앱이 완전 종료되어도, 알림을 통해 앱이 시작된 경우 처리
5454
if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable: Any] {
55+
let handler = container.resolve(PushNotificationOpenHandler.self)
5556
Task { @MainActor in
56-
PushNotificationRoute.shared.handlePushTap(userInfo: remoteNotification)
57+
handler.handlePushOpen(userInfo: remoteNotification)
5758
}
5859
}
5960

@@ -110,8 +111,9 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
110111
) {
111112
logger.info("Tapped notification: \(response.notification.request.content.userInfo)")
112113
let userInfo = response.notification.request.content.userInfo
114+
let handler = container.resolve(PushNotificationOpenHandler.self)
113115
Task { @MainActor in
114-
PushNotificationRoute.shared.handlePushTap(userInfo: userInfo)
116+
handler.handlePushOpen(userInfo: userInfo)
115117
}
116118
completionHandler()
117119
}

Application/DevLogApp/Sources/App/DevLogApp.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct DevLogApp: App {
2727
sessionUseCase: container.resolve(ObserveAuthSessionUseCase.self),
2828
networkConnectivityUseCase: container.resolve(ObserveNetworkConnectivityUseCase.self),
2929
systemThemeUseCase: container.resolve(ObserveSystemThemeUseCase.self),
30+
trackAnalyticsEventUseCase: container.resolve(TrackAnalyticsEventUseCase.self),
3031
widgetURLTab: { MainTab(widgetURL: $0) },
3132
pushNotificationTodoIdPublisher: PushNotificationRoute.shared.observe(),
3233
clearPushNotificationRoute: { PushNotificationRoute.shared.clear() }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// PushNotificationOpenHandler.swift
3+
// DevLog
4+
//
5+
// Created by opfic on 5/28/26.
6+
//
7+
8+
import Foundation
9+
import DevLogDomain
10+
11+
final class PushNotificationOpenHandler {
12+
private let trackAnalyticsEventUseCase: TrackAnalyticsEventUseCase
13+
14+
init(trackAnalyticsEventUseCase: TrackAnalyticsEventUseCase) {
15+
self.trackAnalyticsEventUseCase = trackAnalyticsEventUseCase
16+
}
17+
18+
func handlePushOpen(userInfo: [AnyHashable: Any]) {
19+
trackAnalyticsEventUseCase.execute(.pushOpen)
20+
PushNotificationRoute.shared.handlePushTap(userInfo: userInfo)
21+
}
22+
}

Application/DevLogApp/Sources/Resource/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
</array>
2121
<key>FirebaseAppDelegateProxyEnabled</key>
2222
<false/>
23+
<key>FirebaseAutomaticScreenReportingEnabled</key>
24+
<false/>
2325
<key>GIDClientID</key>
2426
<string>$(CLIENT_ID)</string>
2527
<key>GITHUB_CLIENT_ID</key>

Application/DevLogData/Sources/DataAssembler.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ public final class DataAssembler: Assembler {
7979
UserDataRepositoryImpl(userService: container.resolve(UserService.self))
8080
}
8181

82+
container.register(AnalyticsRepository.self) {
83+
AnalyticsRepositoryImpl(
84+
analyticsService: container.resolve(AnalyticsService.self)
85+
)
86+
}
87+
8288
container.register(PushNotificationRepository.self) {
8389
PushNotificationRepositoryImpl(
8490
pushNotificationService: container.resolve(PushNotificationService.self),
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// AnalyticsService.swift
3+
// DevLogData
4+
//
5+
// Created by opfic on 5/27/26.
6+
//
7+
8+
public protocol AnalyticsService {
9+
func trackScreenView(_ name: String)
10+
func trackTodoCreate()
11+
func trackTodoComplete()
12+
func trackWebPageCreate()
13+
func trackPushOpen()
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// AnalyticsRepositoryImpl.swift
3+
// DevLogData
4+
//
5+
// Created by opfic on 5/27/26.
6+
//
7+
8+
import DevLogDomain
9+
10+
final class AnalyticsRepositoryImpl: AnalyticsRepository {
11+
private let analyticsService: AnalyticsService
12+
13+
init(analyticsService: AnalyticsService) {
14+
self.analyticsService = analyticsService
15+
}
16+
17+
func track(_ event: AnalyticsEvent) {
18+
switch event {
19+
case .screenView(let name):
20+
analyticsService.trackScreenView(name)
21+
case .todoCreate:
22+
analyticsService.trackTodoCreate()
23+
case .todoComplete:
24+
analyticsService.trackTodoComplete()
25+
case .webPageCreate:
26+
analyticsService.trackWebPageCreate()
27+
case .pushOpen:
28+
analyticsService.trackPushOpen()
29+
}
30+
}
31+
}

Application/DevLogDomain/Sources/DomainAssembler.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public final class DomainAssembler: Assembler {
1111
public init() { }
1212

1313
public func assemble(_ container: any DIContainer) {
14+
registerAnalyticsUseCases(container)
1415
registerAuthUseCases(container)
1516
registerConnectivityUseCases(container)
1617
registerAuthProviderUseCases(container)
@@ -24,6 +25,12 @@ public final class DomainAssembler: Assembler {
2425
}
2526

2627
private extension DomainAssembler {
28+
func registerAnalyticsUseCases(_ container: any DIContainer) {
29+
container.register(TrackAnalyticsEventUseCase.self) {
30+
TrackAnalyticsEventUseCaseImpl(container.resolve(AnalyticsRepository.self))
31+
}
32+
}
33+
2734
func registerAuthUseCases(_ container: any DIContainer) {
2835
container.register(SignInUseCase.self) {
2936
SignInUseCaseImpl(container.resolve(AuthenticationRepository.self))
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// AnalyticsEvent.swift
3+
// DevLogDomain
4+
//
5+
// Created by opfic on 5/28/26.
6+
//
7+
8+
public enum AnalyticsEvent {
9+
case screenView(String)
10+
case todoCreate
11+
case todoComplete
12+
case webPageCreate
13+
case pushOpen
14+
}

0 commit comments

Comments
 (0)