분석 녹음 파일 업로드 화면 구현#137
Conversation
* **feat: 발표 분석 프로세스 단계별 UI 및 로직 구현**
* 새로운 발표 분석을 위한 `AnalysisNavKey` 및 탐색 그래프(`AnalysisEntryBuilder`)를 추가했습니다.
* `AnalysisFlowViewModel`을 통해 5단계의 분석 흐름(일정 입력 -> 상황 설정 -> 대본 입력 -> 음성 업로드 -> 분석 중)을 관리하도록 구현했습니다.
* **일정 추가**: 발표 제목 및 `PrezelDatePicker`를 이용한 날짜 선택 기능을 구현했습니다.
* **상황 설정**: `PrezelAccordion`과 칩/카드 UI를 사용하여 발표 유형, 목적, 스타일, 청중을 선택하는 기능을 추가했습니다.
* **대본 입력**: `.txt` 파일 업로드와 직접 입력(`PrezelTextArea`) 방식을 모두 지원합니다.
* **음성 업로드**: 오디오 파일(`mp3`, `wav`) 선택 및 업로드 상태를 표시하는 카드 UI를 추가했습니다.
* **분석 중**: 가상의 딜레이와 함께 분석 진행 상태를 보여주는 로딩 화면을 구현했습니다.
* **feat: 홈 화면 내 분석 진입점 추가**
* 홈 화면에 `PrezelFloatingMenu`(FAB)를 도입하여 '음성 녹음' 및 '파일 업로드' 분석 모드로 진입할 수 있는 인터페이스를 추가했습니다.
* FAB 확장 시 배경 오버레이 처리를 추가하여 사용성을 개선했습니다.
* **refactor: 로그인 및 내비게이션 구조 단순화**
* `LoginViewModel`에서 실제 카카오 로그인 로직을 임시로 제거하고, 로그인 클릭 시 바로 홈 화면으로 이동하도록 흐름을 단순화했습니다.
* `feature:login:impl` 모듈의 불필요한 의존성(`AuthManager`, `LoginUseCase`)을 정리했습니다.
* **build: 모듈 의존성 및 리소스 구성**
* `feature:analysis` api/impl 모듈을 신규 생성하고 `app` 모듈 및 관련 기능 모듈에 의존성을 연결했습니다.
* 분석 단계별 아이콘, 문자열 리소스 및 공통 레이아웃(`AnalysisStepLayout`)을 정의했습니다.
* **feat: 대본 파일 업로드 기능 개선 및 지원 형식 확대**
* 대본 파일 지원 형식을 기존 `.txt`에서 `pdf`, `.txt`로 확대하고 관련 안내 문구를 수정했습니다.
* `ActivityResultContracts.OpenDocument`를 사용하여 다양한 문서 타입(`pdf`, `text/*` 등)을 선택할 수 있도록 개선했습니다.
* 파일 업로드 화면을 `StatusView`와 커스텀 벡터 아이콘(`feature_analysis_impl_no_script`)을 사용하는 UI로 개편했습니다.
* `ContentResolver`를 통해 URI로부터 실제 파일명을 추출하도록 `toFileName` 확장 함수를 개선했습니다.
* **refactor: 발표 일정 및 입력 유효성 로직 수정**
* 발표 제목 입력 시 공백 제외 최소 2자 이상 입력해야 다음 단계로 이동 가능하도록 검증 로직을 강화했습니다.
* 발표 날짜 표시 형식을 `YYYY.MM.DD`에서 `YYYY년 MM월 DD일`로 변경하고 관련 파싱 로직을 업데이트했습니다.
* **style: 디자인 시스템 컴포넌트 동작 및 UI 세부 조정**
* `PrezelTabs`: 탭 클릭 시 리플 효과(Ripple)가 발생하지 않도록 `NoRippleInteractionSource`를 적용했습니다.
* `PrezelAccordion`: 아코디언이 확장(`expanded`)된 상태에서는 하단 구분선(`showDivider`)이 노출되지 않도록 수정했습니다.
* `DayCell`: 데이트 피커 내 날짜 선택 시 리플 효과를 제거했습니다.
* `PresentationScheduleScreen`: 날짜 선택 필드의 상호작용 소스를 초기화하여 클릭 영역 동작을 개선했습니다.
…al-recording-file-upload # Conflicts: # Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginScreen.kt # Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/LoginViewModel.kt # Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/contract/LoginUiEffect.kt # Prezel/feature/login/impl/src/main/java/com/team/prezel/feature/login/impl/navigation/LoginEntryBuilder.kt
…al-recording-file-upload
* **feat: 파일 인식 실패 화면 추가 및 탐색 로직 구현**
* 음성 및 대본 파일 인식 실패 시 표시할 `FileRecognitionFailedScreen`, `ScriptFileRecognitionFailedScreen`을 추가했습니다.
* `AnalysisFlowStep`에 실패 상태(`FILE_RECOGNITION_FAILED`, `SCRIPT_FILE_RECOGNITION_FAILED`)를 추가하고, 재시도(`RetryFileUpload`) 인텐트 처리를 구현했습니다.
* **feat: 대본 및 음성 파일 업로드 진행 상태 표시 구현**
* 파일 선택 시 즉시 완료되지 않고 시뮬레이션된 프로그레스 바를 표시하도록 개선했습니다 (`animateFloatAsState` 사용).
* `ScriptInputScreen` 및 `AudioUploadScreen`에서 파일 업로드 중 진행률(%)과 상태 바를 시각화했습니다.
* **refactor: 분석 설정(Situation) 인텐트 및 모델 구조 개선**
* 개별 데이터 클래스로 분리되어 있던 카테고리, 목적, 스타일, 청중 선택 인텐트를 `SelectSituationOption` 추상화된 sealed interface 구조로 통합했습니다.
* `PresentationSituationScreen` 내 아코디언 컴포넌트들을 하위 함수로 분리하여 가독성을 높였습니다.
* **refactor: 디자인 시스템 컴포넌트 최신화 및 UI 코드 정리**
* `AnalysisStepLayout`의 버튼 영역을 `PrezelButtonArea`의 최신 API(`mainButton`, `subButton` 슬롯 방식)에 맞게 리팩터링했습니다.
* `PrezelChip`의 파라미터명을 최신 디자인 시스템 사양(`interaction` -> `state`)에 맞게 수정했습니다.
* `PresentationScheduleScreen`에서 날짜 선택 필드와 텍스트 필드 로직을 별도 컴포넌트로 추출했습니다.
* **fix: 지원 파일 형식 수정 및 리소스 추가**
* 대본 파일 형식을 `txt`로 제한하고, 음성 파일 형식을 `m4a`, `mp4`, `mp3` 위주로 조정했습니다.
* 인식 실패 화면에서 사용할 에러 아이콘(`feature_analysis_impl_error_voice.xml`)을 추가했습니다.
* **etc: 개발용 임시 코드 추가**
* `SplashViewModel`에서 로그인 여부와 관계없이 홈으로 바로 이동하도록 임시 로직을 추가했습니다.
* `LoginEntryBuilder`에서 `AuthManager` 의존성을 주입받도록 수정했습니다.
…al-recording-file-upload # Conflicts: # Prezel/settings.gradle.kts
…al-recording-file-upload
- 기존 `AnalyzingScreen`을 `AnalysisLoadingScreen`으로 변경하고 Lottie 애니메이션 적용 - 분석 완료 후 표시할 `AnalysisReportScreen` 추가 및 `AnalysisFlowStep.REPORT` 단계 정의 - `AudioUploadScreen`에 `MediaPlayer` 기반의 오디오 재생 및 탐색(Seek) 로직 구현 - `ScriptInputScreen`과 `AudioUploadScreen`의 파일 업로드 표시 로직을 공통 컴포넌트인 `FileUploader`로 리팩터링 - `AnalysisFlowViewModel` 내 단계 전환 로직에 `REPORT` 단계 추가 및 백버튼 동작 수정 - 분석 완료 시 홈으로 이동하던 `AnalysisEntryBuilder` 로직을 화면 내 상태 변화로 처리하도록 변경
- `SplashViewModel`에서 홈 화면으로 즉시 이동하던 임시 코드 제거 - 실제 로그인 상태를 확인하고 로딩 상태를 업데이트하는 로직 활성화
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough분석 기능 모듈(api/impl)이 추가되어 발표 일정→상황→스크립트→오디오 업로드→분석→리포트 흐름을 구현합니다. ViewModel 상태 머신, UI 화면들, 파일 캐시, 네트워킹 계층, 유스케이스 및 Home 화면 통합이 포함되며, Category/Purpose/Style/Audience 열거형 값이 업데이트됩니다. Changes분석 기능 모듈 및 UI 흐름 구현
Category/Purpose/Style/Audience 열거형 값 변경
Possibly related issues
Possibly related PRs
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AudioUploadScreen.kt`:
- Line 53: Add WAV MIME types to the audio filter and update the user-facing
format hint: modify the AUDIO_FILE_MIME_TYPES array in AudioUploadScreen.kt to
include "audio/wav" and "audio/x-wav", and update the localization key
feature_analysis_impl_audio_file_format (or the UI text that references it) to
mention WAV alongside m4a/mp4/mp3 so the OpenDocument chooser allows WAV files
and the guidance string reflects WAV support.
In `@Prezel/feature/analysis/impl/src/main/res/values/strings.xml`:
- Around line 46-55: Update the user-facing file-format strings so they match
the actual supported formats: change the resource
feature_analysis_impl_script_file_format to list all supported script formats
(not just "txt") and change feature_analysis_impl_audio_file_format to include
"wav" in addition to "m4a, mp4, mp3" so the displayed guidance accurately
reflects supported inputs.
In
`@Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/navigation/HomeEntryBuilder.kt`:
- Around line 19-25: The two FAB callbacks in HomeScreen
(navigateToFileUploadAnalysis and navigateToVoiceRecordingAnalysis) both call
navigator.navigate(AnalysisNavKey.Create) so the routing can't distinguish
intent; update routing to pass distinct navigation keys or parameters (e.g., add
separate AnalysisNavKey variants or include a mode/argument on
AnalysisNavKey.Create) and change the two callbacks to call the appropriate
key/value so navigator.navigate receives a unique identifier for file-upload vs
voice-recording; search for HomeScreen, navigateToFileUploadAnalysis,
navigateToVoiceRecordingAnalysis, AnalysisNavKey.Create, and navigator.navigate
to apply the change.
🪄 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: ecd4e1f8-eb42-4941-a317-cfddee234518
📒 Files selected for processing (33)
Prezel/app/build.gradle.ktsPrezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/PrezelAccordion.ktPrezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/datepicker/config/DayCell.ktPrezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/navigations/PrezelTabs.ktPrezel/feature/analysis/api/build.gradle.ktsPrezel/feature/analysis/api/src/main/java/com/team/prezel/feature/analysis/api/AnalysisNavKey.ktPrezel/feature/analysis/impl/build.gradle.ktsPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisLoadingScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisReportScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AudioUploadScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/FileRecognitionFailedScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/PresentationScheduleScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/PresentationSituationScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/ScriptInputScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/SituationOptions.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/component/AnalysisStepLayout.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/component/AnalysisUploadComponents.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiEffect.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiIntent.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiState.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/navigation/AnalysisEntryBuilder.ktPrezel/feature/analysis/impl/src/main/res/drawable/feature_analysis_impl_error_voice.xmlPrezel/feature/analysis/impl/src/main/res/drawable/feature_analysis_impl_no_script.xmlPrezel/feature/analysis/impl/src/main/res/drawable/feature_analysis_impl_no_voice.xmlPrezel/feature/analysis/impl/src/main/res/values/strings.xmlPrezel/feature/analysis/impl/src/test/java/com/team/prezel/feature/analysis/impl/.gitkeepPrezel/feature/home/impl/build.gradle.ktsPrezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.ktPrezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/navigation/HomeEntryBuilder.ktPrezel/feature/home/impl/src/main/res/values/strings.xmlPrezel/settings.gradle.kts
- `feature:analysis:impl` 모듈 내 화면 컴포넌트들을 도메인별 패키지(`audio`, `result`, `schedule`, `script`, `situation`)로 분리하여 재구성 - `PresentationSituationScreen`의 아코디언 UI에 선택된 옵션 표시 기능 추가 및 카드 디자인 개선 - `AnalysisStepLayout` 내 버튼 영역의 배경 표시 설정 및 제목 텍스트 스타일을 `title2Bold`로 변경 - `AnalysisFlowUiIntent`에 상황 설정 옵션 선택을 위한 헬퍼 함수 추가 및 `AnalysisScreen` 내 인텐트 전달 로직 단순화 - `strings.xml` 내 분석 관련 문구 및 상황 설정 옵션 명칭(차분한, 편안한 등)을 기획안에 맞춰 수정 - `MediaPlayer` 생성 시 `uri` 처리를 `toUri()`를 사용하도록 개선하여 안정성 확보 - `core:model`의 `Category` enum 순서 조정 및 상황 설정 관련 리소스 매핑 로직 업데이트
- `AnalyzePresentationRecordingUseCase` 추가 및 도메인 모델 정의 - `PracticeRepository` 및 구현체에 발표 녹음 분석 로직(`analyzePresentationRecording`) 추가 - `PracticeRemoteDataSource` 및 Ktor 기반 멀티파트 데이터 전송 로직 구현 - 발표 분석 요청/응답을 위한 네트워크 DTO(`PresentationRecordingAnalysisResponse` 등) 추가 - `AnalysisFlowViewModel`에서 파일 캐시 복사 및 분석 API 호출 로직 구현 - 분석 중 로딩 화면 및 결과 데이터 상태 관리 추가 - `PracticeRepositoryImplTest`에 발표 분석 시나리오 테스트 케이스 추가
- `AnalysisFailureHandler`를 추가하여 에러 유형별(인증, 서버, 네트워크 등) 처리 로직 구현 - 분석 실패 시 재시도 또는 스낵바 메시지 표시를 위한 `AnalysisFailureAction` 및 `AnalysisUiMessage` 정의 - `AnalysisFlowViewModel`에서 분석 요청 로직을 `PresentationAnalysisRequest` 데이터 클래스로 캡슐화하고 리팩터링 - 분석 실패 대응 테스트 코드(`AnalysisFailureHandlerTest`) 추가 - `AppErrorExt`에서 `FILE_UPLOAD_FAILED` 에러를 `INVALID_REQUEST`로 매핑하도록 수정 및 관련 테스트 추가 - 에러 메시지 표시를 위한 문자열 리소스 및 `AnalysisFlowUiEffect.ShowMessage` 추가
- `AnalysisFileCache` 인터페이스 및 `AnalysisFileCacheImpl` 구현체 추가 - `AnalysisFlowViewModel` 내부에 존재하던 파일 복사 로직을 `AnalysisFileCache`로 분리 - Content Uri로부터 임시 파일을 생성하고 앱 캐시 디렉토리로 복사하는 기능 모듈화 - `PresentationAnalysisRequest`를 `PresentationAnalysisSubmission`으로 네이밍 변경 - Hilt를 사용하여 `AnalysisFileCache` 의존성 주입 설정 추가 (`AnalysisModule`)
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (3)
Prezel/core/data/src/test/java/com/team/prezel/core/data/error/AppErrorExtTest.kt (1)
13-26: ⚡ Quick win변경된 에러 매핑 2건도 테스트에 포함해 주세요.
현재 테스트는
FILE_UPLOAD_FAILED만 검증하고 있어, 이번 변경에 포함된SENTENCE_NOT_FOUND,TERMS_NOT_FOUND매핑 회귀를 놓치기 쉽습니다.테스트 보강 예시
class AppErrorExtTest { `@Test` fun `지원하지 않는 파일 형식 에러는 잘못된 요청으로 변환한다`() { val result = Result .failure<Unit>( ApiException( status = 400, errorCode = ServerErrorCode.FILE_UPLOAD_FAILED, message = "지원하지 않는 오디오 파일 형식입니다.", ), ).mapDomainFailure() val exception = assertIs<AppException>(result.exceptionOrNull()) assertEquals(AppError.INVALID_REQUEST, exception.error) assertEquals("지원하지 않는 오디오 파일 형식입니다.", exception.message) } + + `@Test` + fun `문장 없음 에러는 서버 에러로 변환한다`() { + val result = Result + .failure<Unit>( + ApiException( + status = 500, + errorCode = ServerErrorCode.SENTENCE_NOT_FOUND, + message = "문장을 찾을 수 없습니다.", + ), + ).mapDomainFailure() + + val exception = assertIs<AppException>(result.exceptionOrNull()) + assertEquals(AppError.SERVER_ERROR, exception.error) + } + + `@Test` + fun `약관 없음 에러는 not found로 변환한다`() { + val result = Result + .failure<Unit>( + ApiException( + status = 404, + errorCode = ServerErrorCode.TERMS_NOT_FOUND, + message = "약관을 찾을 수 없습니다.", + ), + ).mapDomainFailure() + + val exception = assertIs<AppException>(result.exceptionOrNull()) + assertEquals(AppError.NOT_FOUND, exception.error) + } }🤖 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/data/src/test/java/com/team/prezel/core/data/error/AppErrorExtTest.kt` around lines 13 - 26, Add two additional assertions in the same test (`지원하지 않는 파일 형식 에러는 잘못된 요청으로 변환한다`) to cover the new error mappings: create Result.failure<Unit>(ApiException(..., errorCode = ServerErrorCode.SENTENCE_NOT_FOUND, ...)).mapDomainFailure() and assert the resulting exception is an AppException with the expected AppError (e.g., AppError.NOT_FOUND) and the original message; repeat the same for ServerErrorCode.TERMS_NOT_FOUND. Use the same helpers used in the test (Result.failure, ApiException, mapDomainFailure(), assertIs<AppException>(), assertEquals(...)) so the new assertions mirror the existing FILE_UPLOAD_FAILED check.Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/result/AnalysisLoadingScreen.kt (1)
24-27: ⚡ Quick win
LaunchedEffect키에 콜백을 포함해 stale 호출을 방지해 주세요Line 25의
LaunchedEffect(Unit)는 재구성 중onFinished참조가 바뀌어도 기존 람다를 호출할 수 있습니다. 콜백 자체를 key로 두는 편이 안전합니다.수정 예시
- if (onFinished != null) { - LaunchedEffect(Unit) { + if (onFinished != null) { + LaunchedEffect(onFinished) { delay(ANALYSIS_LOADING_DURATION_MILLIS) onFinished() } }🤖 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/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/result/AnalysisLoadingScreen.kt` around lines 24 - 27, LaunchedEffect(Unit) can call a stale onFinished after recomposition; change the LaunchedEffect key to onFinished so the effect restarts whenever the callback reference changes and will always invoke the current lambda, i.e., wrap the delay(ANALYSIS_LOADING_DURATION_MILLIS) + onFinished() inside LaunchedEffect(onFinished) (retaining ANALYSIS_LOADING_DURATION_MILLIS and delay) and keep the existing null-check for onFinished.Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiState.kt (1)
41-45: ⚡ Quick winFILE/AUDIO 업로드 URI 검증이
null만 검사합니다
- 현재
OpenDocument취소/클리어 흐름은null로 전달되므로 단계 진행이 열리지 않습니다.- 다만 다른 입력 경로에서
""(빈 문자열)가 들어올 경우 통과 가능하니isNullOrBlank()로 방어하는 게 더 안전합니다.수정 예시
- ScriptInputType.FILE_UPLOAD -> form.scriptFileUri != null + ScriptInputType.FILE_UPLOAD -> !form.scriptFileUri.isNullOrBlank() ScriptInputType.DIRECT_INPUT -> form.script.isNotBlank() } - AnalysisFlowStep.AUDIO_UPLOAD -> form.audioFileUri != null + AnalysisFlowStep.AUDIO_UPLOAD -> !form.audioFileUri.isNullOrBlank()🤖 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/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiState.kt` around lines 41 - 45, The check for upload URIs only guards against null which lets empty strings slip through; update the predicates to use isNullOrBlank() for file/audio URIs so cancelled/cleared flows (which may pass null) and empty-string cases both fail. Specifically, replace usages like form.audioFileUri != null and form.scriptFileUri != null (and any checks tied to ScriptInputType.FILE_UPLOAD and AnalysisFlowStep.AUDIO_UPLOAD) with isNullOrBlank() negation (e.g., !form.audioFileUri.isNullOrBlank()) so the step gating is robust.
🤖 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/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSourceImpl.kt`:
- Around line 61-71: The code only checks scriptFilePath for null but not for
empty/blank, so creating File(File("")) can fail; update the scriptFile
initialization to guard against blank values (e.g., replace val scriptFile =
scriptFilePath?.let(::File) with a check using takeIf or isNullOrBlank like
scriptFilePath?.takeIf(String::isNotBlank)?.let(::File)) so scriptFile remains
null for empty paths and the subsequent MultiPartFormDataContent
append("scriptFile", ...) block is skipped safely.
- Around line 60-89: The multipart builder currently loads whole files into
memory via file.readBytes() for scriptFile and audio (see append keys
"scriptFile" and "audio") and also constructs File("") when scriptFilePath is
empty; update PracticeRemoteDataSourceImpl to (1) validate audioFilePath and
scriptFilePath for null/blank before creating File objects (avoid creating
File("") and guard empty audio path), and (2) replace readBytes() usages with a
streaming/content-provider approach supported by Ktor multipart (use an
InputStream/ByteReadChannel provider or Ktor's InputProvider when appending
multipart parts) so the file is streamed instead of fully read into memory;
ensure headers (ContentType/ContentDisposition) are preserved when switching to
the streaming append.
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFailureHandler.kt`:
- Around line 21-35: The current when(error) in the AnalysisFailureHandler
returns RetryFileUpload only for audio; add a branch for the script-recognition
error so script upload failures trigger the correct recovery flow: map
AppError.SCRIPT_FILE_RECOGNITION_FAILED (the enum/constant) to
AnalysisFailureAction.RetryFileUpload(uploadType = AnalysisUploadType.SCRIPT)
alongside the existing audio mapping in the same when expression so script
failures route to the SCRIPT retry flow.
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt`:
- Around line 57-59: The guard in moveNext() currently treats
AnalysisFlowStep.ANALYZING as an exception and allows a path to REPORT, which
lets duplicate taps or delayed events jump to the report before analysis
finishes; update moveNext() to treat ANALYZING as non-movable (remove the
special-case exception) so that if currentState.step ==
AnalysisFlowStep.ANALYZING the method returns early (or check
!currentState.canMoveNext || currentState.step == AnalysisFlowStep.ANALYZING
then return), and ensure any explicit transition logic that moves ANALYZING ->
REPORT (the code handling that transition) only runs after the analysis
completion callback/state change; apply the same change to the similar block
handling steps in the other method referenced (the block around the ANALYZING ->
REPORT transition).
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/cache/AnalysisFileCacheImpl.kt`:
- Around line 40-45: When creating the temp file `target` in
AnalysisFileCacheImpl (the block that calls File.createTempFile and then
context.contentResolver.openInputStream(uri).use { ... target.outputStream().use
{ input.copyTo(output) } }), ensure `target` is deleted if any exception occurs
during opening/copying; wrap the input/output copy in a try/catch/finally (or
use runCatching { ... }.onFailure { target.delete() }) so that on failure you
call `target.delete()` (and optionally log) to avoid leaving residual files in
context.cacheDir.
---
Nitpick comments:
In
`@Prezel/core/data/src/test/java/com/team/prezel/core/data/error/AppErrorExtTest.kt`:
- Around line 13-26: Add two additional assertions in the same test (`지원하지 않는 파일
형식 에러는 잘못된 요청으로 변환한다`) to cover the new error mappings: create
Result.failure<Unit>(ApiException(..., errorCode =
ServerErrorCode.SENTENCE_NOT_FOUND, ...)).mapDomainFailure() and assert the
resulting exception is an AppException with the expected AppError (e.g.,
AppError.NOT_FOUND) and the original message; repeat the same for
ServerErrorCode.TERMS_NOT_FOUND. Use the same helpers used in the test
(Result.failure, ApiException, mapDomainFailure(), assertIs<AppException>(),
assertEquals(...)) so the new assertions mirror the existing FILE_UPLOAD_FAILED
check.
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiState.kt`:
- Around line 41-45: The check for upload URIs only guards against null which
lets empty strings slip through; update the predicates to use isNullOrBlank()
for file/audio URIs so cancelled/cleared flows (which may pass null) and
empty-string cases both fail. Specifically, replace usages like
form.audioFileUri != null and form.scriptFileUri != null (and any checks tied to
ScriptInputType.FILE_UPLOAD and AnalysisFlowStep.AUDIO_UPLOAD) with
isNullOrBlank() negation (e.g., !form.audioFileUri.isNullOrBlank()) so the step
gating is robust.
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/result/AnalysisLoadingScreen.kt`:
- Around line 24-27: LaunchedEffect(Unit) can call a stale onFinished after
recomposition; change the LaunchedEffect key to onFinished so the effect
restarts whenever the callback reference changes and will always invoke the
current lambda, i.e., wrap the delay(ANALYSIS_LOADING_DURATION_MILLIS) +
onFinished() inside LaunchedEffect(onFinished) (retaining
ANALYSIS_LOADING_DURATION_MILLIS and delay) and keep the existing null-check for
onFinished.
🪄 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: aeba7d0a-0e89-44a2-afa1-a7da7b5508e4
📒 Files selected for processing (25)
Prezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.ktPrezel/core/data/src/main/java/com/team/prezel/core/data/repository/PracticeRepositoryImpl.ktPrezel/core/data/src/test/java/com/team/prezel/core/data/error/AppErrorExtTest.ktPrezel/core/data/src/test/java/com/team/prezel/core/data/repository/practice/PracticeRepositoryImplTest.ktPrezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/repository/practice/PracticeRepository.ktPrezel/core/domain/src/main/kotlin/com/team/prezel/core/domain/usecase/practice/AnalyzePresentationRecordingUseCase.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/PresentationRecordingAnalysisResult.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSource.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSourceImpl.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/model/practice/PresentationAnalysisRequest.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/model/practice/PresentationRecordingAnalysisResponse.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/service/PracticeService.ktPrezel/feature/analysis/impl/build.gradle.ktsPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFailureHandler.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/cache/AnalysisFileCache.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/cache/AnalysisFileCacheImpl.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiEffect.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiState.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/di/AnalysisModule.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/model/AnalysisUiMessage.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/result/AnalysisLoadingScreen.ktPrezel/feature/analysis/impl/src/main/res/values/strings.xmlPrezel/feature/analysis/impl/src/test/java/com/team/prezel/feature/analysis/impl/AnalysisFailureHandlerTest.kt
✅ Files skipped from review due to trivial changes (3)
- Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/model/AnalysisUiMessage.kt
- Prezel/feature/analysis/impl/src/main/res/values/strings.xml
- Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/PresentationRecordingAnalysisResult.kt
- `PracticeRemoteDataSourceImpl`에서 `scriptFilePath`가 빈 문자열인 경우 파일로 처리하지 않도록 개선 - `AnalysisFileCacheImpl`에서 파일 복사 중 예외 발생 시 생성 중이던 임시 파일을 삭제하도록 수정 - 서버의 `FILE_IS_EMPTY` 에러를 도메인의 `SCRIPT_FILE_RECOGNITION_FAILED` 에러로 매핑하는 로직 추가 - `AnalysisFailureHandler`에 스크립트 인식 실패 시 재업로드를 유도하는 액션 추가 - 스크립트 파일 인식 실패 및 에러 변환 로직에 대한 단위 테스트 추가
- 대용량 파일 업로드 시 메모리 효율을 위해 `readBytes()` 대신 `ChannelProvider`를 사용하도록 변경 - `PracticeRemoteDataSourceImpl`에서 파일 경로에 대한 유효성 검사(`require`) 추가 - `AnalysisFlowViewModel`에서 분석 중(`ANALYZING`) 단계의 불필요한 이동 로직 제거 및 상태 전이 방식 개선 - 파일 인식 실패 또는 리포트 단계에서 잘못된 단계로 전환되지 않도록 방어 로직 적용
- `AnalysisFlowUiState`: `scriptFileUri` 및 `audioFileUri` 유효성 검사 시 `isNullOrBlank()`를 사용하여 빈 값 체크 강화 - `AppErrorExtTest`: `VOICE_ANALYSIS_FAILED` 에러가 `AppError.SERVER_ERROR`로 올바르게 매핑되는지 확인하는 테스트 추가
There was a problem hiding this comment.
🧹 Nitpick comments (2)
Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt (2)
82-93: 🏗️ Heavy lift분석 중 뒤로 가기 시 진행 중인 코루틴이 취소되지 않습니다.
moveBack()에서ANALYZING -> AUDIO_UPLOAD로 전환할 때, Line 87에서 시작된 분석 코루틴은 계속 실행됩니다. 사용자가 뒤로 가서 새로운 분석을 시작하면, 이전 분석이 완료될 때 예기치 않은 상태 업데이트가 발생할 수 있습니다.🔧 제안: 분석 Job을 추적하고 필요 시 취소
+ private var analysisJob: Job? = null + private fun analyzePresentation() { val submission = currentState.form.toPresentationAnalysisSubmissionOrNull() ?: return updateState { copy(step = AnalysisFlowStep.ANALYZING) } - viewModelScope.launch { + analysisJob?.cancel() + analysisJob = viewModelScope.launch { submission .analyzePresentationRecording() .onSuccess(::handleAnalysisSuccess) .onFailure { throwable -> handleAnalysisFailure(throwable.toAnalysisFailureAction()) } } }
moveBack()에서ANALYZING상태일 때 Job을 취소:private fun moveBack() { + if (currentState.step == AnalysisFlowStep.ANALYZING) { + analysisJob?.cancel() + } val previousStep = when (currentState.step) {Also applies to: 184-194
🤖 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/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt` around lines 82 - 93, The analyzePresentation coroutine launched in analyzePresentation() is not tracked so it continues after moveBack(); fix by storing the Job returned from viewModelScope.launch in a property (e.g., analysisJob) when calling viewModelScope.launch { ... } and use that Job to cancel the work when the user navigates back: in moveBack() check if current step == AnalysisFlowStep.ANALYZING and call analysisJob?.cancel() (and clear the property), ensuring cancellation happens before you update state to AUDIO_UPLOAD; keep existing handlers (handleAnalysisSuccess/handleAnalysisFailure) but make them cancellation-safe (ignore or no-op on CancellationException) so a cancelled job does not perform unexpected state updates.
234-236: 💤 Low value
scriptFileUri에도 빈 문자열 방어 처리 권장Line 234의
script는takeIf(String::isNotBlank)로 빈 문자열을null로 변환하지만, Line 235의scriptFileUri는 입력 타입만 체크합니다.canMoveNext에서 이미isNullOrBlank()검증이 있지만, 일관성과 방어적 프로그래밍을 위해 동일한 처리를 추가하면 좋습니다.♻️ 제안 수정
- scriptFileUri = scriptFileUri.takeIf { scriptInputType == ScriptInputType.FILE_UPLOAD }, + scriptFileUri = scriptFileUri.takeIf { scriptInputType == ScriptInputType.FILE_UPLOAD && !it.isNullOrBlank() },🤖 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/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt` around lines 234 - 236, The assignment of scriptFileUri should also defensively convert blank strings to null like script does: update the expression that sets scriptFileUri (currently using scriptFileUri.takeIf { scriptInputType == ScriptInputType.FILE_UPLOAD }) to only keep non-blank values when scriptInputType == ScriptInputType.FILE_UPLOAD; reference the scriptFileUri variable and ScriptInputType.FILE_UPLOAD and ensure this matches the blank-checking behavior used for script (and keeps canMoveNext's isNullOrBlank expectations).
🤖 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.
Nitpick comments:
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt`:
- Around line 82-93: The analyzePresentation coroutine launched in
analyzePresentation() is not tracked so it continues after moveBack(); fix by
storing the Job returned from viewModelScope.launch in a property (e.g.,
analysisJob) when calling viewModelScope.launch { ... } and use that Job to
cancel the work when the user navigates back: in moveBack() check if current
step == AnalysisFlowStep.ANALYZING and call analysisJob?.cancel() (and clear the
property), ensuring cancellation happens before you update state to
AUDIO_UPLOAD; keep existing handlers
(handleAnalysisSuccess/handleAnalysisFailure) but make them cancellation-safe
(ignore or no-op on CancellationException) so a cancelled job does not perform
unexpected state updates.
- Around line 234-236: The assignment of scriptFileUri should also defensively
convert blank strings to null like script does: update the expression that sets
scriptFileUri (currently using scriptFileUri.takeIf { scriptInputType ==
ScriptInputType.FILE_UPLOAD }) to only keep non-blank values when
scriptInputType == ScriptInputType.FILE_UPLOAD; reference the scriptFileUri
variable and ScriptInputType.FILE_UPLOAD and ensure this matches the
blank-checking behavior used for script (and keeps canMoveNext's isNullOrBlank
expectations).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 409701d5-97d4-44dd-b739-549a4deebf80
📒 Files selected for processing (10)
Prezel/core/common/src/main/kotlin/com/team/prezel/core/common/error/AppError.ktPrezel/core/data/src/main/java/com/team/prezel/core/data/error/AppErrorExt.ktPrezel/core/data/src/test/java/com/team/prezel/core/data/error/AppErrorExtTest.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSourceImpl.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFailureHandler.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/cache/AnalysisFileCacheImpl.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/contract/AnalysisFlowUiState.ktPrezel/feature/analysis/impl/src/test/java/com/team/prezel/feature/analysis/impl/AnalysisFailureHandlerTest.ktPrezel/feature/terms/impl/src/main/java/com/team/prezel/feature/terms/impl/TermsViewModel.kt
- `AnalysisFlowUiState`: `scriptFileUri` 및 `audioFileUri` 유효성 검사 시 `isNullOrBlank()`를 사용하여 빈 값 체크 강화 - `AppErrorExtTest`: `VOICE_ANALYSIS_FAILED` 에러가 `AppError.SERVER_ERROR`로 올바르게 매핑되는지 확인하는 테스트 추가
- `PresentationAnalysisRequest.kt` 내 선언된 Enum 클래스들을 제거하고 `String` 타입으로 대체 - `PracticeRemoteDataSource` 인터페이스 및 구현체의 파라미터 타입을 `String`으로 변경 - `PracticeMapper`에서 도메인 모델을 `String` 타입의 요청 값으로 매핑하도록 수정 - `AnalysisFlowViewModel`에서 스크립트 파일 URI 처리 시 Null 또는 공백 체크 로직 강화 - `TermsViewModel` 내 에러 처리 로직의 불필요한 Case 제거 및 `else` 분기 추가 - `AnalysisEntryBuilder` 내 불필요한 지역 변수 제거 및 람다식 정리
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt (1)
87-92:⚠️ Potential issue | 🟠 Major | ⚡ Quick win분석 요청 중복/경합으로 이전 응답이 현재 화면 상태를 덮어쓸 수 있습니다.
Line 87-92에서 in-flight Job 추적이 없고, Line 190에서 분석 중 뒤로가기가 허용됩니다. 이 상태에서 먼저 시작한 요청이 늦게 완료되면 Line 121-127에서
REPORT로 강제 전환될 수 있습니다.🔧 제안 수정
import androidx.lifecycle.viewModelScope @@ import kotlinx.coroutines.launch +import kotlinx.coroutines.Job @@ ) : BaseViewModel<AnalysisFlowUiState, AnalysisFlowUiIntent, AnalysisFlowUiEffect>(AnalysisFlowUiState()) { + private var analyzeJob: Job? = null + @@ private fun analyzePresentation() { val submission = currentState.form.toPresentationAnalysisSubmissionOrNull() ?: return updateState { copy(step = AnalysisFlowStep.ANALYZING) } - viewModelScope.launch { + analyzeJob?.cancel() + analyzeJob = viewModelScope.launch { submission .analyzePresentationRecording() - .onSuccess(::handleAnalysisSuccess) + .onSuccess { result -> + if (currentState.step == AnalysisFlowStep.ANALYZING) { + handleAnalysisSuccess(result) + } + } .onFailure { throwable -> handleAnalysisFailure(throwable.toAnalysisFailureAction()) } } } @@ private fun moveBack() { + if (currentState.step == AnalysisFlowStep.ANALYZING) { + analyzeJob?.cancel() + } val previousStep = when (currentState.step) {Also applies to: 121-127, 190-200
🤖 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/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt` around lines 87 - 92, Avoid race conditions by tracking and invalidating in-flight analysis requests: add a cancellable Job or a monotonically increasing analysisRequestId as a ViewModel field and, before calling viewModelScope.launch { submission.analyzePresentationRecording() ... }, cancel the previous Job or capture the new requestId; then in the success/failure callbacks (handleAnalysisSuccess / handleAnalysisFailure via onSuccess/onFailure) verify the captured requestId matches the current one (or that the Job wasn't cancelled) before mutating UI state. Also update the back-navigation handling (the code that allows navigating back while analysis is running) to increment/clear the requestId or cancel the Job so any earlier responses are ignored and do not force state transitions (e.g., to REPORT). Ensure submission.analyzePresentationRecording() responses are gated by this token/cancel logic and that toAnalysisFailureAction() handling also checks it.
🤖 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/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt`:
- Around line 234-236: 현재 로직은 script를 입력 타입과 무관하게 보존하고 scriptFileUri는
FILE_UPLOAD일 때만 설정해 텍스트 스크립트와 파일이 동시에 전송될 수 있으니, script를 FILE_UPLOAD인 경우 비우고(또는
null로) 텍스트는 scriptInputType이 FILE_UPLOAD이 아닐 때만 유지하도록 변경하세요; 즉
AnalysisFlowViewModel의 해당 할당에서 script는 script.takeIf { scriptInputType !=
ScriptInputType.FILE_UPLOAD && it.isNotBlank() }로 조건을 추가하고 scriptFileUri는 기존처럼
scriptInputType == ScriptInputType.FILE_UPLOAD && !it.isNullOrBlank() 조건을 유지해 중복
전송을 방지하세요.
In
`@Prezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryPresentationCard.kt`:
- Line 159: The Category.WORK mapping in HistoryPresentationCard is using
R.string.feature_history_impl_category_report which is inconsistent with the
analysis screen; update the Category.WORK branch in the when/mapper inside
HistoryPresentationCard to use the same string resource the analysis screen uses
for the WORK (업무/비즈니스) label (replace
R.string.feature_history_impl_category_report with the correct WORK resource
used elsewhere, e.g. feature_history_impl_category_work or the shared work label
resource) so the enum displays consistently across screens.
---
Outside diff comments:
In
`@Prezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.kt`:
- Around line 87-92: Avoid race conditions by tracking and invalidating
in-flight analysis requests: add a cancellable Job or a monotonically increasing
analysisRequestId as a ViewModel field and, before calling viewModelScope.launch
{ submission.analyzePresentationRecording() ... }, cancel the previous Job or
capture the new requestId; then in the success/failure callbacks
(handleAnalysisSuccess / handleAnalysisFailure via onSuccess/onFailure) verify
the captured requestId matches the current one (or that the Job wasn't
cancelled) before mutating UI state. Also update the back-navigation handling
(the code that allows navigating back while analysis is running) to
increment/clear the requestId or cancel the Job so any earlier responses are
ignored and do not force state transitions (e.g., to REPORT). Ensure
submission.analyzePresentationRecording() responses are gated by this
token/cancel logic and that toAnalysisFailureAction() handling also checks it.
🪄 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: 8064cde0-e24e-4b72-bc6e-2411e0599bb2
📒 Files selected for processing (25)
Prezel/app/build.gradle.ktsPrezel/core/data/src/main/java/com/team/prezel/core/data/mapper/PracticeMapper.ktPrezel/core/data/src/main/java/com/team/prezel/core/data/repository/PracticeRepositoryImpl.ktPrezel/core/data/src/test/java/com/team/prezel/core/data/repository/practice/PracticeRepositoryImplTest.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Audience.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Category.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Purpose.ktPrezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Style.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSource.ktPrezel/core/network/src/main/java/com/team/prezel/core/network/datasource/PracticeRemoteDataSourceImpl.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/AnalysisFlowViewModel.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/audio/AudioUploadScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/component/AnalysisFileNameExt.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/navigation/AnalysisEntryBuilder.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/situation/PresentationSituationScreen.ktPrezel/feature/analysis/impl/src/main/java/com/team/prezel/feature/analysis/impl/situation/SituationOptions.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryScreen.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/HistoryViewModel.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryItemList.ktPrezel/feature/history/impl/src/main/java/com/team/prezel/feature/history/impl/component/HistoryPresentationCard.ktPrezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeScreen.ktPrezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/HomeViewModel.ktPrezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.ktPrezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/title/PresentationHero.ktPrezel/feature/terms/impl/src/main/java/com/team/prezel/feature/terms/impl/TermsViewModel.kt
💤 Files with no reviewable changes (1)
- Prezel/core/data/src/test/java/com/team/prezel/core/data/repository/practice/PracticeRepositoryImplTest.kt
✅ Files skipped from review due to trivial changes (1)
- Prezel/core/model/src/main/java/com/team/prezel/core/model/presentation/Purpose.kt
- `strings.xml` 내 발표 관련 칩(Category, Purpose, Style, Audience)의 리소스 ID를 도메인 모델 키워드에 맞춰 변경 - `HistoryPresentationCard`에서 변경된 리소스 ID를 참조하도록 수정 - `AnalysisFlowViewModel`에서 분석 요청 시 중복 요청 방지 및 취소 로직 추가 (`analyzeJob` 관리) - 분석 중 뒤로 가기 시 진행 중인 분석 작업(`Job`)을 취소하도록 개선 - `PresentationAnalysisSubmission` 생성 시 스크립트 입력 타입(`ScriptInputType`)에 따른 데이터 매핑 로직 정교화
- `aspectRatio(1f)`를 추가하여 선택 옵션 아이템이 정방형 비율을 유지하도록 수정 - 아이템 내부 `Column`에 `fillMaxSize`를 적용하여 가용 영역을 전체로 확장 - 고정 높이 `Spacer`를 가변 가중치(`weight(1f)`)로 변경하여 내부 요소 배치 최적화
📌 작업 내용
발표 분석 플로우 신규 구현
홈 화면에서 분석 진입 액션 추가
분석 모듈 추가
feature:analysis:api,feature:analysis:impl모듈 생성AnalysisNavKey.Create기반 네비게이션 엔트리 추가파일 업로드 및 미리보기 UX 추가
발표 녹음 분석 API 연동
/recording/analyzeAPI 연동을 추가했습니다.type,purpose,style,audience요청 값을 매핑했습니다.파일 업로드 안정성 개선
content://Uri를 API 업로드에 사용할 수 있도록 앱 cacheDir 임시 파일로 복사하는AnalysisFileCache를 추가했습니다.ApplicationContext의존성을 제거하고 파일 캐시 처리를 별도 helper로 분리했습니다.scriptFilePath,audioFilePath,recordingFilePath빈 문자열 방어를 추가했습니다.readBytes()로 전체 파일을 메모리에 올리지 않도록 KtorChannelProvider기반 스트리밍 업로드로 변경했습니다.🧩 관련 이슈
📸 스크린샷
2026-05-24.00.28.02.mov
Summary by CodeRabbit
릴리스 노트
새로운 기능
UI 개선
기타