Skip to content

홈 화면 API 연동 및 네비게이션 연결#141

Merged
moondev03 merged 12 commits into
developfrom
feat/#140-홈-화면-api-연동
May 29, 2026

Hidden character warning

The head ref may contain hidden characters: "feat/#140-\ud648-\ud654\uba74-api-\uc5f0\ub3d9"
Merged

홈 화면 API 연동 및 네비게이션 연결#141
moondev03 merged 12 commits into
developfrom
feat/#140-홈-화면-api-연동

Conversation

@moondev03
Copy link
Copy Markdown
Member

@moondev03 moondev03 commented May 29, 2026

📌 작업 내용

  • 홈 화면 메인 데이터 조회 API 및 발표별 연습 기록 조회 API를 연동했습니다.
  • FetchMainDataUseCase에서 발표 목록과 연습 기록을 결합하도록 개선하고, 홈 화면 UI 상태를 실제 서버 데이터 기반으로 매핑하도록 변경했습니다.
  • 홈 화면 FAB 확장 메뉴와 전역 Dimmer 오버레이를 추가해 분석 진입 UX를 개선했습니다.
  • 홈 화면 UI를 HomeScreenContent, HomeAnalysisFabOverlay 등으로 분리해 구조를 정리했습니다.
  • 발표 상세 시트에서 연습 기록 카드, 성장 그래프, 발표 상태별 콘텐츠가 노출되도록 고도화했습니다.
  • 사용자 닉네임을 실제 데이터와 연동하고, 닉네임 캐싱 로직을 추가했습니다.
  • 연습 녹음 분석 API 및 네비게이션 흐름에 presentationId를 전달하도록 수정했습니다.
  • 프로필 화면은 ViewModel 초기화 시점에 데이터를 로드하도록 변경하고, 불필요한 Intent를 제거했습니다.
  • PracticeCard의 D-Day 포함 범위를 수정하고 일부 디자인 시스템 버튼 색상 설정을 보정했습니다.

🧩 관련 이슈


📸 스크린샷


📢 논의하고 싶은 내용

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 연습 기록 조회 기능 추가
    • 메인 데이터 번들 조회 기능 추가
    • 앱 전역 오버레이 상태 관리 기능 추가
  • Improvements

    • 홈 화면 UI 구조 개선 및 성장 그래프 표시 기능 강화
    • 프로필 화면 데이터 로딩 자동화
    • 보고서 화면에 연습 기록 표시 추가
    • 비활성화된 버튼 색상 개선
    • 부동 버튼 활성/비활성 상태 제어 추가

Review Change Stack

moondev03 added 11 commits May 29, 2026 14:47
* **feat: 메인 데이터 및 연습 기록 관련 도메인 모델 및 UseCase 추가**
    * 메인 화면의 대시보드 데이터를 위한 `MainData`와 프레젠테이션 연습 일정을 위한 `PracticeRecords` 모델을 추가했습니다.
    * `FetchMainDataUseCase` 및 `FetchPracticeRecordsUseCase`를 구현하여 관련 비즈니스 로직을 제공합니다.

* **feat: 네트워크 레이어 API 연동 및 DTO 정의**
    * `PresentationService`에 메인 데이터 조회(`GET /main`) 및 연습 기록 조회(`GET /recording/{presentationId}/practice-records`) API를 추가했습니다.
    * 서버 응답 처리를 위한 `GetMainDataResponse`와 `GetPracticeRecordsResponse` DTO를 정의했습니다.

* **refactor: PresentationRepository 및 Data 레이어 구현**
    * `PresentationRepository` 인터페이스에 새로운 기능들을 추가하고, `PresentationRepositoryImpl`에서 이를 구현했습니다.
    * `PresentationMapper`를 확장하여 네트워크 응답 DTO를 도메인 모델인 `MainData` 및 `PracticeRecords`로 변환하는 매핑 로직을 추가했습니다.
    * `PresentationRemoteDataSource`를 통해 실제 네트워크 통신을 수행하도록 연동했습니다.
* **feat: `AppDimmerState` 및 `LocalAppDimmerState` 추가**
    * 앱 전체 화면을 어둡게(Dim) 처리하거나 상태를 관리하기 위한 `AppDimmerState` 클래스를 정의했습니다.
    * `show`, `hide` 메서드와 함께 Dim 영역 클릭 시 호출될 `onDismissRequest` 콜백 로직을 구현했습니다.
    * `LocalAppDimmerState` CompositionLocal을 제공하여 하위 컴포넌트에서 전역 Dimmer 상태에 접근할 수 있도록 했습니다.

* **feat: `PrezelApp` 내 전역 Dimmer 레이아웃 적용**
    * `PrezelApp` 최상위 레이아웃에 Dimmer를 위한 `Box` 오버레이를 추가했습니다.
    * `appDimmerState.isVisible` 상태에 따라 `scrimContainer` 색상의 배경이 나타나도록 설정했습니다.
    * `noRippleClickable`을 사용하여 Dimmer 영역 클릭 시 설정된 `dismiss` 동작이 수행되도록 구현했습니다.
* **fix: `PracticeCard` 트래커 아이템의 종료일 포함 로직 수정**
    * `toTrackerItems` 함수 호출 시 `dDay`에 1일을 더하도록 변경하여, 트래커 아이템 범위에 D-Day 당일이 올바르게 포함되도록 수정했습니다.

* **refactor: `PracticeCardPreview` 샘플 데이터 및 날짜 계산 로직 변경**
    * 프리뷰 확인을 위한 `baseDate`와 `dDay` 날짜를 업데이트했습니다.
    * 샘플 아이템 리스트 생성 시에도 `dDay`에 1일을 추가하여 실제 컴포넌트의 날짜 계산 방식과 일치시켰습니다.
* **refactor: `FetchMainDataUseCase` 내 연습 기록 병렬 조회 및 결합 로직 추가**
    * 각 발표 데이터(`MainData`)에 해당하는 연습 기록을 `async`/`awaitAll`을 이용해 병렬로 조회하도록 개선했습니다.
    * 유즈케이스의 반환 타입을 `Result<List<MainDataWithPracticeRecords>>`로 변경했습니다.

* **refactor: 도메인 및 네트워크 모델 구조 수정**
    * 발표 정보와 연습 기록을 함께 담는 `MainDataWithPracticeRecords` 모델을 새롭게 정의했습니다.
    * `MainData` 모델에서 `accuracyScoreChange`, `scriptMatchRateChange` 필드를 제거했습니다.
    * `GetMainDataResponse` 네트워크 응답 모델의 필드들을 nullable로 변경하고, `PresentationMapper`에서 이에 따른 null 처리 로직을 반영했습니다.

* **build: 의존성 추가 및 기타 변경 사항**
    * `core:domain` 모듈에 `kotlinx-datetime` 라이브러리 의존성을 추가했습니다.
    * `AnalyzePresentationUseCase` 실행 시 성공 로그를 출력하도록 수정했습니다.
* **feat: 홈 화면 데이터 연동 및 상태 관리 로직 구현**
    * `FetchMainDataUseCase`를 연동하여 실제 데이터를 조회하도록 `HomeViewModel`을 수정했습니다.
    * 성장 그래프의 아이템 선택 상태를 토글하는 `ClickCardGraphItem` Intent 및 처리 로직을 추가했습니다.
    * `HomeUiState`에서 도메인 모델(`MainDataWithPracticeRecords`)을 UI 모델로 변환하는 매핑 로직을 구현했습니다.

* **refactor: UI 데이터 모델 구조 개선**
    * `PresentationUiModel`을 `Past`와 `Upcoming` 타입을 가진 `sealed interface`로 개편하여 발표 상태별 데이터 구조를 명확히 했습니다.
    * 연습 기록 데이터를 관리하는 `PracticeRecordsUiModel`과 성장 그래프 데이터를 위한 `GrowthGraphData` 모델을 추가했습니다.

* **feat: 홈 화면 UI 구성 요소 고도화**
    * **Floating Action Button(FAB) 및 배경 딤(Dim) 처리:** `LocalAppDimmerState`를 연동하여 FAB 확장 시 배경을 어둡게 처리하고, `Popup` 및 `PopupPositionProvider`를 통해 FAB 메뉴의 위치를 동적으로 계산하도록 개선했습니다.
    * **발표 상세 시트(PresentationSheet):** 기존의 단순 버튼 구조에서 `PracticeCard`(연습 기록) 및 `CardGraph`(성장 그래프)를 포함한 상세한 정보를 보여주도록 업데이트했습니다.
    * **발표 상태별 대응:** 과거 발표와 예정된 발표에 따라 시트 내 타이틀과 하단 콘텐츠(그래프 또는 키워드 섹션)가 다르게 노출되도록 구현했습니다.

* **style: 디자인 시스템 컴포넌트 및 리소스 추가**
    * `PrezelFloatingButton` 및 `PrezelFloatingMenu`에 `enabled` 속성을 추가하여 상태 제어가 가능하도록 수정했습니다.
    * 홈 화면 하단 시트에서 사용하는 새로운 문자열 리소스를 추가했습니다.
    * `feature:home:impl` 모듈에 `core:domain` 의존성을 추가했습니다.
* **refactor: `HomeScreen` 컴포저블 분리 및 구조화**
    * `HomeScreen.kt`의 거대한 UI 로직을 `HomeScreenContent`와 `HomeAnalysisFabOverlay`로 분리하여 가독성과 유지보수성을 높였습니다.
    * 홈 화면의 상태(Loading, Empty, Content)에 따른 레이아웃 처리 로직을 `HomeScreenContent`로 통합했습니다.
    * `HomeScreen` 프리뷰 코드에서 `CompositionLocalProvider`를 통해 필요한 상태를 주입하도록 개선했습니다.

* **feat: `HomeAnalysisFabOverlay` 컴포넌트 추가**
    * 음성 녹음 분석 및 파일 업로드 분석 기능을 제공하는 확장형 플로팅 메뉴를 구현했습니다.
    * `PopupPositionProvider`를 사용하여 메뉴의 확장/축소 상태에 따른 동적 팝업 위치 계산 로직을 적용했습니다.
    * `LocalAppDimmerState`와 연동하여 메뉴 확장 시 배경 딤 처리가 적용되도록 구현했습니다.

* **refactor: `PrezelApp` 레이아웃 및 네비게이션 로직 정리**
    * `PrezelApp`의 UI 구조를 `AppNavigationContent`와 `AppDimmerOverlay` 컴포저블로 분리하여 역할을 명확히 했습니다.
    * 반복되는 네비게이션 애니메이션 설정을 `defaultPrezelNavTransition` 함수로 공통화했습니다.

* **refactor: 데이터 소스 및 UI 모델 정리**
    * `PresentationRemoteDataSourceImpl`에서 파일 처리를 위한 헬퍼 함수들을 파일 수준의 프라이빗 함수로 이동하여 클래스 내부 로직을 단순화했습니다.
    * `GrowthGraphData` 모델에서 사용되지 않는 `selectedItem` 프로퍼티를 제거했습니다.

* **fix: UI 텍스트 및 프리뷰 데이터 수정**
    * `PresentationSheet`의 D-Day 표기 형식을 `-3`에서 `D-3`으로 수정했습니다.
    * `HomeScreen` 프리뷰에서 그래프 데이터가 올바르게 표시되도록 샘플 데이터를 보강했습니다.
* **refactor: 네트워크 및 데이터 레이어 내 발표 ID 파라미터 추가**
    * `PracticeService`의 연습 녹음 분석 API(`analyzePracticeRecording`)에 `presentationId` 쿼리 파라미터를 추가했습니다.
    * `PracticeRemoteDataSource` 및 `PracticeRepository` 인터페이스와 관련 구현체에 `presentationId` 전달 로직을 반영했습니다.

* **refactor: 네비게이션 키 구조 변경 및 파라미터 전달 로직 개선**
    * `PracticeNavKey`를 `data object`에서 `presentationId`를 포함하는 `data class`로 변경했습니다.
    * `PracticeAnalysisNavKey`에 분석 대상 발표를 식별하기 위한 `presentationId` 필드를 추가했습니다.
    * `HomeEntryBuilder` 및 `PracticeEntryBuilder`에서 변경된 네비게이션 키를 통해 ID가 전달되도록 수정했습니다.

* **refactor: UI 컴포넌트 및 네비게이션 의존성 정리**
    * `HomeScreen`에서 `LocalNavigator` 직접 참조를 제거하고, `navigateToPracticeRecording` 콜백을 통해 네비게이션을 처리하도록 개선했습니다.
    * `HomeScreenContent` 및 하위 컴포넌트에서 연습 녹음 시작 시 해당 발표의 ID를 전달하도록 변경했습니다.
    * `EmptyPresentationSheet`의 불필요한 클릭 리스너 전달 로직을 정리했습니다.

* **refactor: 도메인 및 ViewModel 내 발표 ID 활용**
    * `AnalyzePracticeRecordingUseCase`가 `presentationId`를 요구하도록 수정했습니다.
    * `PracticeAnalysisViewModel` 및 Assisted Factory에 `presentationId`를 추가하여 분석 요청 시 사용하도록 구현했습니다.
* **refactor: ButtonType.FILLED의 배경색 테마 컬러 변경**
    * `ButtonType.FILLED`에 적용되던 배경색 색상 값을 `PrezelTheme.colors.bgLarge`에서 `PrezelTheme.colors.bgDisabled`로 변경했습니다.
* **refactor: 메인 데이터 처리를 위한 `MainDataBundle` 도입 및 UseCase 개편**
    * 닉네임과 발표 데이터 목록을 포함하는 `MainDataBundle` 클래스를 추가했습니다.
    * `FetchMainDataUseCase`에서 `UserRepository`를 통해 사용자의 닉네임을 비동기로 함께 조회하도록 수정했습니다.
    * `HomeUiState`의 매핑 로직을 `MainDataBundle` 기반으로 변경하여 하드코딩된 임시 닉네임을 실제 데이터로 대체했습니다.

* **feat: `UserRepository` 내 닉네임 캐싱 로직 추가**
    * `UserRepository` 인터페이스에 `getUserNickname` 메서드를 추가했습니다.
    * `UserRepositoryImpl`에서 `@Volatile` 변수를 사용하여 닉네임을 메모리에 캐싱하고, 정보 조회나 수정 시 캐시를 갱신하도록 구현했습니다.

* **refactor: 홈 화면 UI 구성 요소 리팩터링 및 기능 연결**
    * 홈 화면의 빈 상태를 표시하는 로직을 별도의 `EmptySheet` 컴포넌트로 분리하여 가독성을 높였습니다.
    * `EmptySheet`에 사용될 문자열 리소스를 추가하고 디자인 시스템의 버튼 및 테마를 적용했습니다.
    * 홈 화면의 '발표 추가' 버튼 클릭 시 음성 녹음 분석 화면으로 이동하도록 네비게이션 로직을 연결했습니다.
* **refactor: UI 모델 클래스 이름 변경**
    * `PracticeUiModel`을 보다 구체적인 명칭인 `PracticeRecordsUiModel`로 변경했습니다.
* **refactor: `ProfileViewModel` 초기화 시 데이터 로드 수행**
    * 기존 UI 레이어에서 `FetchData` Intent를 통해 데이터를 요청하던 방식에서, ViewModel의 `init` 블록 내에서 `fetchUserInfo()`를 직접 호출하도록 변경했습니다.

* **cleanup: `ProfileUiIntent.FetchData` 및 관련 코드 제거**
    * 더 이상 사용되지 않는 `ProfileUiIntent.FetchData` 정의를 삭제했습니다.
    * `ProfileScreen`의 `LaunchedEffect`에서 `FetchData` Intent를 전달하던 로직을 제거하고, ViewModel의 `onIntent` 내 해당 처리 케이스를 삭제했습니다.
@moondev03 moondev03 self-assigned this May 29, 2026
@moondev03 moondev03 requested a review from HamBeomJoon as a code owner May 29, 2026 12:02
@moondev03 moondev03 added the ✨ feat 새로운 기능 추가 또는 기존 기능 확장 label May 29, 2026
@moondev03 moondev03 linked an issue May 29, 2026 that may be closed by this pull request
@moondev03 moondev03 added the 🔧 fix 정상 동작해야 하는 기능의 결함 수정 label May 29, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Warning

Review limit reached

@moondev03, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 40 minutes and 14 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d2ccf94-7906-4caa-9257-ee10f9214d49

📥 Commits

Reviewing files that changed from the base of the PR and between 01695e1 and 5c5617e.

📒 Files selected for processing (3)
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/AnalyzePresentationUseCase.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/EmptyPresentationSheet.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt
📝 Walkthrough

Walkthrough

네트워크 응답 모델부터 도메인 인터페이스, 저장소 구현을 거쳐 홈 화면의 데이터 주도 렌더링까지 통합되는 홈 화면 API 연동 변경입니다. 연습 기록과 성장 그래프를 조회하는 새로운 엔드포인트를 추가하고, 복합 데이터를 병렬 로드하는 유스케이스를 도입하며, 홈 화면 UI를 상태 기반으로 재구성합니다.

Changes

홈 화면 API 연동 및 렌더링

Layer / File(s) Summary
네트워크 응답 모델 및 서비스 계약
core/network/model/presentation/{GetMainDataResponse,GetPracticeRecordsResponse}.kt, core/network/service/{PracticeService,PresentationService}.kt
메인 데이터와 연습 기록 조회 REST API 응답 타입(@Serializable DTO)과 서비스 메서드 시그니처를 추가하여 API 계약을 정의합니다.
도메인 모델 및 UI 모델 정의
core/model/presentation/{MainData,PracticeRecords,MainDataWithPracticeRecords,MainDataBundle,PresentationDetailWithPracticeRecords}.kt, feature/home/impl/main/model/{PresentationUiModel,GrowthGraphItemUiModel,PracticeRecordsUiModel}.kt
연습 기록, 메인 데이터 번들을 표현하는 도메인 모델과 홈/리포트 화면에서 사용할 UI 모델(PresentationUiModel sealed interface로 Past/Upcoming 분기, 성장 그래프/연습 기록 UI 모델)을 정의합니다.
원격 데이터 소스 계약 및 구현
core/network/datasource/{PresentationRemoteDataSource,PresentationRemoteDataSourceImpl,PracticeRemoteDataSource,PracticeRemoteDataSourceImpl}.kt
프레젠테이션/연습 원격 데이터 소스에 메인 데이터 및 연습 기록 조회 메서드를 추가하고, 서비스 호출을 구현하며, 멀티파트 파일 업로드 헬퍼 함수를 재배치합니다.
저장소 구현 및 매퍼 함수
core/data/repository/{PresentationRepositoryImpl,PracticeRepositoryImpl,UserRepositoryImpl}.kt, core/data/mapper/PresentationMapper.kt
데이터 저장소에 getPracticeRecords(), getMainData() 메서드를 추가하고, 네트워크 응답을 도메인 모델로 변환하는 매퍼 확장 함수를 구현하며, 사용자 닉네임 캐싱 기능을 추가합니다.
도메인 인터페이스 및 유스케이스
core/domain/repository/{PresentationRepository,PracticeRepository,UserRepository}.kt, core/domain/usecase/presentation/{FetchMainDataUseCase,FetchPracticeRecordsUseCase,FetchPresentationDetailUseCase}.kt, core/domain/usecase/practice/AnalyzePracticeRecordingUseCase.kt
저장소 인터페이스에 새 메서드 시그니처를 추가하고, 메인 데이터 병렬 조회 유스케이스(FetchMainDataUseCase에서 coroutineScope/async 사용)를 정의하며, 기존 유스케이스에 presentationId 파라미터를 전파합니다.
코어 UI 상태 및 네비게이션 키
core/ui/state/LocalAppDimmerState.kt, core/domain/build.gradle.kts, feature/practice/api/PracticeNavKey.kt
전역 오버레이 제어용 AppDimmerState Compose 상태를 정의하고, 도메인 모듈에 kotlinx.datetime 의존성을 추가하며, 네비게이션 키를 presentationId 필드를 포함하는 데이터 클래스로 전환합니다.
앱 차원 오버레이 및 네비게이션 구조
app/ui/PrezelApp.kt
AppDimmerStateCompositionLocalProvider로 제공하고, 네비게이션 UI와 오버레이를 병렬 렌더링하며, 전환 로직을 defaultPrezelNavTransition() 헬퍼로 추출합니다.
홈 화면 재구성 및 콘텐츠 위임
feature/home/impl/main/{HomeScreen,HomeEntryBuilder}.kt
HomeScreen을 얇은 엔트리포인트로 리팩토링하고, UI 조립 로직을 HomeScreenContent 컴포넌트로 분리하며, 네비게이션 콜백(navigateToPracticeRecording)을 연결합니다.
홈 뷰모델 데이터 주도 로직
feature/home/impl/main/HomeViewModel.kt
FetchMainDataUseCase를 호출해 실제 API 데이터를 로드하고, HomeUiState를 업데이트하며, 그래프 아이템 선택 인덱스를 토글하는 로직을 구현합니다.
홈 콘텐츠 렌더링 및 상태 분기
feature/home/impl/main/component/HomeScreenContent.kt, feature/home/impl/main/component/HomeAnalysisFabOverlay.kt
HomeScreenContent와 내부 컴포넌트들(HomeContent, HomeEmptyContent, HomePresentationContent, HomeMultipleContent)을 추가하여 UI 상태에 따른 동적 렌더링을 구현하고, 음성/파일 분석 FAB 오버레이를 추가합니다.
홈 UI 상태 및 모델 변환
feature/home/impl/main/contract/{HomeUiState,HomeUiIntent}.kt, feature/home/impl/main/component/{head/HomeHeadSection,title/PresentationHero}.kt
MainDataBundle.toUiState() 확장 함수로 상태 변환을 구현하고, HomeUiIntent.ClickCardGraphItem을 추가하며, UI 모델 프리뷰 데이터를 새 모델 구조에 맞게 갱신합니다.
홈 시트 및 바텀 시트 컴포넌트
feature/home/impl/main/component/body/{EmptySheet,PresentationSheet,EmptyPresentationSheet,HomeBottomSheetTitle}.kt
빈 상태 시트를 추가하고, 프레젠테이션 시트를 PresentationUiModel과 그래프 선택 콜백을 받도록 리팩토링하며, 타이틀 컴포넌트에 간격을 추가합니다.
디자인 시스템 버튼 활성화 상태
core/designsystem/component/actions/button/{config/PrezelButtonDefaults,floating/{PrezelFloatingButton,PrezelFloatingMenu}}.kt
플로팅 버튼에 enabled 파라미터를 추가하고, 버튼 기본값 색상(bgDisabled)을 업데이트합니다.
연습 카드 날짜 계산 조정
core/ui/component/PracticeCard.kt
날짜 범위 계산에서 dDay를 하루 더한 값을 기준으로 사용하도록 조정합니다.
연습 기능 네비게이션 및 뷰모델 통합
feature/practice/impl/{analysis/PracticeAnalysisViewModel,navigation/{PracticeAnalysisNavKey,PracticeEntryBuilder}}.kt
연습 분석 네비게이션 키에 presentationId를 추가하고, 뷰모델 팩토리에 전파하며, 엔트리빌더에서 네비게이션을 연결합니다.
프로필 초기화 로직 재구성
feature/profile/impl/{ProfileScreen,ProfileViewModel,contract/ProfileUiIntent}.kt
의도 기반 데이터 로드를 제거하고, 뷰모델 초기화 중에 자동으로 사용자 정보를 가져오도록 변경합니다.
리포트 화면 모델 통합 및 렌더링
feature/report/impl/{AnalysisReportViewModel,component/{ReportBodyContent,body/PracticeHistorySection},contract/{AnalysisReportUiState,AnalysisReportUiStateMapper},model/{PracticeRecordsUiModel,PracticeUiModel},preview/ReportPreviewUiState}.kt
PresentationDetailWithPracticeRecords 모델을 사용하도록 UI 상태 매핑을 변경하고, 연습 기록 UI 모델을 적용하며, 기존 PracticeUiModel을 제거합니다.
홈 문자열 리소스 및 모듈 의존성
feature/home/impl/build.gradle.kts, feature/home/impl/src/main/res/values/strings.xml
홈 피처 빌드에 coreDomain 의존성을 추가하고, 하단 시트 및 빈 상태 문자열 리소스를 정의합니다.

Possibly related PRs

  • Team-Prezel/Prezel-Android#139: 두 PR 모두 PresentationDetailWithPracticeRecords/PresentationAnalysisSummary 기반 변환과 리포트 화면 UI 상태 매핑을 직접 수정해 강하게 연관됩니다.
  • Team-Prezel/Prezel-Android#102: 메인 PR의 UserRepositoryImpl 닉네임 캐싱 및 getUserNickname() 추가가 프로필 흐름의 사용자/닉네임 처리 기반 위에 직접 구축됩니다.
  • Team-Prezel/Prezel-Android#124: 메인 PR이 AnalyzePracticeRecordingUseCase/PracticeRepositoryImplpresentationId를 추가해 연습 분석 호출 계약을 변경하므로 직접 연관됩니다.

Copy link
Copy Markdown
Contributor

@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

🧹 Nitpick comments (4)
Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/FetchMainDataUseCase.kt (1)

18-44: 💤 Low value

병렬 조회 구조는 정상이나, 발표 수에 비례하는 N+1 요청 패턴에 유의하세요.

각 발표마다 getPracticeRecords를 개별 호출하므로 발표 목록이 커지면 동시 네트워크 요청 수가 비례해 증가합니다. 구조적 동시성(coroutineScope + async/awaitAll)과 예외 전파/runCatching 캡처는 올바르게 구성되어 있어 동작상 문제는 없습니다. 향후 목록 규모가 커질 가능성이 있다면 서버에서 메인 데이터에 연습 기록을 포함해 단일 응답으로 내려주는 방식을 검토해 보세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/FetchMainDataUseCase.kt`
around lines 18 - 44, The current invoke() in FetchMainDataUseCase builds
presentations by calling repository.getPracticeRecords(...) inside a
per-presentation async, causing an N+1 request pattern as presentation count
grows; change this to a batched fetch: after repository.getMainData(), collect
presentationIds and call a new or existing repository method (e.g.,
repository.getPracticeRecordsForPresentations(presentationIds) or extend
repository.getMainData() to include practiceRecords) to retrieve all practice
records in one request, then map those records back to each presentation when
constructing MainDataBundle (keeping coroutineScope + async/awaitAll only for
non-network parallelism if needed); update repository interfaces and
toMainDataWithPracticeRecords usage accordingly to use the batched result.
Prezel/core/ui/src/main/java/com/team/prezel/core/ui/state/LocalAppDimmerState.kt (1)

18-30: ⚡ Quick win

dismiss()에서 hide()까지 보장하도록 보완 필요(현재는 우회 중)
LocalAppDimmerState.dismiss()onDismissRequest?.invoke()만 실행하고 isVisible = false 처리는 하지 않습니다. 따라서 show()를 콜백 없이(기본 {} 사용) 호출하는 케이스가 생기면 스크림 탭 시 디머가 닫히지 않는 “잠재적” 문제가 있습니다.

다만 현재 LocalAppDimmerState.current 사용처는 HomeAnalysisFabOverlay뿐이며, 여기서는 show { isFabExpanded = false }로 콜백을 넘기고(Prezel/feature/home/.../HomeAnalysisFabOverlay.kt), LaunchedEffect(isFabExpanded)에서 isFabExpanded가 false가 되면 hide()를 호출해 오늘 경로는 정상 동작합니다.

LocalAppDimmerState.dismiss()에서 hide()를 함께 수행하도록(예: hide() 후 콜백 invoke) 해서 기본 콜백 {}를 쓰더라도 항상 닫히게 만드는 쪽을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Prezel/core/ui/src/main/java/com/team/prezel/core/ui/state/LocalAppDimmerState.kt`
around lines 18 - 30, LocalAppDimmerState.dismiss() currently only invokes
onDismissRequest and doesn't change isVisible, so update dismiss() to call
hide() first and then invoke the callback (e.g., call hide() and then
onDismissRequest?.invoke()) so the dimmer is always hidden even when the default
empty callback is used; reference LocalAppDimmerState.dismiss(), hide(), show(),
onDismissRequest and isVisible when making the change.
Prezel/feature/home/impl/src/main/res/values/strings.xml (1)

27-29: 💤 Low value

중복 문자열 값 정리 (선택).

feature_home_impl_empty_sheet_practice_card_title(라인 29, "지금부터 연습해보세요")은 라인 4 feature_home_impl_bottom_sheet_empty_title과, feature_home_impl_empty_sheet_practice_card_button_text(라인 28, "연습하기")은 라인 25 feature_home_impl_practice_recording_action과 동일한 값입니다. 동일 의미라면 기존 키 재사용을 검토해 중복을 줄일 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Prezel/feature/home/impl/src/main/res/values/strings.xml` around lines 27 -
29, The three string entries duplicate existing values: replace
feature_home_impl_empty_sheet_practice_card_title ("지금부터 연습해보세요") with the
existing feature_home_impl_bottom_sheet_empty_title, and replace
feature_home_impl_empty_sheet_practice_card_button_text ("연습하기") with
feature_home_impl_practice_recording_action, then remove the duplicate keys from
strings.xml and update any UI/layout/code references that used
feature_home_impl_empty_sheet_practice_card_title or
feature_home_impl_empty_sheet_practice_card_button_text to use the reused keys
(leave feature_home_impl_empty_sheet_practice_card_message as-is if unique).
Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt (1)

41-69: 💤 Low value

enabled가 토글 버튼에만 적용됨 (선택 검토).

enabled = false여도 isExpanded가 true이면 메뉴 항목(라인 48-59)은 그대로 노출/클릭 가능합니다. 비활성화 시 메뉴 펼침까지 막을 의도라면 isExpanded && enabled 조건을 고려하세요. 호출부에서 isExpanded를 함께 통제한다면 현재 동작으로 충분합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt`
around lines 41 - 69, In PrezelFloatingMenu, the menu UI is still shown when
isExpanded is true even if enabled is false; update the render guard so the menu
only displays when both isExpanded and enabled (e.g., change the if condition
around PrezelMenu from "if (isExpanded)" to "if (isExpanded && enabled)"),
ensuring PrezelMenu (and its items) are not interactable when the floating
control is disabled while leaving PrezelFloatingButton usage/props (isExpanded,
onChangeExpanded, enabled) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/AnalyzePresentationUseCase.kt`:
- Line 35: Remove the leftover debug print in AnalyzePresentationUseCase: delete
the onSuccess { println(date) } call in the chain inside
AnalyzePresentationUseCase (the debug lambda attached via .onSuccess) so no
standard-output debug remains; if you need runtime visibility, replace it with a
structured logger call (e.g., processLogger.info/trace) that includes the date
instead of println.

In
`@Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/EmptyPresentationSheet.kt`:
- Around line 25-28: EmptyPresentationSheet currently renders PrezelButton with
onClick = {} (no-op) causing a UX gap; update EmptyPresentationSheet to accept a
callback parameter (e.g., onPracticeClick: () -> Unit) and pass it to
PrezelButton's onClick, update EmptyPresentationContentPreview to supply a no-op
or sample lambda, and ensure any real callers of EmptyPresentationSheet wire a
proper handler; if you cannot guarantee callers will provide a handler,
alternatively render the PrezelButton as enabled = false or hide it until a real
callback is available—make the change in the EmptyPresentationSheet function and
its usages (EmptyPresentationContentPreview) so the button is either actionable
via onPracticeClick or explicitly disabled/hidden.

In
`@Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt`:
- Around line 42-50: PresentationSheet's PresentationUiModel.Past branch always
renders CardGraph with presentation.growthGraphData.graphItems which can be
empty (mapped via GetMainDataResponse.toDomain()), causing CardGraph's internal
require to throw; change the Past branch to check
presentation.growthGraphData.graphItems.isNotEmpty() before rendering CardGraph
(or render a safe placeholder/empty state) so CardGraph is only called with
non-empty items; update the branch around CardGraph and keep the existing
onClickCardGraphItemIndex handling for the non-empty path.

In
`@Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/contract/HomeUiState.kt`:
- Line 40: The hardcoded fallback "unknown" in HomeUiState (val fallbackNickname
= nickname.ifBlank { "unknown" }) should be moved to a string resource (e.g.,
add <string name="unknown_nickname">Unknown</string> in strings.xml) and the
code should obtain that resource instead of the literal; either compute
fallbackNickname outside the pure UI state (in the ViewModel or Composable)
using context.getString(R.string.unknown_nickname) / stringResource(...) and
pass it into HomeUiState or change HomeUiState to accept a fallback parameter so
the resource can be injected where Android context is available—replace the
literal with the resource-backed value and update callers accordingly.

---

Nitpick comments:
In
`@Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt`:
- Around line 41-69: In PrezelFloatingMenu, the menu UI is still shown when
isExpanded is true even if enabled is false; update the render guard so the menu
only displays when both isExpanded and enabled (e.g., change the if condition
around PrezelMenu from "if (isExpanded)" to "if (isExpanded && enabled)"),
ensuring PrezelMenu (and its items) are not interactable when the floating
control is disabled while leaving PrezelFloatingButton usage/props (isExpanded,
onChangeExpanded, enabled) unchanged.

In
`@Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/FetchMainDataUseCase.kt`:
- Around line 18-44: The current invoke() in FetchMainDataUseCase builds
presentations by calling repository.getPracticeRecords(...) inside a
per-presentation async, causing an N+1 request pattern as presentation count
grows; change this to a batched fetch: after repository.getMainData(), collect
presentationIds and call a new or existing repository method (e.g.,
repository.getPracticeRecordsForPresentations(presentationIds) or extend
repository.getMainData() to include practiceRecords) to retrieve all practice
records in one request, then map those records back to each presentation when
constructing MainDataBundle (keeping coroutineScope + async/awaitAll only for
non-network parallelism if needed); update repository interfaces and
toMainDataWithPracticeRecords usage accordingly to use the batched result.

In
`@Prezel/core/ui/src/main/java/com/team/prezel/core/ui/state/LocalAppDimmerState.kt`:
- Around line 18-30: LocalAppDimmerState.dismiss() currently only invokes
onDismissRequest and doesn't change isVisible, so update dismiss() to call
hide() first and then invoke the callback (e.g., call hide() and then
onDismissRequest?.invoke()) so the dimmer is always hidden even when the default
empty callback is used; reference LocalAppDimmerState.dismiss(), hide(), show(),
onDismissRequest and isVisible when making the change.

In `@Prezel/feature/home/impl/src/main/res/values/strings.xml`:
- Around line 27-29: The three string entries duplicate existing values: replace
feature_home_impl_empty_sheet_practice_card_title ("지금부터 연습해보세요") with the
existing feature_home_impl_bottom_sheet_empty_title, and replace
feature_home_impl_empty_sheet_practice_card_button_text ("연습하기") with
feature_home_impl_practice_recording_action, then remove the duplicate keys from
strings.xml and update any UI/layout/code references that used
feature_home_impl_empty_sheet_practice_card_title or
feature_home_impl_empty_sheet_practice_card_button_text to use the reused keys
(leave feature_home_impl_empty_sheet_practice_card_message as-is if unique).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e767f434-3ec1-4a7b-a7f5-e4c66347de13

📥 Commits

Reviewing files that changed from the base of the PR and between c7dc878 and 01695e1.

📒 Files selected for processing (65)
  • Prezel/app/src/main/java/com/team/prezel/ui/PrezelApp.kt
  • Prezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PresentationMapper.kt
  • Prezel/core/data/src/main/java/com/team/prezel/core/data/repository/PracticeRepositoryImpl.kt
  • Prezel/core/data/src/main/java/com/team/prezel/core/data/repository/PresentationRepositoryImpl.kt
  • Prezel/core/data/src/main/java/com/team/prezel/core/data/repository/UserRepositoryImpl.kt
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/config/PrezelButtonDefaults.kt
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingButton.kt
  • Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/actions/button/floating/PrezelFloatingMenu.kt
  • Prezel/core/domain/build.gradle.kts
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/repository/practice/PracticeRepository.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/repository/presentation/PresentationRepository.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/repository/profile/UserRepository.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/practice/AnalyzePracticeRecordingUseCase.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/AnalyzePresentationUseCase.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/FetchMainDataUseCase.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/FetchPracticeRecordsUseCase.kt
  • Prezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/presentation/FetchPresentationDetailUseCase.kt
  • Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/MainData.kt
  • Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/MainDataBundle.kt
  • Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/MainDataWithPracticeRecords.kt
  • Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/PracticeRecords.kt
  • Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/PresentationDetailWithPracticeRecords.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSource.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSourceImpl.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PresentationRemoteDataSource.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PresentationRemoteDataSourceImpl.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/model/presentation/GetMainDataResponse.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/model/presentation/GetPracticeRecordsResponse.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/service/PracticeService.kt
  • Prezel/core/network/src/main/java/com/team/prezel/core/network/service/PresentationService.kt
  • Prezel/core/ui/src/main/java/com/team/prezel/core/ui/component/PracticeCard.kt
  • Prezel/core/ui/src/main/java/com/team/prezel/core/ui/state/LocalAppDimmerState.kt
  • Prezel/feature/home/impl/build.gradle.kts
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeViewModel.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/HomeAnalysisFabOverlay.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/HomeScreenContent.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/EmptyPresentationSheet.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/EmptySheet.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/HomeBottomSheetTitle.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/head/HomeHeadSection.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/title/PresentationHero.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/contract/HomeUiIntent.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/contract/HomeUiState.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/GrowthGraphItemUiModel.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/PracticeRecordsUiModel.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/model/PresentationUiModel.kt
  • Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/navigation/HomeEntryBuilder.kt
  • Prezel/feature/home/impl/src/main/res/values/strings.xml
  • Prezel/feature/practice/api/src/main/java/com/team/prezel/feature/practice/api/PracticeNavKey.kt
  • Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/analysis/PracticeAnalysisViewModel.kt
  • Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeAnalysisNavKey.kt
  • Prezel/feature/practice/impl/src/main/java/com/team/prezel/feature/practice/impl/navigation/PracticeEntryBuilder.kt
  • Prezel/feature/profile/impl/src/main/java/com/team/prezel/feature/profile/impl/ProfileScreen.kt
  • Prezel/feature/profile/impl/src/main/java/com/team/prezel/feature/profile/impl/ProfileViewModel.kt
  • Prezel/feature/profile/impl/src/main/java/com/team/prezel/feature/profile/impl/contract/ProfileUiIntent.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/AnalysisReportViewModel.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/component/ReportBodyContent.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/component/body/PracticeHistorySection.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/contract/AnalysisReportUiState.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/contract/AnalysisReportUiStateMapper.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/model/PracticeRecordsUiModel.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/model/PracticeUiModel.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/preview/ReportPreviewUiState.kt
💤 Files with no reviewable changes (3)
  • Prezel/feature/profile/impl/src/main/java/com/team/prezel/feature/profile/impl/contract/ProfileUiIntent.kt
  • Prezel/feature/report/impl/src/main/java/com/team/prezel/feature/report/impl/model/PracticeUiModel.kt
  • Prezel/feature/profile/impl/src/main/java/com/team/prezel/feature/profile/impl/ProfileScreen.kt

* **refactor: 발표 데이터 표시 로직 개선 및 미사용 컴포넌트 제거**
    * `PresentationSheet`에서 과거 발표(`PresentationUiModel.Past`)의 그래프 데이터가 비어있는 경우, 하단 시트 콘텐츠를 렌더링하지 않도록 가드 로직을 추가했습니다.
    * 더 이상 사용되지 않는 `EmptyPresentationSheet.kt` 컴포넌트 파일을 삭제했습니다.

* **cleanup: 불필요한 디버그 코드 제거**
    * `AnalyzePresentationUseCase` 내 성공 콜백에 남아있던 디버그용 `println` 호출을 삭제했습니다.
@moondev03 moondev03 merged commit 391d3c4 into develop May 29, 2026
2 checks passed
@moondev03 moondev03 deleted the feat/#140-홈-화면-api-연동 branch May 29, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 새로운 기능 추가 또는 기존 기능 확장 🔧 fix 정상 동작해야 하는 기능의 결함 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

홈 화면 API 연동 프로필 편집 화면 구성 변경 시 입력 데이터 유실 문제 수정

2 participants