Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c24a983
[chore] #192 core:analytics 모듈 생성
Ojongseok Apr 17, 2026
f68de49
[chore] #192 Firebase 애널리틱스 로깅 인터페이스 작성
Ojongseok Apr 17, 2026
1301d2e
[chore] #192 AnalyticsModule 생성
Ojongseok Apr 17, 2026
c6c0aee
Merge branch 'develop' of https://github.com/YAPP-Github/27th-App-Tea…
Ojongseok Apr 18, 2026
b314f80
[chore] #192 guava 라이브러리 추가
Ojongseok Apr 18, 2026
8a969fa
[chore] #192 feature:photo-upload 모듈의 guava 의존성 추가
Ojongseok Apr 18, 2026
cbf7ff0
[chore] #192 Feature 플러그인 내 analytics 공통 의존성 추가
Ojongseok Apr 18, 2026
08e7f2f
[feat] #192 네컷지도 최대 줌레벨 설정 및 기본 줌레벨 변경
Ojongseok Apr 19, 2026
c98fe91
[chore] #192 AnalyticsEvent 이벤트 정의
Ojongseok Apr 19, 2026
a87f994
[chore] #192 AnalyticsEvent 이벤트 로깅 구문 추가
Ojongseok Apr 19, 2026
e256ba7
[chore] #192 logger 디렉토리 생성 및 경로 이동
Ojongseok Apr 19, 2026
53ee082
[chore] #192 Feature 단위 이벤트 분리
Ojongseok Apr 19, 2026
0263996
[chore] #192 AnalyticsEvent 경로 변경에 따른 import 수정
Ojongseok Apr 19, 2026
8b9f555
[feat] #192 네컷지도 최대 줌 레벨 14.0으로 변경
Ojongseok Apr 19, 2026
fbb61e9
[chore] #192 detekt trailing comma 및 unused import 수정
Ojongseok Apr 19, 2026
fc269fd
[feat] #192 이벤트별 로깅 파라미터 타입 수정
Ojongseok Apr 19, 2026
7fee14d
[chore] #192 MapBrandFilterToggle 이벤트 로깅 reduce 구문 외부로 이동
Ojongseok Apr 19, 2026
482fced
[chore] #192 사진 복제 이벤트 로깅 구문 수정
Ojongseok Apr 19, 2026
73d55b1
[chore] #192 UserProperty 로깅 구문 위치 변경
Ojongseok Apr 19, 2026
437c38e
[chore] #192 로그아웃 및 회원 탈퇴 이벤트 추가
Ojongseok Apr 19, 2026
ef3ad4d
[chore] #192 resetAnalytics() 로깅 구문 제거
Ojongseok Apr 19, 2026
ce2de75
[chore] #192 detekt 룰 설정
Ojongseok Apr 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ dependencies {
implementation(projects.core.model)
implementation(projects.core.navigation)
implementation(projects.core.ui)
implementation(projects.core.analytics)
implementation(projects.feature.auth.api)
implementation(projects.feature.auth.impl)
implementation(projects.feature.pose.api)
Expand All @@ -89,7 +90,6 @@ dependencies {
implementation(libs.timber)

implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
implementation(libs.firebase.crashlytics)

implementation(libs.androidx.activity.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class AndroidFeatureImplConventionPlugin : Plugin<Project> {
"implementation"(project(":core:common"))
"implementation"(project(":core:domain"))
"implementation"(project(":core:ui"))
"implementation"(project(":core:analytics"))

"implementation"(libs.findLibrary("androidx.hilt.lifecycle.viewModel.compose").get())
}
Expand Down
13 changes: 13 additions & 0 deletions core/analytics/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
alias(libs.plugins.neki.android.library)
alias(libs.plugins.neki.hilt)
}

android {
namespace = "com.neki.android.core.analytics"
}

dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.neki.android.core.analytics.event

sealed interface AnalyticsEvent {

val name: String
val params: Map<String, Any>
get() = emptyMap()
}
Comment thread
Ojongseok marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.neki.android.core.analytics.event

sealed interface ArchiveAnalyticsEvent : AnalyticsEvent {

data object ArchivingView : ArchiveAnalyticsEvent {
override val name = "archiving_view"
}

data class PhotoUpload(val method: String, val count: Int) : ArchiveAnalyticsEvent {
override val name = "photo_upload"
override val params = mapOf(
"method" to method,
"count" to count,
)
}

data object AlbumCreate : ArchiveAnalyticsEvent {
override val name = "album_create"
}

data class AlbumAddFromDetail(val albumCount: Int) : ArchiveAnalyticsEvent {
override val name = "album_add_from_detail"
override val params = mapOf("album_count" to albumCount)
}

data class AlbumAddFromMulti(val photoCount: Int, val albumCount: Int) : ArchiveAnalyticsEvent {
override val name = "album_add_from_multi"
override val params = mapOf(
"photo_count" to photoCount,
"album_count" to albumCount,
)
}

data object PhotoMove : ArchiveAnalyticsEvent {
override val name = "photo_move"
}

data object PhotoCopy : ArchiveAnalyticsEvent {
override val name = "photo_copy"
}

data object PhotoDetailView : ArchiveAnalyticsEvent {
override val name = "photo_detail_view"
}

data object PhotoMemoCreate : ArchiveAnalyticsEvent {
override val name = "photo_memo_create"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.neki.android.core.analytics.event

sealed interface GlobalAnalyticsEvent : AnalyticsEvent {

data object AppOpen : GlobalAnalyticsEvent {
override val name = "app_open"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.neki.android.core.analytics.event

sealed interface MapAnalyticsEvent : AnalyticsEvent {

data object MapView : MapAnalyticsEvent {
override val name = "map_view"
}

data class MapReSearch(val hasFilter: Boolean, val regionChanged: Boolean) : MapAnalyticsEvent {
override val name = "map_re_search"
override val params = mapOf(
"has_filter" to hasFilter,
"region_changed" to regionChanged,
)
}

data class MapBrandFilterToggle(
val action: String,
val selectedCount: Int,
val brandName: String,
) : MapAnalyticsEvent {
override val name = "map_brand_filter_toggle"
override val params = mapOf(
"action" to action,
"selected_count" to selectedCount,
"brand_name" to brandName,
)
}

data class BoothSelect(val entryPoint: String, val brandName: String) : MapAnalyticsEvent {
override val name = "booth_select"
override val params = mapOf(
"entry_point" to entryPoint,
"brand_name" to brandName,
)
}

data class MapRouteClick(val mapType: String) : MapAnalyticsEvent {
override val name = "map_route_click"
override val params = mapOf("map_type" to mapType)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.neki.android.core.analytics.event

sealed interface MypageAnalyticsEvent : AnalyticsEvent {

data object Logout : MypageAnalyticsEvent {
override val name = "mypage_logout"
}

data object Withdraw : MypageAnalyticsEvent {
override val name = "mypage_withdraw"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.neki.android.core.analytics.event

sealed interface PoseAnalyticsEvent : AnalyticsEvent {

data object PoseView : PoseAnalyticsEvent {
override val name = "pose_view"
}

data object PoseRandomStart : PoseAnalyticsEvent {
override val name = "pose_random_start"
}

data class PoseRandomSessionEnd(val totalSwipeCount: Int) : PoseAnalyticsEvent {
override val name = "pose_random_session_end"
override val params = mapOf("total_swipe_count" to totalSwipeCount)
}

data class PoseFilterToggle(val peopleCount: Int) : PoseAnalyticsEvent {
override val name = "pose_filter_toggle"
override val params = mapOf("people_count" to peopleCount)
}

data object PoseBookmarkFilter : PoseAnalyticsEvent {
override val name = "pose_bookmark_filter"
}

data object PoseBookmark : PoseAnalyticsEvent {
override val name = "pose_bookmark"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.neki.android.core.analytics.logger

import com.neki.android.core.analytics.event.AnalyticsEvent

interface AnalyticsLogger {
fun log(event: AnalyticsEvent)
fun setUserId(userId: String)
fun setUserProperty(key: String, value: String)
}
Comment thread
Ojongseok marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.neki.android.core.analytics.logger

import android.content.Context
import com.google.firebase.analytics.FirebaseAnalytics
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
internal abstract class AnalyticsModule {

@Binds
@Singleton
abstract fun bindAnalyticsLogger(impl: FirebaseAnalyticsLogger): AnalyticsLogger

companion object {
@Provides
@Singleton
fun provideFirebaseAnalytics(
@ApplicationContext context: Context,
): FirebaseAnalytics = FirebaseAnalytics.getInstance(context)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.neki.android.core.analytics.logger

import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
import com.neki.android.core.analytics.event.AnalyticsEvent
import javax.inject.Inject

internal class FirebaseAnalyticsLogger @Inject constructor(
private val firebaseAnalytics: FirebaseAnalytics,
) : AnalyticsLogger {

override fun log(event: AnalyticsEvent) {
val bundle = Bundle().apply {
event.params.forEach { (key, value) ->
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Double -> putDouble(key, value)
is Boolean -> putBoolean(key, value)
else -> putString(key, value.toString())
}
}
}
Comment thread
Ojongseok marked this conversation as resolved.
firebaseAnalytics.logEvent(event.name, bundle)
}

override fun setUserId(userId: String) {
firebaseAnalytics.setUserId(userId)
}

override fun setUserProperty(key: String, value: String) {
firebaseAnalytics.setUserProperty(key, value)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.neki.android.feature.archive.impl.album

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.neki.android.core.analytics.event.ArchiveAnalyticsEvent
import com.neki.android.core.analytics.logger.AnalyticsLogger
import com.neki.android.core.dataapi.repository.FolderRepository
import com.neki.android.core.dataapi.repository.PhotoRepository
import com.neki.android.core.model.AlbumPreview
Expand All @@ -22,6 +24,7 @@ import javax.inject.Inject
class AllAlbumViewModel @Inject constructor(
private val photoRepository: PhotoRepository,
private val folderRepository: FolderRepository,
private val analyticsLogger: AnalyticsLogger,
) : ViewModel() {

val store: MviIntentStore<AllAlbumState, AllAlbumIntent, AllAlbumSideEffect> =
Expand Down Expand Up @@ -182,6 +185,7 @@ class AllAlbumViewModel @Inject constructor(
viewModelScope.launch {
folderRepository.createFolder(name = albumName)
.onSuccess {
analyticsLogger.log(ArchiveAnalyticsEvent.AlbumCreate)
fetchFolders(reduce)
postSideEffect(AllAlbumSideEffect.ShowToastMessage("새로운 앨범을 추가했어요"))
postSideEffect(AllAlbumSideEffect.NotifyResult)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import androidx.paging.PagingData
import androidx.paging.cachedIn
import androidx.paging.filter
import androidx.paging.map
import com.neki.android.core.analytics.event.ArchiveAnalyticsEvent
import com.neki.android.core.analytics.logger.AnalyticsLogger
import com.neki.android.core.dataapi.repository.FolderRepository
import com.neki.android.core.dataapi.repository.PhotoRepository
import com.neki.android.core.domain.usecase.UploadMultiplePhotoUseCase
Expand Down Expand Up @@ -41,6 +43,7 @@ class AlbumDetailViewModel @AssistedInject constructor(
private val photoRepository: PhotoRepository,
private val folderRepository: FolderRepository,
private val uploadMultiplePhotoUseCase: UploadMultiplePhotoUseCase,
private val analyticsLogger: AnalyticsLogger,
) : ViewModel() {

@AssistedFactory
Expand Down Expand Up @@ -245,7 +248,7 @@ class AlbumDetailViewModel @AssistedInject constructor(
}
postSideEffect(
AlbumDetailSideEffect.NavigateToSelectAlbum(
SelectAlbumAction.CopyPhotos(photoIds = photoIds),
SelectAlbumAction.CopyPhotos(photoIds = photoIds, fromPhotoDetail = false),
),
)
}
Expand Down Expand Up @@ -310,6 +313,7 @@ class AlbumDetailViewModel @AssistedInject constructor(
photoIds = state.importPhotoState.selectedPhotoIds.toList(),
targetFolderIds = listOf(albumId),
).onSuccess {
analyticsLogger.log(ArchiveAnalyticsEvent.PhotoCopy)
Comment thread
Ojongseok marked this conversation as resolved.
reduce { copy(isShowImportPhotoBottomSheet = false, importPhotoState = ImportPhotoState()) }
_importAlbumFilter.value = null
postSideEffect(AlbumDetailSideEffect.ShowToastMessage("사진을 앨범에 추가했어요"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridItemSpan
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -60,6 +61,10 @@ internal fun ArchiveMainRoute(
val lazyState = rememberLazyStaggeredGridState()
val nekiToast = remember { NekiToast(context) }

LaunchedEffect(Unit) {
viewModel.logArchivingView()
}
Comment thread
Ojongseok marked this conversation as resolved.

viewModel.store.sideEffects.collectWithLifecycle { sideEffect ->
when (sideEffect) {
ArchiveMainSideEffect.NavigateToQRScan -> navigateToQRScan()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.neki.android.feature.archive.impl.main
import androidx.compose.foundation.text.input.TextFieldState
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.neki.android.core.analytics.event.ArchiveAnalyticsEvent
import com.neki.android.core.analytics.logger.AnalyticsLogger
import com.neki.android.core.dataapi.repository.FolderRepository
import com.neki.android.core.dataapi.repository.PhotoRepository
import com.neki.android.core.dataapi.repository.UserRepository
Expand All @@ -26,6 +28,7 @@ class ArchiveMainViewModel @Inject constructor(
private val photoRepository: PhotoRepository,
private val folderRepository: FolderRepository,
private val userRepository: UserRepository,
private val analyticsLogger: AnalyticsLogger,
) : ViewModel() {

val store: MviIntentStore<ArchiveMainState, ArchiveMainIntent, ArchiveMainSideEffect> =
Expand All @@ -39,6 +42,10 @@ class ArchiveMainViewModel @Inject constructor(
store.onIntent(ArchiveMainIntent.EnterArchiveMainScreen)
}

fun logArchivingView() {
analyticsLogger.log(ArchiveAnalyticsEvent.ArchivingView)
}

private fun onIntent(
intent: ArchiveMainIntent,
state: ArchiveMainState,
Expand Down Expand Up @@ -173,6 +180,7 @@ class ArchiveMainViewModel @Inject constructor(
viewModelScope.launch {
folderRepository.createFolder(name = albumName)
.onSuccess {
analyticsLogger.log(ArchiveAnalyticsEvent.AlbumCreate)
fetchFolders(reduce)
postSideEffect(ArchiveMainSideEffect.ShowToastMessage("새로운 앨범을 추가했어요"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private fun EntryProviderScope<NavKey>.archiveEntry(navigator: MainNavigator) {
navigateToPhotoDetail = navigator::navigateToPhotoDetail,
navigateToSelectAlbum = { photoIds ->
navigator.navigateToSelectAlbum(
action = SelectAlbumAction.CopyPhotos(photoIds = photoIds),
action = SelectAlbumAction.CopyPhotos(photoIds = photoIds, fromPhotoDetail = false),
title = "앨범에 추가",
multiSelect = true,
)
Expand Down Expand Up @@ -189,7 +189,7 @@ private fun EntryProviderScope<NavKey>.archiveEntry(navigator: MainNavigator) {
navigateBack = navigator::goBack,
navigateToSelectAlbum = { photoId ->
navigator.navigateToSelectAlbum(
action = SelectAlbumAction.CopyPhotos(listOf(photoId), false),
action = SelectAlbumAction.CopyPhotos(photoIds = listOf(photoId), fromPhotoDetail = true),
title = "모든 앨범",
multiSelect = false,
)
Expand Down
Loading
Loading