Skip to content

[Feature] 카테고리 로깅 추가#1534

Open
JaeYoung290 wants to merge 2 commits into
developfrom
feature/#1496-category-logging
Open

[Feature] 카테고리 로깅 추가#1534
JaeYoung290 wants to merge 2 commits into
developfrom
feature/#1496-category-logging

Conversation

@JaeYoung290

@JaeYoung290 JaeYoung290 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

PR 개요

PR 체크리스트

  • Code convention을 잘 지켰나요?
  • Lint check를 수행하였나요?
  • Assignees를 추가했나요?

작업사항

  • 버그 수정
  • 신규 기능
  • 코드 스타일 수정 (포맷팅 등)
  • 리팩토링 (기능 수정 X, API 수정 X)
  • 기타

작업사항의 상세한 설명

  • 카테고리 로깅을 추가했습니다.

논의 사항

스크린샷

추가내용

  • develop, sprint 브랜치를 향하고 있습니다
  • production 브랜치를 향하고 있습니다

Summary by CodeRabbit

  • 새 기능
    • 시간표, 분실물, 캠퍼스, 교통 등 카테고리별 메뉴 선택 이벤트를 기록합니다.
    • 카테고리 메뉴를 선택하면 해당 항목에 맞는 분석 정보가 자동으로 분류됩니다.

@JaeYoung290
JaeYoung290 requested a review from a team as a code owner July 16, 2026 12:24
@JaeYoung290 JaeYoung290 self-assigned this Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

카테고리 분석용 상수가 추가되었으며, CategoryViewModel은 메뉴 클릭 시 항목별 분석 이벤트를 기록한 뒤 메뉴 이동을 수행하도록 변경되었습니다.

Changes

카테고리 분석 이벤트

Layer / File(s) Summary
카테고리 분석 상수 정의
core/analytics/.../AnalyticsConstant.kt
카테고리 분석에 사용할 5개의 문자열 상수가 AnalyticsConstant.Category에 추가되었습니다.
카테고리 클릭 로깅 연결
feature/category/.../CategoryViewModel.kt
CategoryMenuId별 분석 라벨과 값을 매핑하고, 메뉴 이동 전에 EventLogger.logCampusClickEvent를 호출하도록 변경되었습니다.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested labels: ci:passed

Suggested reviewers: kongwoojin, kym-p

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 카테고리 메뉴 클릭 로깅 추가라는 주요 변경 사항을 잘 요약한 제목입니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#1496-category-logging

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

description = stringResource(R.string.timetable_description),
onClick = { onMenuClick(CategoryMenuId.TIMETABLE) }
onClick = {
EventLogger.logCampusClickEvent(AnalyticsConstant.Label.Category.CATEGORY_TIMETABLE, "시간표")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] Clean Architecture 위반 — Composable에서 직접 애널리틱스 호출

현재 EventLogger.logCampusClickEvent()가 Composable 내부 onClick 람다에서 직접 호출되고 있습니다. 이 패턴은 세 가지 문제를 일으킵니다:

  1. Clean Architecture 위반: UI 레이어가 애널리틱스 부수 효과(side effect)를 직접 처리합니다. 이미 onMenuClick 콜백이 ViewModel로 이벤트를 전달하는 구조인데, 애널리틱스만 Composable에 남아 책임이 분산됩니다.
  2. 테스트 불가: EventLogger는 싱글턴 객체이므로 단위 테스트에서 모킹이 불가능합니다. ViewModel에서 처리하면 EventLogger를 인터페이스로 교체해 테스트할 수 있습니다.
  3. 패턴 불일치: 이 프로젝트는 Orbit MVI를 사용하며, 모든 부수 효과는 ViewModel의 intent { } 블록에서 처리해야 합니다.

개선안 — CategoryViewModel.onMenuClick으로 로깅 이동:

// CategoryViewModel.kt
fun onMenuClick(id: CategoryMenuId) = intent {
    logCategoryAnalytics(id)
    postSideEffect(CategorySideEffect.NavigateToMenu(id))
}

private fun logCategoryAnalytics(id: CategoryMenuId) {
    val (label, value) = when (id) {
        CategoryMenuId.TIMETABLE        -> AnalyticsConstant.Label.Category.CATEGORY_TIMETABLE      to "시간표"
        CategoryMenuId.LOST_AND_FOUND   -> AnalyticsConstant.Label.Category.CATEGORY_LOST_PROPERTY  to "분실물"
        CategoryMenuId.OPERATING_INFO   -> AnalyticsConstant.Label.Category.CATEGORY_CAMPUS         to "교내 시설물 정보"
        CategoryMenuId.DINING           -> AnalyticsConstant.Label.Category.CATEGORY_CAMPUS         to "식단"
        CategoryMenuId.STORE            -> AnalyticsConstant.Label.Category.CATEGORY_CAMPUS         to "주변상점"
        CategoryMenuId.BUS_TIMETABLE    -> AnalyticsConstant.Label.Category.CATEGORY_TRANSPORTATION to "버스 시간표"
        CategoryMenuId.TRANSPORT_SEARCH -> AnalyticsConstant.Label.Category.CATEGORY_TRANSPORTATION to "교통편 조회하기"
        CategoryMenuId.CALLVAN          -> AnalyticsConstant.Label.Category.CATEGORY_TRANSPORTATION to "콜벤팟 모집"
        CategoryMenuId.HOUSING          -> AnalyticsConstant.Label.Category.CATEGORY_ETC            to "복덕방"
        CategoryMenuId.KOIN_BUSINESS    -> AnalyticsConstant.Label.Category.CATEGORY_ETC            to "코인 for Business"
    }
    EventLogger.logCampusClickEvent(label, value)
}

이렇게 하면:

  • Composable의 onClickonMenuClick(CategoryMenuId.TIMETABLE)만 호출하면 됩니다.
  • CategoryMenuId에서 label과 value를 모두 유도할 수 있어 UI가 어느 섹션에 있는지 신경 쓸 필요가 없습니다.
  • when 식이 exhaustive해져 새 enum 값 추가 시 컴파일 오류로 즉시 알 수 있습니다.

CategoryMenuId.CALLVAN -> "콜벤팟 모집"
CategoryMenuId.HOUSING -> "복덕방"
CategoryMenuId.KOIN_BUSINESS -> "코인 for Business"
else -> null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] enum whenelse -> null 사용 — 컴파일 타임 안전성 소실

CategoryMenuId는 enum class이므로, when 식에서 else를 사용하면 새 enum 값이 추가되거나 주석 처리된 DEPARTMENT_INFO가 해제될 때 컴파일러가 경고를 발생시키지 않습니다. 새 메뉴 ID가 조용히 null을 반환해 애널리틱스 이벤트가 무음으로 누락됩니다.

현재 TIMETABLE, LOST_AND_FOUND가 이 함수에 없어 else로 처리되고 있는데, 이를 명시적으로 나열하면 컴파일 타임 안전성을 확보할 수 있습니다.

Suggested change
else -> null
CategoryMenuId.TIMETABLE, CategoryMenuId.LOST_AND_FOUND -> null

CategoryMenu.kt의 주석 처리된 DEPARTMENT_INFO가 활성화될 경우에도, 명시적 exhaustive when이라면 컴파일 에러가 즉시 발생해 누락된 로깅을 사전에 방지할 수 있습니다.

items = CAMPUS_MENUS,
onItemClick = { menu -> onMenuClick(menu.id) }
onItemClick = { menu ->
categoryMenuLogValue(menu.id)?.let { EventLogger.logCampusClickEvent(AnalyticsConstant.Label.Category.CATEGORY_CAMPUS, it) }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor] ?.let으로 인한 묵음 누락 — 로깅 실패가 드러나지 않음

categoryMenuLogValuenull을 반환하면 ?.let 블록 전체가 조용히 건너뛰어져 애널리틱스 이벤트가 발송되지 않습니다. 새 CategoryMenuIdCAMPUS_MENUS에 추가되고 categoryMenuLogValue에 매핑되지 않았을 경우, 로그도 없이 지나쳐 버립니다.

가장 근본적인 해결은 상위 Major 코멘트처럼 ViewModel로 이동해 exhaustive when으로 처리하는 것입니다. 현재 구조를 유지해야 한다면, 누락 감지를 위해 최소한 다음처럼 경고를 남기는 것을 권장합니다:

onItemClick = { menu ->
    val logValue = categoryMenuLogValue(menu.id)
    if (logValue != null) {
        EventLogger.logCampusClickEvent(AnalyticsConstant.Label.Category.CATEGORY_CAMPUS, logValue)
    } else {
        Timber.w("categoryMenuLogValue: unhandled CategoryMenuId = ${menu.id}")
    }
    onMenuClick(menu.id)
}

description = stringResource(R.string.lost_and_found_description),
onClick = { onMenuClick(CategoryMenuId.LOST_AND_FOUND) }
onClick = {
EventLogger.logCampusClickEvent(AnalyticsConstant.Label.Category.CATEGORY_LOST_PROPERTY, "분실물")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor] TIMETABLE / LOST_AND_FOUND 로깅 패턴 불일치

TIMETABLE(line 124)과 LOST_AND_FOUND(이 줄)은 하드코딩된 문자열 "분실물"onClick 람다 안에서 직접 사용하는 반면, 나머지 메뉴 항목들은 categoryMenuLogValue()를 통해 매핑됩니다. 두 가지 방식이 혼용되어 향후 유지보수 시 수정 누락이 발생할 수 있습니다.

상위 Major 코멘트의 개선안(ViewModel로 통합)을 따르면 이 불일치도 자연스럽게 해소됩니다.

Comment on lines +221 to +227
object Category {
const val CATEGORY_TIMETABLE = "category_timetable"
const val CATEGORY_LOST_PROPERTY = "category_lost_property"
const val CATEGORY_CAMPUS = "category_campus"
const val CATEGORY_TRANSPORTATION = "category_transportation"
const val CATEGORY_ETC = "category_etc"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Info] 잘 작성된 코드 — Category 상수 구조

Label.Category nested object를 별도로 분리한 구조가 깔끔합니다. 기존 Label의 flat 상수들과 섞이지 않아 가독성이 좋고, 향후 카테고리 관련 이벤트가 늘어날 때 확장하기 용이한 구조입니다. 👍

@sonarqubecloud

Copy link
Copy Markdown

@bcsd-b-bot bcsd-b-bot Bot added the ci:passed label Jul 16, 2026

@KYM-P KYM-P left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GOOD 추가적으로 해당 부분에 로깅 event 추가해주세요

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants