Skip to content

Feat/#38 myinterststudy 구현#39

Merged
starshape7 merged 4 commits intodevelopfrom
feat/#38-myinterststudy-구현
Jan 22, 2026

Hidden character warning

The head ref may contain hidden characters: "feat/#38-myinterststudy-\uad6c\ud604"
Merged

Feat/#38 myinterststudy 구현#39
starshape7 merged 4 commits intodevelopfrom
feat/#38-myinterststudy-구현

Conversation

@starshape7
Copy link
Collaborator

@starshape7 starshape7 commented Jan 22, 2026

Related issue 🛠

Work Description 📝

  • 관심사 스터디 페이지 구현

Screenshot 📸

image image

Uncompleted Tasks 😅

PR Point 📌

트러블 슈팅 💥

Summary by CodeRabbit

  • 새로운 기능

    • 관심사 기반 스터디 검색 및 브라우징 추가
    • 선호 카테고리 탭, 무한 스크롤 목록, 정렬 및 필터(모집 상태·활동 유형·활동비) UI 추가
    • 필터 화면(적용/초기화) 및 정렬 바텀시트 제공
    • 내비게이션에 관심사 스터디 경로 통합
  • 변경

    • 사용자 선호 카테고리 표현 및 전송 형식 개선 (내부 데이터 표현 갱신)

✏️ Tip: You can customize this high-level summary in your review settings.

@starshape7 starshape7 linked an issue Jan 22, 2026 that may be closed by this pull request
1 task
@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Walkthrough

사용자 선호 카테고리 기반 스터디 조회 및 필터 기능을 추가합니다. API/데이터계층, 저장소, 뷰모델, UI(목록 · 필터 화면)와 네비게이션, 사용자 선호 카테고리 매핑이 확장되었습니다.

Changes

Cohort / File(s) 변경 요약
데이터/API 계층
data/study/src/main/java/.../StudyDataSource.kt, data/study/src/main/java/.../StudyDataSourceImpl.kt, data/study/src/main/java/.../StudyService.kt
getPreferCategoryStudies(...) 메서드(카테고리, 모집상태, 비용, 온라인 여부, 정렬, cursor/size) 추가 — 외부 API 호출 경로 및 시그니처 확장
저장소 레이어
domain/study/src/main/java/.../StudyRepository.kt, data/study/src/main/java/.../StudyRepositoryImpl.kt
Repository 인터페이스 및 구현에 getPreferCategoryStudies(...) 추가 및 서비스 응답을 도메인으로 매핑
사용자 선호 카테고리 매핑
data/user/src/main/java/.../UserRequestDto.kt, data/user/src/main/java/.../UserMapper.kt, domain/user/src/main/java/.../UserPreferredCategoryResult.kt
DTO 필드명 userThemescategories로 변경 및 도메인 타입 List<String?>List<StudyTheme?>로 변경(직렬화/매핑 영향)
네비게이션 통합
feature/study/src/main/java/.../navigation/*, feature/main/src/main/java/.../MainNavHost.kt, feature/main/src/main/java/.../MainNavigator.kt, feature/main/src/main/java/.../MainScreen.kt
PreferCategoryPreferCategoryFilter 라우트/그래프 추가, 메인 네비게이션에 관심사 메뉴 연동
UI — 화면 및 필터
feature/study/src/main/java/.../PreferCategoryStudyScreen.kt, feature/study/src/main/java/.../PreferCategoryStudyFilterScreen.kt
선호 카테고리 기반 목록 화면(탭, 정렬, 무한스크롤) 및 필터 화면(모집상태, 활동 유형, 비용) 신규 추가 — 많은 UI composable 추가 (상호작용 포인트 확인 필요)
ViewModel / 상태관리
feature/study/src/main/java/.../PreferCategoryStudyViewModel.kt
PreferCategoryStudyViewModel 추가: 선호 카테고리 로드, 페이징, 필터/정렬 상태 관리, API 호출 흐름 담당
마이페이지 연결
feature/mypage/src/main/java/.../MyPageViewModel.kt
사용자 선호 카테고리 표시 로직을 title 리스트로 변환 저장하도록 변경

Sequence Diagram

sequenceDiagram
    participant User as "User"
    participant MainNav as "MainNavHost"
    participant VM as "PreferCategoryStudyViewModel"
    participant UserRepo as "UserRepository"
    participant StudyRepo as "StudyRepository"
    participant StudySvc as "StudyService"

    User->>MainNav: 관심사 메뉴 선택
    MainNav->>VM: 화면 진입 (init)
    VM->>UserRepo: getPreferCategories()
    UserRepo-->>VM: List<StudyTheme?>
    VM->>StudyRepo: getPreferCategoryStudies(category, filters, sort, cursor, size)
    StudyRepo->>StudySvc: getPreferCategoryStudies(...)
    StudySvc-->>StudyRepo: BaseResponse<StudyResponseDto>
    StudyRepo-->>VM: Result<StudyResultList>
    VM-->>MainNav: uiState (Success/Empty/Failure)
    MainNav-->>User: 화면 렌더링

    User->>MainNav: 필터 적용
    MainNav->>VM: applyFilter(...)
    VM->>StudyRepo: getPreferCategoryStudies(...) (필터 포함)
    StudyRepo->>StudySvc: getPreferCategoryStudies(...)
    StudySvc-->>StudyRepo: 필터된 결과
    StudyRepo-->>VM: Result<StudyResultList>
    VM-->>MainNav: 업데이트된 uiState
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

🐶 종훈, 🍒 [FEAT]

Poem

🐰 관심사 탭 위를 깡충깡충,
필터와 정렬로 쏙쏙 모아,
무한 스크롤에 꿈을 싣고,
내가 좋아하는 스터디로 가자! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 pull request의 주요 변경사항을 명확하게 반영하고 있습니다. '관심사 스터디 구현'이 모든 코드 변경의 핵심 목표입니다.
Description check ✅ Passed PR 설명이 관련 이슈, 작업 내용, 스크린샷을 포함하고 있으나 PR Point와 트러블슈팅 섹션이 비어있습니다.
Linked Issues check ✅ Passed 모든 코드 변경사항이 #38 이슈의 'myInterstStudy 구현' 요구사항을 충족합니다. 데이터 계층에서 UI 계층까지 일관되게 구현되었습니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 관심사 스터디 기능 구현과 직접 관련이 있으며, 범위 밖의 변경은 발견되지 않습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@data/study/src/main/java/com/umcspot/spot/study/repositoryimpl/StudyRepositoryImpl.kt`:
- Around line 94-96: The error log inside the onFailure block currently uses the
wrong method name; update the Log.e call in StudyRepositoryImpl (the onFailure
inside getPreferCategoryStudies) to use "getPreferCategoryStudies failed"
instead of "getPreferLocationStudies failed" so the log message matches the
getPreferCategoryStudies function and avoids confusion during debugging.
- Line 14: Remove the unused JUnit test import from the production repository
implementation: delete the import line "import
org.junit.experimental.categories.Category" in StudyRepositoryImpl
(StudyRepositoryImpl.kt), confirm there are no usages of Category in the file,
and run the build/compile to ensure no remaining test-only imports remain.

In `@feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt`:
- Around line 173-178: The preferCategoryStudyGraph call sets onItemClick to an
empty lambda so clicks do nothing; update its onItemClick to forward the clicked
study id/item to the navigator like in jjimGraph by calling
navigator.navigateToStudyDetail(it) (or the appropriate parameter name) instead
of onItemClick = { } to enable navigation to the study detail screen.

In
`@feature/study/src/main/java/com/umcspot/spot/study/preferCategory/navigation/PreferCategoryStudyFilterNavigation.kt`:
- Around line 24-30: getBackStackEntry(PreferCategory) can throw if
PreferCategory isn't on the back stack; wrap the call to
navController.getBackStackEntry(PreferCategory) in a try-catch and provide a
safe fallback: if try succeeds use that parentEntry to obtain the
PreferCategoryStudyViewModel via hiltViewModel(parentEntry), otherwise obtain a
fallback ViewModel with hiltViewModel() (or explicitly navigate to
PreferCategory before showing the filter) and pass that categoryVm into
PreferCategoryStudyFilterScreen; update the code around
navController.getBackStackEntry, PreferCategoryStudyViewModel and
PreferCategoryStudyFilterScreen to use this safe pattern.
🧹 Nitpick comments (7)
feature/mypage/src/main/java/com/umcspot/spot/mypage/main/MyPageViewModel.kt (2)

40-40: 로그 태그가 클래스명과 불일치합니다.

"HomeViewModel" 대신 "MyPageViewModel"을 사용해야 디버깅 시 로그 필터링이 정확해집니다. 라인 60, 72에도 동일한 문제가 있습니다.

♻️ 수정 제안
-                    Log.e("HomeViewModel", "loadMyPageInfo error", e)
+                    Log.e("MyPageViewModel", "loadMyPageInfo error", e)

라인 60:

-                    Log.e("HomeViewModel", "loadPreferredRegion error", e)
+                    Log.e("MyPageViewModel", "loadPreferredRegion error", e)

라인 72:

-                    Log.e("HomeViewModel", "loadPreferCategories error", e)
+                    Log.e("MyPageViewModel", "loadPreferCategories error", e)

71-74: 주석 처리된 실패 상태 처리를 고려해 주세요.

onFailure 블록에서 UI 상태 업데이트가 주석 처리되어 있어 사용자에게 에러 피드백이 제공되지 않습니다. 추후 적절한 에러 핸들링 구현이 필요합니다.

feature/study/src/main/java/com/umcspot/spot/study/preferCategory/PreferCategoryStudyViewModel.kt (1)

82-141: CancellationException 재전파 권장

runCatching이 CancellationException을 포함한 모든 예외를 흡수하면, 취소 이후에도 UI 업데이트가 진행될 수 있습니다. 구조화된 동시성을 유지하려면 CancellationException은 반드시 재전파해야 합니다.

이 문제는 load() (82-110줄)와 loadNextPage() (112-142줄) 두 메서드 모두에서 발생합니다. 각 메서드의 .onFailure 블록에 CancellationException 재전파 로직을 추가하세요:

.onFailure { e ->
    if (e is CancellationException) throw e
    Log.e("PreferCategoryStudyViewModel", "error", e)
}

CancellationException import를 추가해야 합니다:

import kotlinx.coroutines.CancellationException
feature/study/src/main/java/com/umcspot/spot/study/preferCategory/PreferCategoryStudyScreen.kt (2)

228-251: items.indexOf(item) 호출로 인한 O(n²) 성능 문제

items() 내부에서 indexOf를 호출하면 각 아이템마다 O(n) 검색이 발생하여 전체 O(n²) 복잡도가 됩니다. itemsIndexed를 사용하면 인덱스를 직접 받을 수 있습니다.

♻️ 수정 제안
-        items(
-            items = items,
-            key = { it.id }
-        ) { item ->
+        itemsIndexed(
+            items = items,
+            key = { _, item -> item.id }
+        ) { index, item ->
             Spacer(Modifier.padding(screenHeightDp(5.dp)))

             StudyListItem(
                 item = item,
                 modifier = Modifier
                     .fillMaxWidth(),
                 onClick = { onItemClick(item.id) }
             )

-            if(items.indexOf(item) != items.lastIndex) {
+            if(index != items.lastIndex) {
                 Spacer(Modifier.padding(screenHeightDp(5.dp)))

                 HorizontalDivider(
                     modifier = Modifier
                         .fillMaxWidth(),
                     color = SpotTheme.colors.G300,
                     thickness = 1.dp
                 )
             }
         }

76-78: 사용되지 않는 import 문

kotlin.collections.lastIndex, kotlin.collections.orEmpty, kotlin.text.format은 명시적으로 import하지 않아도 Kotlin 표준 라이브러리에서 자동으로 사용 가능합니다.

♻️ 제거 제안
-import kotlin.collections.lastIndex
-import kotlin.collections.orEmpty
-import kotlin.text.format
feature/study/src/main/java/com/umcspot/spot/study/preferCategory/PreferCategoryStudyFilterScreen.kt (2)

66-66: acceptEnabled가 항상 true로 하드코딩되어 있습니다.

현재 buttonEnabled가 항상 true이므로 버튼 활성화 로직이 무의미합니다. 필터 조건이 변경되었을 때만 버튼을 활성화하거나, 이 변수가 불필요하다면 제거하는 것을 고려해 주세요.

♻️ 필터 변경 여부에 따른 버튼 활성화 예시
-    val acceptEnabled = true
+    val acceptEnabled = draftRecruitingStatus != recruitingStatus ||
+        draftFee != fee ||
+        draftActivity != activity

197-197: screenHeightDp() 사용이 일관되지 않습니다.

다른 모든 Spacer에서는 screenHeightDp()를 사용하지만, 이 라인에서만 직접 80.dp를 사용하고 있습니다. 다양한 화면 크기에서 일관된 레이아웃을 위해 수정이 필요합니다.

♻️ 제안된 수정
-                Spacer(Modifier.height(80.dp))
+                Spacer(Modifier.height(screenHeightDp(80.dp)))

Comment on lines +173 to +178
preferCategoryStudyGraph(
contentPadding = contentPadding,
onRegisterScrollToTop = onRegisterScrollToTop,
onItemClick = { },
onFilterClick = { navigator.navController.navigateToPreferCategoryStudyFilter() },
)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

onItemClick 콜백이 빈 람다로 설정되어 있습니다.

onItemClick = { }가 비어 있어 스터디 아이템 클릭 시 상세 페이지로 이동하지 않습니다. jjimGraph(라인 113)처럼 navigator.navigateToStudyDetail(it)로 연결해야 할 것으로 보입니다.

🔧 수정 제안
 preferCategoryStudyGraph(
     contentPadding = contentPadding,
     onRegisterScrollToTop = onRegisterScrollToTop,
-    onItemClick = { },
+    onItemClick = { navigator.navController.navigateToStudyDetail(it) },
     onFilterClick = { navigator.navController.navigateToPreferCategoryStudyFilter() },
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
preferCategoryStudyGraph(
contentPadding = contentPadding,
onRegisterScrollToTop = onRegisterScrollToTop,
onItemClick = { },
onFilterClick = { navigator.navController.navigateToPreferCategoryStudyFilter() },
)
preferCategoryStudyGraph(
contentPadding = contentPadding,
onRegisterScrollToTop = onRegisterScrollToTop,
onItemClick = { navigator.navController.navigateToStudyDetail(it) },
onFilterClick = { navigator.navController.navigateToPreferCategoryStudyFilter() },
)
🤖 Prompt for AI Agents
In `@feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt` around lines
173 - 178, The preferCategoryStudyGraph call sets onItemClick to an empty lambda
so clicks do nothing; update its onItemClick to forward the clicked study
id/item to the navigator like in jjimGraph by calling
navigator.navigateToStudyDetail(it) (or the appropriate parameter name) instead
of onItemClick = { } to enable navigation to the study detail screen.

Comment on lines +24 to +30
composable<PreferCategoryFilter> {
val parentEntry = remember(navController) {
navController.getBackStackEntry(PreferCategory)
}

val categoryVm: PreferCategoryStudyViewModel = hiltViewModel(parentEntry)
PreferCategoryStudyFilterScreen(
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

androidx navigation NavController getBackStackEntry behavior when destination is not on back stack

💡 Result:

Short answer: NavController.getBackStackEntry(...) requires the destination to be on the NavController's back stack and will throw an IllegalArgumentException if it is not. If you need a safe check, call getCurrentDestination() or inspect the back stack (or catch the exception) instead. [1][2]

Sources:

  • NavController.getBackStackEntry docs (throws IllegalArgumentException if destination is not on the back stack). [1]
  • Navigation/back stack guide (behavior of current destination and empty back stack). [2]

References:
[1] AndroidX Navigation — NavController.getBackStackEntry API.
[2] Android Developers — Navigation and the back stack guide.


🏁 Script executed:

find . -path "*/preferCategory/navigation*" -type f

Repository: SPOTeam/Android-V2

Length of output: 291


🏁 Script executed:

cat -n ./feature/study/src/main/java/com/umcspot/spot/study/preferCategory/navigation/PreferCategoryStudyFilterNavigation.kt

Repository: SPOTeam/Android-V2

Length of output: 1755


🏁 Script executed:

cat -n ./feature/study/src/main/java/com/umcspot/spot/study/preferCategory/navigation/PreferCategoryStudyNavigation.kt

Repository: SPOTeam/Android-V2

Length of output: 1390


🏁 Script executed:

rg -A 5 -B 5 "preferCategoryStudyFilterGraph" --type kotlin

Repository: SPOTeam/Android-V2

Length of output: 4357


🏁 Script executed:

cat -n ./feature/main/src/main/java/com/umcspot/spot/main/MainNavHost.kt | grep -A 30 "preferCategoryStudyGraph"

Repository: SPOTeam/Android-V2

Length of output: 3026


PreferCategory가 백스택에 없을 때 크래시 발생 가능

getBackStackEntry(PreferCategory)는 해당 destination이 백스택에 없으면 IllegalArgumentException을 발생시킵니다. 현재 코드는 다음 두 가지 위험성이 있습니다:

  1. 필터 화면이 PreferCategory를 거치지 않고 진입될 수 있는 경로 존재 (깊은 링크, 상태 복원 등)
  2. 예외 처리 없이 백스택 엔트리를 직접 접근

다음 중 하나로 해결하세요:

  • PreferCategory 진입을 강제하거나
  • navController.getBackStackEntry(PreferCategory)를 try-catch로 감싸고 안전한 폴백 처리 구현
🤖 Prompt for AI Agents
In
`@feature/study/src/main/java/com/umcspot/spot/study/preferCategory/navigation/PreferCategoryStudyFilterNavigation.kt`
around lines 24 - 30, getBackStackEntry(PreferCategory) can throw if
PreferCategory isn't on the back stack; wrap the call to
navController.getBackStackEntry(PreferCategory) in a try-catch and provide a
safe fallback: if try succeeds use that parentEntry to obtain the
PreferCategoryStudyViewModel via hiltViewModel(parentEntry), otherwise obtain a
fallback ViewModel with hiltViewModel() (or explicitly navigate to
PreferCategory before showing the filter) and pass that categoryVm into
PreferCategoryStudyFilterScreen; update the code around
navController.getBackStackEntry, PreferCategoryStudyViewModel and
PreferCategoryStudyFilterScreen to use this safe pattern.

@starshape7 starshape7 merged commit c86df27 into develop Jan 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT/#38] MyInterstStudy 구현

1 participant

Comments