-
Notifications
You must be signed in to change notification settings - Fork 0
🔗 :: (#468) 면접후기 기능 리팩토링 #473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
The head ref may contain hidden characters: "feature/468-\uBA74\uC811\uD6C4\uAE30-\uAE30\uB2A5-\uB9AC\uD329\uD1A0\uB9C1"
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 17
♻️ Duplicate comments (18)
core/common/src/main/java/team/retum/common/enums/ApplyStatus.kt (1)
12-13: DOC_FAILED 상태의 UI 처리 누락과거 리뷰에서 이미 지적되었듯이, 새로 추가된
DOC_FAILED상태가ApplyCompanyItem.kt의 색상 매핑에서 명시적으로 처리되지 않습니다.PROCESSING상태는 UI에서 올바르게 처리되고 있으나,DOC_FAILED는else분기로 빠져 의도하지 않은onPrimary색상이 할당됩니다.feature/home/src/main/java/team/retum/home/ui/ApplyCompanyItem.kt (1)
48-54: DOC_FAILED 상태가 여전히 처리되지 않습니다과거 리뷰에서 지적된 것처럼,
ApplyStatus.DOC_FAILED가when표현식에서 명시적으로 처리되지 않아else분기를 통해onPrimary색상이 할당됩니다.DOC_FAILED("서류 탈락")는 의미상FAILED("탈락")와 유사하므로error색상으로 매핑되어야 합니다.다음과 같이 수정하세요:
- ApplyStatus.FAILED, ApplyStatus.REJECTED -> JobisTheme.colors.error + ApplyStatus.FAILED, ApplyStatus.DOC_FAILED, ApplyStatus.REJECTED -> JobisTheme.colors.errorfeature/post-review/src/main/java/team/retum/post/review/viewmodel/PostExpectReviewViewModel.kt (2)
12-27: 중첩 setState 호출을 제거해야 합니다.
setAnswer와setQuestion이setState블록 안에서 다시setButtonEnabled를 호출하면서 또 다른setState를 트리거하고 있습니다.BaseViewModel.setState는MutableStateFlow.update를 사용하므로 이 패턴은 마지막 호출이 값을 덮어써 상태 플리커나 레이스를 일으킬 수 있습니다. 한 번의setState안에서 필드와buttonEnabled를 동시에 계산·반영하도록 정리해 주세요.- internal fun setAnswer(answer: String) = setState { - setButtonEnabled(answer = answer) - state.value.copy(answer = answer) - } - - internal fun setQuestion(question: String) = setState { - setButtonEnabled(question = question) - state.value.copy(question = question) - } - - private fun setButtonEnabled( - answer: String = state.value.answer, - question: String = state.value.question, - ) = setState { - state.value.copy(buttonEnabled = answer.isNotBlank() && question.isNotBlank()) - } + internal fun setAnswer(answer: String) { + val question = state.value.question + setState { + state.value.copy( + answer = answer, + buttonEnabled = answer.isNotBlank() && question.isNotBlank(), + ) + } + } + + internal fun setQuestion(question: String) { + val answer = state.value.answer + setState { + state.value.copy( + question = question, + buttonEnabled = answer.isNotBlank() && question.isNotBlank(), + ) + } + }
29-36: setEmpty가 상태를 실제로 비우지 못합니다.
setEmpty가copy만 호출하고setState로 적용하지 않아onNextClick이 이전 질문/답변 값을 그대로 전파합니다. 버튼 비활성화도 되지 않습니다. 먼저 상태를 비우고 버튼도 꺼지게 만든 뒤 사이드 이펙트를 보내도록 수정해 주세요.- internal fun setEmpty() { - with(state.value) { - copy( - question = "", - answer = "", - ) - } - onNextClick() - } + internal fun setEmpty() { + setState { + state.value.copy( + question = "", + answer = "", + buttonEnabled = false, + ) + } + onNextClick() + }feature/post-review/src/main/res/values/strings.xml (1)
30-30: 이전 리뷰 코멘트가 이미 반영되었습니다."잘못된 요청이에요"로 올바르게 수정되어 있습니다.
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt (3)
22-23: navArgument 타입 명시 필요 및 companyId는 LongType 사용이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다.
navArgument블록에type =을 명시하고,companyId는LongType으로 변경하여 수동 파싱을 제거해야 합니다.다음 diff를 적용하세요:
- navArgument(ResourceKeys.COMPANY_NAME) { NavType.StringType }, - navArgument(ResourceKeys.COMPANY_ID) { NavType.StringType }, + navArgument(ResourceKeys.COMPANY_NAME) { type = NavType.StringType }, + navArgument(ResourceKeys.COMPANY_ID) { type = NavType.LongType },
29-30: companyName URI 인코딩 및 companyId 타입 안전성 개선이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다:
- 공백이나 특수문자가 포함된 회사명은 네비게이션 경로를 깨뜨립니다.
companyId를 String으로 받아 파싱하면서 0L 디폴트를 사용하면 실패를 숨깁니다.다음 diff를 적용하세요:
+import android.net.Uri -import kotlin.text.toLongOrNull @@ - companyName = it.arguments?.getString(ResourceKeys.COMPANY_NAME) ?: "", - companyId = it.arguments?.getString(ResourceKeys.COMPANY_ID)?.toLongOrNull() ?: 0L, + companyName = requireNotNull(it.arguments).getString(ResourceKeys.COMPANY_NAME).orEmpty(), + companyId = requireNotNull(it.arguments).getLong(ResourceKeys.COMPANY_ID),
35-39: navigateToPostReview에서 companyName URI 인코딩 필요이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다.
companyName을 URI 인코딩하지 않으면 공백이나 특수문자로 인해 네비게이션이 실패할 수 있습니다.다음 diff를 적용하세요:
+import android.net.Uri @@ fun NavController.navigateToPostReview(companyName: String, companyId: Long) { - navigate("$NAVIGATION_POST_REVIEW/$companyName/$companyId") { + navigate("$NAVIGATION_POST_REVIEW/${Uri.encode(companyName)}/$companyId") { popUpTo(NAVIGATION_POST_NEXT_REVIEW) { inclusive = false } popUpTo(NAVIGATION_POST_EXPECT_REVIEW) { inclusive = false } } }feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt (2)
19-29: 질문 로드 실패 처리 누락이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다. 네트워크 실패 시 상태가 갱신되지 않아 UX가 멈춘 것처럼 보일 수 있습니다.
다음과 같이 실패 분기를 추가하는 것을 권장합니다:
viewModelScope.launch(Dispatchers.IO) { fetchQuestionsUseCase().onSuccess { setState { state.value.copy( questions = it.questions, + answers = List(it.questions.size) { "" }, + qnaElements = emptyList(), + buttonEnabled = false, ) } + }.onFailure { + setState { + state.value.copy( + questions = emptyList(), + answers = emptyList(), + qnaElements = emptyList(), + buttonEnabled = false, + ) + } + // TODO: 실패 사이드이펙트/토스트 노출 } }
47-61: answers 길이 검증 및 버튼 활성화 로직 누락이전 리뷰에서 지적된 것처럼, 질문 수와
answers길이가 불일치하면zip으로 데이터가 잘려나가 데이터 유실이 발생합니다. 또한 모든 답변이 채워졌을 때만 버튼을 활성화해야 합니다.다음과 같이 길이 검증과 버튼 활성화를 함께 반영해주세요:
internal fun setQuestion() { setState { with(state.value) { - val updatedQuestions = questions.map { it.id } - copy( - qnaElements = updatedQuestions.zip(answers).map { (q, a) -> - PostReviewContent( - question = q, - answer = a, - ) - }, - ) + val ids = questions.map { it.id } + val pairs = ids.zip(answers) + val allAnswered = answers.size == ids.size && answers.all { it.isNotBlank() } + copy( + qnaElements = pairs.map { (q, a) -> PostReviewContent(question = q, answer = a) }, + answers = answers, + buttonEnabled = allAnswered, + ) } } }feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt (2)
55-55: answers 리스트 고정 크기로 인한 크래시 가능성이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다. 질문 수(
state.questions.size)와answers크기가 불일치하면answers[page]에서 IndexOutOfBoundsException이 발생합니다.다음 diff를 적용하세요:
- val answers = remember { mutableStateListOf("", "", "") } + val answers = remember(state.questions) { + mutableStateListOf(*Array(state.questions.size) { "" }) + }또는
toMutableStateList()사용:+import androidx.compose.runtime.toMutableStateList @@ - val answers = remember { mutableStateListOf("", "", "") } + val answers = remember(state.questions) { + List(state.questions.size) { "" }.toMutableStateList() + }
166-166: 마지막 페이지 하드코딩 제거 필요이전 리뷰에서 지적된 문제가 아직 해결되지 않았습니다. 페이지 수가 3이 아닐 경우 흐름이 깨집니다. 마지막 인덱스를 동적으로 계산해야 합니다.
다음 diff를 적용하세요:
onClick = { setQuestion() coroutineScope.launch { - if (pagerState.currentPage != 2) pagerState.animateScrollToPage(pagerState.currentPage + 1) else onPostExpectReviewClick() + val lastPageIndex = pagerState.pageCount - 1 + if (pagerState.currentPage < lastPageIndex) { + pagerState.animateScrollToPage(pagerState.currentPage + 1) + } else { + onPostExpectReviewClick() + } } },feature/post-review/src/main/java/team/retum/post/review/ui/PostExpectReviewScreen.kt (1)
88-154: 하단 배치가 깨지지 않도록 Column 에 전체 높이를 부여하세요
Spacer(weight = 1f)로 버튼을 하단에 붙이려면 부모Column이 가용 높이를 차지해야 합니다. 현재Modifier.fillMaxSize()가 없어 버튼이 중간에 머무릅니다. 기존 지적과 동일하니 아래와 같이 수정해 주세요.-import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize @@ - Column { + Column( + modifier = Modifier.fillMaxSize(), + ) {feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
33-39: 라우트 인코딩 누락으로 인한 네비게이션 실패JSON 안에
/,?,%등이 포함되면 경로가 분리되어 목적지 매칭이 실패하고, 예외를 메시지 없이 던져 디버깅도 어렵습니다. Uri.encode/Uri.decode와 명시적인 예외 메시지를 추가해 주세요.+import android.net.Uri @@ fun NavController.navigateToPostNextReview(reviewData: PostReviewData) { - navigate("$NAVIGATION_POST_NEXT_REVIEW/${reviewData.toJsonString()}") + val payload = Uri.encode(reviewData.toJsonString()) + navigate("$NAVIGATION_POST_NEXT_REVIEW/$payload") } internal fun NavBackStackEntry.getReviewData(): PostReviewData { - val reviewData = arguments?.getString(ResourceKeys.REVIEW_DATA) ?: throw NullPointerException() - return reviewData.toReviewData() + val encoded = requireNotNull(arguments?.getString(ResourceKeys.REVIEW_DATA)) { + "Missing '${ResourceKeys.REVIEW_DATA}' argument for $NAVIGATION_POST_NEXT_REVIEW" + } + return Uri.decode(encoded).toReviewData() }feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewScreen.kt (1)
704-708: 힌트 문자열을 필드 용도에 맞게 교체해주세요.면접관 수 입력 필드에 여전히
search문자열을 사용하고 있어 UX가 어색합니다. 면접관 수에 맞는 새로운 string 리소스를 추가한 뒤 해당 리소스를 사용해주세요.- hint = stringResource(id = R.string.search), + hint = stringResource(id = R.string.hint_interviewer_count),※
hint_interviewer_count리소스 추가도 함께 반영해야 합니다.feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (3)
107-116: [DUPLICATE] Compose SnapshotStateList를 IO 스레드에서 변경하는 스레드 안전성 문제Line 114에서
techs.addAll(it.codes)를Dispatchers.IO에서 호출하면 스냅샷 충돌 및 크래시 위험이 있습니다. 이 문제는 이전 리뷰에서 이미 지적되었습니다.메인 스레드에서 변경하거나 StateFlow로 변경하세요:
internal fun fetchCodes(keyword: String?) = viewModelScope.launch(Dispatchers.IO) { fetchCodeUseCase( keyword = keyword, type = CodeType.JOB, parentCode = null, ).onSuccess { - techs.addAll(it.codes) + withContext(Dispatchers.Main) { + techs.addAll(it.codes) + } } }
130-140: [DUPLICATE] 버튼 활성화 로직 역전 및 NPE 위험Line 137에서
!state.value.keyword?.isNotEmpty()!!는 키워드가 입력되면 버튼을 비활성화하는 역전된 로직이며, keyword가 null일 경우 NPE 위험이 있습니다. 이 문제는 이전 리뷰에서 이미 지적되었습니다.수정 적용:
else -> { - setState { state.value.copy(buttonEnabled = !state.value.keyword?.isNotEmpty()!!) } + setState { + state.value.copy( + buttonEnabled = !state.value.keyword.isNullOrEmpty() + ) + } }
187-197: [DUPLICATE] interviewerCount 파싱 시 크래시 위험Line 191에서
count.toInt()는 빈 문자열이나 숫자가 아닌 입력 시 NumberFormatException을 발생시킵니다. 이 문제는 이전 리뷰에서 이미 지적되었습니다.안전한 파싱 적용:
internal fun onNextClick() { with(state.value) { + val parsedCount = count.toIntOrNull() + if (parsedCount == null || parsedCount <= 0) { + postSideEffect(PostReviewSideEffect.BadRequest) + return + } postSideEffect(PostReviewSideEffect.MoveToNext( companyId = companyId, - interviewerCount = count.toInt(), + interviewerCount = parsedCount, jobCode = selectedTech ?: 0, interviewType = interviewType, location = interviewLocation, )) } }
🧹 Nitpick comments (10)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt (1)
33-47: 에러 핸들링 추가를 고려하세요.
fetchCodeUseCase실패 시 처리가 없어 사용자가 빈 목록만 보게 됩니다. 에러 상태를 추가하거나 로깅을 고려하세요.fetchCodeUseCase( keyword = null, type = CodeType.JOB, parentCode = null, ).onSuccess { setState { state.value.copy( majorList = it.codes, ) } + }.onFailure { + // 로깅 또는 에러 상태 처리 고려 }feature/post-review/src/main/java/team/retum/post/review/model/PostReviewData.kt (1)
30-38: deprecated API 사용을 피하세요.
URLEncoder.encode(String, String)및URLDecoder.decode(String, String)는 deprecated API입니다.StandardCharsets를 사용하여 현대적이고 안전한 API로 변경하는 것을 권장합니다.다음과 같이 수정하세요:
+import java.nio.charset.StandardCharsets + internal fun PostReviewData.toJsonString(): String { val json = Json.encodeToString(this) - return URLEncoder.encode(json, "UTF-8") + return URLEncoder.encode(json, StandardCharsets.UTF_8) } internal fun String.toReviewData(): PostReviewData { - val decoded = URLDecoder.decode(this, "UTF-8") + val decoded = URLDecoder.decode(this, StandardCharsets.UTF_8) return Json.decodeFromString<PostReviewData>(decoded) }feature/review/src/main/java/team/retum/review/ui/ReviewScreen.kt (1)
77-89:items()확장 함수 대신itemsIndexed()사용을 권장합니다.
items(state.reviews.size)와 인덱스 접근 대신, LazyColumn의itemsIndexed()확장 함수를 사용하면 더 간결하고 안전합니다.다음과 같이 리팩토링할 수 있습니다:
+import androidx.compose.foundation.lazy.itemsIndexed + - LazyColumn { - items(state.reviews.size) { - val review = state.reviews[it] + LazyColumn { + itemsIndexed(state.reviews) { _, review -> ReviewItems( companyImageUrl = review.companyLogoUrl, companyName = review.companyName, reviewId = review.reviewId, writer = review.writer, major = review.major, onReviewDetailClick = onReviewDetailClick, ) } }feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (1)
38-38: TODO 주석: API 요청 최적화 필요빈번한 API 요청 문제를 해결해야 합니다. 캐싱, 요청 중복 제거, 또는 상태 관리 개선 등의 방법을 고려해보세요.
이 문제를 해결하는 구현을 생성하거나 새로운 이슈를 열어드릴까요?
feature/review/src/main/java/team/retum/review/viewmodel/SearchReviewsViewModel.kt (1)
33-60: 검색어가 비면 결과도 초기화해 주세요검색어를 모두 지우면
fetchReviews()는 호출되지 않는데, 이전 검색 결과가 그대로 남아 있어 화면과 검색어가 불일치합니다. 공백일 때는 리스트와 빈 상태 플래그를 초기화하는 편이 자연스럽습니다.viewModelScope.launch { - state.map { it.keyword }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS).collect { - if (!it.isNullOrBlank()) { - fetchReviews() - } + state.map { it.keyword }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS).collect { keyword -> + if (keyword.isNullOrBlank()) { + setState { + state.value.copy( + reviews = emptyList(), + showRecruitmentsEmptyContent = false, + ) + } + } else { + fetchReviews() } } }feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt (5)
113-122: 파라미터 수가 많아 데이터 클래스 사용 권장8개의 파라미터를 개별적으로 전달하고 있습니다. 유지보수성과 가독성 향상을 위해 데이터 클래스로 그룹화하는 것을 고려해보세요.
다음과 같이 데이터 클래스를 사용할 수 있습니다:
data class StudentInfoData( val writer: String, val major: String, val year: Int, val companyName: String, val location: InterviewLocation, val type: InterviewType, val interviewerCount: Int, val selectedTabIndex: Int, ) @Composable private fun StudentInfo(data: StudentInfoData) { // implementation }
133-140: 복잡한 문자열 포맷팅 로직 단순화 권장중첩된
stringResource호출과 조건문이 가독성을 저해합니다. 로직을 별도로 추출하는 것을 고려해보세요.다음과 같이 리팩토링할 수 있습니다:
val reviewType = if (selectedTabIndex != 0) { stringResource(id = R.string.reviewed_question) } else { stringResource(id = R.string.interview_review) } JobisText( text = stringResource( id = R.string.review_writer_title, writer, reviewType, ), style = JobisTypography.PageTitle, )
254-258: 텍스트 오버플로우 처리 명시 권장
maxLines = 3을 설정했지만JobisText의 기본overflow속성에 의존하고 있습니다. 명시적으로TextOverflow.Ellipsis를 지정하는 것이 좋습니다.JobisText의 기본값이 Ellipsis이므로 현재는 문제없지만, 명시성을 위해 다음과 같이 작성할 수 있습니다:
Text( text = buildAnnotatedString { /* ... */ }, style = JobisTypography.Description, modifier = Modifier .padding(top = 12.dp) .fillMaxWidth(0.5f), maxLines = 3, overflow = TextOverflow.Ellipsis, // 명시적 지정 )
274-274: 사용하지 않는 index 변수 제거 권장
forEachIndexed를 사용하지만index변수를 사용하지 않습니다.forEach로 변경하는 것을 고려하세요.- review.forEachIndexed { index, reviewItem -> + review.forEach { reviewItem ->
344-350: 답변 텍스트 오버플로우 처리 검토
maxLines = 3을 설정했지만 긴 답변이 잘릴 경우 사용자가 전체 내용을 볼 방법이 없습니다. 확장 상태에서는 전체 텍스트를 표시하거나, 최소한 말줄임표가 표시되도록 해야 합니다.확장 상태에서는 전체 답변을 표시하는 것을 고려하세요:
JobisText( text = reviewItem.answer, color = JobisTheme.colors.inverseOnSurface, style = JobisTypography.Description, textAlign = TextAlign.Center, - maxLines = 3, + // 확장된 상태에서는 전체 텍스트 표시 )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
feature/bookmark/src/main/res/drawable/ic_empty_bookmark.pngis excluded by!**/*.png
📒 Files selected for processing (86)
app/build.gradle.kts(1 hunks)app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt(3 hunks)app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt(3 hunks)app/src/main/java/team/retum/jobisandroidv2/root/RootNavigation.kt(2 hunks)app/src/main/java/team/retum/jobisandroidv2/root/RootScreen.kt(5 hunks)app/src/main/java/team/retum/jobisandroidv2/ui/BottomMenu.kt(2 hunks)app/src/main/java/team/retum/jobisandroidv2/ui/BottomNavigationBar.kt(1 hunks)app/src/main/res/drawable/ic_review.xml(1 hunks)app/src/main/res/values/strings.xml(1 hunks)core/common/src/main/java/team/retum/common/enums/ApplyStatus.kt(1 hunks)core/common/src/main/java/team/retum/common/enums/InterviewLocation.kt(1 hunks)core/common/src/main/java/team/retum/common/enums/InterviewType.kt(1 hunks)core/common/src/main/java/team/retum/common/enums/ReviewProcess.kt(1 hunks)core/common/src/main/java/team/retum/common/utils/ResourceKeys.kt(1 hunks)core/data/src/main/java/team/retum/data/repository/review/ReviewRepository.kt(1 hunks)core/data/src/main/java/team/retum/data/repository/review/ReviewRepositoryImpl.kt(2 hunks)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/foundation/JobisColor.kt(1 hunks)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/foundation/JobisIcon.kt(1 hunks)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/review/ReviewContent.kt(1 hunks)core/design-system/src/main/res/drawable/ic_asterisk.xml(1 hunks)core/design-system/src/main/res/drawable/ic_success.xml(1 hunks)core/domain/src/main/java/team/retum/usecase/entity/FetchReviewDetailEntity.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/entity/FetchReviewsEntity.kt(2 hunks)core/domain/src/main/java/team/retum/usecase/entity/MyReviews.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/entity/PostReviewEntity.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/entity/QuestionsEntity.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/entity/ReviewsCountEntity.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/usecase/review/FetchMyReviewUseCase.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/usecase/review/FetchQuestionsUseCase.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/usecase/review/FetchReviewsCountUseCase.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/usecase/review/FetchReviewsUseCase.kt(1 hunks)core/network/src/main/java/team/retum/network/api/ReviewApi.kt(2 hunks)core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSource.kt(1 hunks)core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSourceImpl.kt(2 hunks)core/network/src/main/java/team/retum/network/di/RequestUrls.kt(1 hunks)core/network/src/main/java/team/retum/network/model/request/PostReviewRequest.kt(1 hunks)core/network/src/main/java/team/retum/network/model/response/FetchMyReviewResponse.kt(1 hunks)core/network/src/main/java/team/retum/network/model/response/FetchQuestionsResponse.kt(1 hunks)core/network/src/main/java/team/retum/network/model/response/FetchReviewDetailResponse.kt(1 hunks)core/network/src/main/java/team/retum/network/model/response/FetchReviewsCountResponse.kt(1 hunks)core/network/src/main/java/team/retum/network/model/response/FetchReviewsResponse.kt(1 hunks)feature/bookmark/src/main/java/team/retum/bookmark/ui/BookmarkScreen.kt(1 hunks)feature/company/src/main/java/team/retum/company/navigation/CompanyDetailsNavigation.kt(1 hunks)feature/company/src/main/java/team/retum/company/ui/CompanyDetailsScreen.kt(3 hunks)feature/company/src/main/java/team/retum/company/viewmodel/CompanyDetailsViewModel.kt(1 hunks)feature/home/src/main/java/team/retum/home/ui/ApplyCompanyItem.kt(1 hunks)feature/home/src/main/java/team/retum/home/ui/HomeScreen.kt(1 hunks)feature/mypage/src/main/java/team/retum/jobis/navigation/MyPageNavigation.kt(1 hunks)feature/mypage/src/main/java/team/retum/jobis/ui/MyPageScreen.kt(3 hunks)feature/post-review/.gitignore(1 hunks)feature/post-review/build.gradle.kts(1 hunks)feature/post-review/src/main/java/team/retum/post/review/model/PostReviewData.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostExpectReviewScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostExpectReviewViewModel.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt(1 hunks)feature/post-review/src/main/res/values/strings.xml(1 hunks)feature/review/build.gradle.kts(2 hunks)feature/review/src/main/java/team/retum/review/navigation/PostReviewNavigation.kt(0 hunks)feature/review/src/main/java/team/retum/review/navigation/ReviewDetailsNavigation.kt(1 hunks)feature/review/src/main/java/team/retum/review/navigation/ReviewFilterNavigation.kt(1 hunks)feature/review/src/main/java/team/retum/review/navigation/ReviewNavigation.kt(1 hunks)feature/review/src/main/java/team/retum/review/navigation/ReviewsNavigation.kt(0 hunks)feature/review/src/main/java/team/retum/review/navigation/SearchReviewsNavigation.kt(1 hunks)feature/review/src/main/java/team/retum/review/ui/PostReviewScreen.kt(0 hunks)feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt(2 hunks)feature/review/src/main/java/team/retum/review/ui/ReviewFilterScreen.kt(1 hunks)feature/review/src/main/java/team/retum/review/ui/ReviewScreen.kt(1 hunks)feature/review/src/main/java/team/retum/review/ui/ReviewsScreen.kt(0 hunks)feature/review/src/main/java/team/retum/review/ui/SearchReviewScreen.kt(1 hunks)feature/review/src/main/java/team/retum/review/ui/component/ReviewItems.kt(1 hunks)feature/review/src/main/java/team/retum/review/viewmodel/PostReviewViewModel.kt(0 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt(2 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt(1 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewViewModel.kt(1 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewsViewModel.kt(0 hunks)feature/review/src/main/java/team/retum/review/viewmodel/SearchReviewsViewModel.kt(1 hunks)feature/review/src/main/res/values/strings.xml(1 hunks)settings.gradle.kts(1 hunks)
💤 Files with no reviewable changes (6)
- feature/review/src/main/java/team/retum/review/navigation/PostReviewNavigation.kt
- feature/review/src/main/java/team/retum/review/viewmodel/PostReviewViewModel.kt
- feature/review/src/main/java/team/retum/review/viewmodel/ReviewsViewModel.kt
- feature/review/src/main/java/team/retum/review/ui/PostReviewScreen.kt
- feature/review/src/main/java/team/retum/review/navigation/ReviewsNavigation.kt
- feature/review/src/main/java/team/retum/review/ui/ReviewsScreen.kt
🧰 Additional context used
🧬 Code graph analysis (31)
app/build.gradle.kts (1)
buildSrc/src/main/kotlin/GradlePluginExtensions.kt (1)
implementation(15-17)
feature/review/build.gradle.kts (1)
buildSrc/src/main/kotlin/GradlePluginExtensions.kt (1)
implementation(15-17)
feature/post-review/src/main/java/team/retum/post/review/ui/PostExpectReviewScreen.kt (4)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/textfield/JobisTextField.kt (1)
JobisTextField(355-436)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/button/JobisButton.kt (1)
JobisButton(322-340)
feature/review/src/main/java/team/retum/review/navigation/SearchReviewsNavigation.kt (1)
feature/review/src/main/java/team/retum/review/ui/SearchReviewScreen.kt (1)
SearchReview(19-33)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (1)
PostReviewComplete(29-47)
feature/review/src/main/java/team/retum/review/ui/ReviewScreen.kt (4)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewViewModel.kt (4)
setYear(23-25)setCode(19-21)setLocation(31-33)clearReview(35-44)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisLargeTopAppBar(153-181)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/button/JobisIconButton.kt (1)
JobisIconButton(74-93)feature/review/src/main/java/team/retum/review/ui/component/ReviewItems.kt (1)
ReviewItems(25-81)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/review/ReviewContent.kt (1)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/card/JobisCard.kt (1)
JobisCard(43-81)
feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (2)
setState(60-64)postSideEffect(106-110)
feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt (5)
app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt (1)
navigateToPostExpectReview(149-151)feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
navigateToPostExpectReview(31-33)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/textfield/JobisTextField.kt (1)
JobisTextField(355-436)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt (2)
setAnswer(31-41)setQuestion(47-61)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (2)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt (1)
navigateToPostReview(35-40)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostExpectReviewScreen.kt (1)
PostExpectReview(34-76)
app/src/main/java/team/retum/jobisandroidv2/root/RootScreen.kt (1)
feature/review/src/main/java/team/retum/review/navigation/ReviewNavigation.kt (1)
review(10-24)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt (1)
PostNextReview(45-81)
feature/post-review/build.gradle.kts (1)
buildSrc/src/main/kotlin/GradlePluginExtensions.kt (2)
android(8-10)implementation(15-17)
feature/review/src/main/java/team/retum/review/ui/ReviewFilterScreen.kt (4)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/button/JobisButton.kt (1)
JobisButton(322-340)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/checkbox/CheckBox.kt (1)
JobisCheckBox(21-66)
feature/review/src/main/java/team/retum/review/ui/component/ReviewItems.kt (1)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)
feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt (5)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/tab/TabBar.kt (1)
TabBar(21-68)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/card/JobisCard.kt (1)
JobisCard(43-81)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/empty/EmptyContent.kt (1)
EmptyContent(21-56)
feature/review/src/main/java/team/retum/review/navigation/ReviewNavigation.kt (1)
feature/review/src/main/java/team/retum/review/ui/ReviewScreen.kt (1)
Review(24-50)
feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (2)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (2)
setState(60-64)postSideEffect(106-110)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostExpectReviewViewModel.kt (1)
setButtonEnabled(22-27)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewScreen.kt (1)
PostReview(66-135)
feature/review/src/main/java/team/retum/review/navigation/ReviewFilterNavigation.kt (1)
feature/review/src/main/java/team/retum/review/ui/ReviewFilterScreen.kt (1)
ReviewFilter(47-66)
feature/review/src/main/java/team/retum/review/ui/SearchReviewScreen.kt (4)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/textfield/JobisTextField.kt (1)
JobisTextField(355-436)feature/review/src/main/java/team/retum/review/ui/component/ReviewItems.kt (1)
ReviewItems(25-81)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/empty/EmptyContent.kt (1)
EmptyContent(21-56)
feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostExpectReviewViewModel.kt (3)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (2)
setState(60-64)postSideEffect(106-110)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (2)
setButtonEnabled(130-140)onNextClick(187-197)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt (1)
onNextClick(63-71)
feature/review/src/main/java/team/retum/review/viewmodel/SearchReviewsViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewScreen.kt (9)
app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt (1)
navigateToPostNextReview(141-143)feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
navigateToPostNextReview(33-35)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisLargeTopAppBar(153-181)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/button/JobisButton.kt (1)
JobisButton(322-340)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (6)
setButtonClear(179-185)setInterviewType(151-158)setInterviewLocation(160-167)setChecked(142-149)setKeyword(59-67)setSelectedTech(104-105)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/button/JobisIconButton.kt (1)
JobisIconButton(74-93)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/textfield/JobisTextField.kt (1)
JobisTextField(355-436)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/checkbox/CheckBox.kt (1)
JobisCheckBox(21-66)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
feature/review/src/main/java/team/retum/review/navigation/ReviewDetailsNavigation.kt (1)
feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt (1)
ReviewDetails(48-75)
feature/company/src/main/java/team/retum/company/viewmodel/CompanyDetailsViewModel.kt (1)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewViewModel.kt (1)
fetchReviewsUseCase(14-63)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt (7)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt (1)
postReview(15-33)feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
postNextReview(17-31)feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
postExpectReview(15-29)feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt (1)
postReviewComplete(10-22)feature/review/src/main/java/team/retum/review/navigation/ReviewDetailsNavigation.kt (1)
reviewDetails(13-29)feature/review/src/main/java/team/retum/review/navigation/ReviewFilterNavigation.kt (1)
reviewFilter(10-18)feature/review/src/main/java/team/retum/review/navigation/SearchReviewsNavigation.kt (1)
searchReview(10-22)
🪛 detekt (1.23.8)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt
[warning] 38-38: A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions.
(detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause)
🔇 Additional comments (50)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/review/ReviewContent.kt (2)
22-23: 좋은 리팩토링입니다!
reviewId타입을String에서Long으로 변경하고onClick시그니처를(Long) -> Unit으로 단순화한 것은 훌륭한 개선입니다. ID에Long타입을 사용하는 것이 더 표준적이며 타입 안정성을 높여줍니다. 또한 네비게이션에는 리뷰 ID만 필요하므로 API가 더욱 명확해졌습니다.
27-27: 구현이 올바릅니다.
onClick핸들러에reviewId만 전달하도록 구현이 정확하게 업데이트되었습니다.writer파라미터는 여전히 UI 표시에 사용되고 있어 적절합니다.core/network/src/main/java/team/retum/network/di/RequestUrls.kt (1)
85-90: 리뷰 API 경로 리팩토링 확인 완료
상세·목록·질문·집계·내 리뷰 경로가 공통path기반으로 정리되어 새 Retrofit 시그니처와 일관됩니다. GET/POST 경로도 상수 재사용이 가능해 혼선이 줄어들 것 같습니다.core/common/src/main/java/team/retum/common/enums/ApplyStatus.kt (1)
6-6: SEND 라벨 변경 의도를 확인하세요."지원 중"에서 "전송"으로 라벨이 변경되었습니다. 이 변경이 의도된 것이며 모든 UI 텍스트와 사용자 경험상 일관성이 있는지 확인이 필요합니다.
feature/home/src/main/java/team/retum/home/ui/ApplyCompanyItem.kt (1)
51-51: LGTM: SEND와 PROCESSING 상태 처리가 올바르게 추가됨새로 추가된
SEND와PROCESSING상태를secondary색상으로 매핑한 것은 적절합니다.feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt (2)
61-91: LGTM!필터 선택 토글 로직이 올바르게 구현되었습니다. 동일한 값 재선택 시 선택 해제하는 패턴이 사용자 경험에 적합합니다.
94-113: LGTM!
ReviewsFilterState데이터 클래스가 적절하게 정의되었습니다.@Immutable어노테이션과 팩토리 메서드 패턴이 Compose 모범 사례에 부합합니다.feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt (1)
34-36: LGTM!
setTabIndex함수는 탭 인덱스를 올바르게 업데이트하며, 구현이 명확합니다.feature/bookmark/src/main/java/team/retum/bookmark/ui/BookmarkScreen.kt (1)
135-135: 드로어블 리소스 변경 - 시각적 적절성 검증 필요
ic_empty_bookmark에서 설계 시스템의 제네릭ic_empty로 변경되었습니다. 새 리소스는 디자인 시스템에 존재하며(core/design-system/src/main/res/drawable/ic_empty.png), 이 변경이 BookmarkScreen에만 적용되어 있습니다.확인 사항:
- 제네릭
ic_empty가 북마크 빈 상태 컨텍스트에 시각적으로 적절한지 검증- 이것이 의도된 디자인 시스템 통합인지 확인
contentDescription은 여전히 "empty bookmark"이므로, 선택된 아이콘이 북마크 컨텍스트와 일치하는지 확인해주세요.
feature/post-review/src/main/res/values/strings.xml (1)
1-58: LGTM!문자열 리소스가 화면별로 잘 정리되어 있으며, 한국어 표기법도 적절합니다.
app/src/main/java/team/retum/jobisandroidv2/ui/BottomNavigationBar.kt (1)
28-28: LGTM!하단 네비게이션에 Review 메뉴 아이템이 올바르게 추가되었습니다.
settings.gradle.kts (1)
47-47: LGTM!새로운 post-review 모듈이 올바르게 추가되었습니다.
app/src/main/res/values/strings.xml (1)
6-6: LGTM!후기 기능을 위한 문자열 리소스가 올바르게 추가되었습니다.
core/design-system/src/main/res/drawable/ic_asterisk.xml (1)
1-9: LGTM!필수 입력 표시를 위한 별표 아이콘이 올바르게 정의되었습니다.
feature/company/src/main/java/team/retum/company/viewmodel/CompanyDetailsViewModel.kt (1)
45-53: LGTM!
fetchReviewsUseCase의 확장된 시그니처에 맞게 파라미터가 올바르게 전달되고 있습니다. 회사 상세 화면에서는companyId만 필요하므로 나머지 파라미터를 null로 전달하는 것이 적절합니다.app/src/main/java/team/retum/jobisandroidv2/ui/BottomMenu.kt (2)
10-10: LGTM!
NAVIGATION_REVIEWimport가 올바르게 추가되었습니다.
29-33: LGTM!Review 하단 메뉴 아이템이 기존 패턴과 일관되게 잘 정의되었습니다.
core/common/src/main/java/team/retum/common/enums/ReviewProcess.kt (1)
4-8: 검증 완료 - 모든 변경이 올바르게 적용되었습니다.검증 결과,
ReviewProcess열거형의 상수 변경(QUESTION, TECH, FINISH → INTERVIEW_TYPE, INTERVIEW_LOCATION, TECH_STACK, INTERVIEWER_COUNT, SUMMARY)이 코드베이스 전체에 완전하고 일관되게 적용되었습니다:
- 제거된 상수에 대한 직접 참조 없음
- 문자열 기반 참조 없음
- 모든 사용처에서 새로운 상수로 업데이트됨 (PostReviewViewModel.kt, PostReviewScreen.kt)
breaking change이지만 변경이 완전하게 처리되어 컴파일 오류나 런타임 문제가 발생하지 않습니다.
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/foundation/JobisIcon.kt (1)
40-40: 변경 사항이 적절합니다.기존 패턴을 따라 새로운 아이콘 상수를 추가했습니다.
core/design-system/src/main/res/drawable/ic_success.xml (1)
1-9: 벡터 드로어블이 올바르게 정의되었습니다.표준 벡터 드로어블 형식을 따르고 있으며, 성공 완료 화면에서 사용될 아이콘으로 적절합니다.
core/network/src/main/java/team/retum/network/model/response/FetchReviewDetailResponse.kt (2)
23-27: 내부 데이터 클래스 리팩토링이 적절합니다.
Detail에서QnAs로 명명이 개선되었고,area필드가 제거되어 구조가 단순화되었습니다. 명명이 더 명확해졌습니다.
10-20: 검증 완료 - 모든 변경사항이 올바르게 반영되었습니다.새로운 필드 구조가 모든 계층에서 일관되게 처리되고 있습니다. JSON 필드명(review_id, company_name, company_name 등)이 모두 적절한 snake_case로 설정되어 있으며, API → DataSource → Repository → UseCase → ViewModel 전체 파이프라인에서
toEntity()확장함수를 통해 정상적으로 매핑되고 있습니다. QnAs 중첩 클래스도 별도 매핑함수로 지원되고 있습니다.app/build.gradle.kts (1)
118-118: 모듈 의존성 추가가 적절합니다.새로운
post-review기능 모듈을 앱에 통합하는 표준적인 방식입니다.app/src/main/res/drawable/ic_review.xml (1)
1-9: 벡터 드로어블이 올바르게 정의되었습니다.하단 네비게이션용 리뷰 아이콘이 적절한 크기(20dp)와 색상으로 정의되었습니다.
core/domain/src/main/java/team/retum/usecase/entity/ReviewsCountEntity.kt (1)
5-11: 엔티티 정의와 매핑이 적절합니다.단순하고 명확한 엔티티 구조를 따르고 있으며, 네트워크 응답에서 도메인 엔티티로의 매핑이 올바르게 구현되었습니다.
totalPageCount에Long타입을 사용한 것도 적절합니다.core/common/src/main/java/team/retum/common/enums/InterviewType.kt (1)
3-7: 백엔드 API 스펙 확인이 필요합니다.현재
InterviewType열거형은 명시적인 직렬화 어노테이션이 없으며, Moshi의 기본 동작에 따라INDIVIDUAL,GROUP,OTHER형식으로 직렬화됩니다. 코드베이스에서 커스텀 어댑터나 테스트 파일을 찾을 수 없었으므로, 백엔드 API가 실제로 이 형식을 기대하는지 백엔드 스펙을 통해 직접 확인해야 합니다.core/common/src/main/java/team/retum/common/enums/InterviewLocation.kt (1)
3-8: 백엔드 API 스펙을 통해 enum 직렬화 형식 검증 필요
InterviewLocationenum은 Moshi에 의해 enum 상수명이 그대로 직렬화됩니다 (DAEJEON,SEOUL,GYEONGGI,OTHER).InterviewType과 동일하게 명시적인 직렬화 어노테이션이 없으므로, 백엔드가 정확히 이 형식을 기대하는지 API 문서 또는 백엔드 팀을 통해 확인하세요.enum 상수는 이미 요청/응답 클래스(
PostReviewRequest.kt,FetchReviewDetailResponse.kt)에서 사용 중이므로, 기존 백엔드 계약이 일치하는지 재확인하시기 바랍니다.core/domain/src/main/java/team/retum/usecase/entity/MyReviews.kt (1)
1-23: LGTM!도메인 엔티티와 변환 로직이 깔끔하게 구현되어 있습니다. 적절한 가시성 제어자 사용과 불변성 보장이 잘 되어 있습니다.
core/network/src/main/java/team/retum/network/model/response/FetchReviewsCountResponse.kt (1)
1-9: LGTM!간단하고 명확한 응답 모델입니다. Moshi 어댑터 생성과 JSON 매핑이 적절하게 구성되어 있습니다.
feature/review/src/main/java/team/retum/review/navigation/ReviewNavigation.kt (1)
1-28: LGTM!Jetpack Compose Navigation을 활용한 깔끔한 네비게이션 구성입니다. 콜백 기반 설계가 적절하며 확장 함수를 통한 API가 사용하기 편리합니다.
core/domain/src/main/java/team/retum/usecase/usecase/review/FetchMyReviewUseCase.kt (1)
1-13: LGTM!간단하고 명확한 Use Case 구현입니다.
runCatching을 통한 에러 처리와 엔티티 변환이 적절하게 구성되어 있습니다.core/network/src/main/java/team/retum/network/model/response/FetchMyReviewResponse.kt (1)
1-15: LGTM!네트워크 응답 모델이 깔끔하게 구성되어 있습니다. Moshi 어댑터 생성과 JSON 필드 매핑이 적절합니다.
core/network/src/main/java/team/retum/network/model/response/FetchReviewsResponse.kt (1)
12-17: LGTM!응답 모델의 필드 업데이트가 적절합니다.
reviewId의 타입 변경(String → Long)과 새로운 필드들(companyName,companyLogoUrl,major) 추가가 확장된 기능 요구사항과 잘 맞습니다.feature/review/src/main/java/team/retum/review/ui/component/ReviewItems.kt (1)
26-81: LGTM!리뷰 아이템 컴포저블의 구현이 깔끔합니다. 이미지 URL이 비어있을 때의 폴백 처리, 텍스트 오버플로우 처리, 클릭 이벤트 전달 등이 모두 적절하게 구현되어 있습니다.
feature/company/src/main/java/team/retum/company/navigation/CompanyDetailsNavigation.kt (1)
15-15: Long 기반 리뷰 식별자로의 전환이 적절합니다.리뷰 상세 네비게이션 파라미터를
(String, String)에서(Long)으로 변경하여 타입 안정성과 일관성이 개선되었습니다.core/domain/src/main/java/team/retum/usecase/usecase/review/FetchQuestionsUseCase.kt (1)
7-13: LGTM!UseCase의 구현이 간결하고 적절합니다.
runCatching을 사용하여 에러를Result로 래핑하는 방식이 적절하며, 호출자가 성공/실패를 처리할 수 있도록 되어 있습니다.core/domain/src/main/java/team/retum/usecase/entity/QuestionsEntity.kt (1)
16-16: 네이밍 패턴 일관성 확인 완료 - 지적 사항 없음프로젝트 전체의
toEntity()함수 패턴을 검토한 결과,QuestionsEntity.kt의 함수명과 구조는 이미 프로젝트의 표준 네이밍 규칙과 완벽하게 일치합니다. 18개 이상의 엔티티 파일에서internal fun ResponseType.toEntity() = EntityType(...)패턴을 따르고 있으며,QuestionsEntity.kt도 동일한 규칙을 준수하고 있습니다. TODO 주석은 제거해도 무방합니다.Likely an incorrect or invalid review comment.
core/domain/src/main/java/team/retum/usecase/usecase/review/FetchReviewsUseCase.kt (1)
12-14: 파라미터 위임 검증 완료확장된 필터 인자가 repository 계층으로 정확히 전달되고 runCatching 으로 오류를 포장하는 흐름이 명확합니다.
core/data/src/main/java/team/retum/data/repository/review/ReviewRepositoryImpl.kt (1)
20-49: 데이터소스 위임이 올바르게 정렬되었습니다repository 계층에서 추가된 필터 파라미터를 전부 data source 에 네임드 인자로 넘겨주어 매핑 오류 위험이 없습니다.
core/domain/src/main/java/team/retum/usecase/entity/PostReviewEntity.kt (1)
7-21: 도메인 엔티티 구조 변경이 적절합니다.면접 후기 엔티티에 새로운 필드들(interviewType, location, jobCode, interviewerCount, question, answer)이 추가되었고, PostReviewContentEntity의 question 타입이 String에서 Long으로 변경되어 식별자로서 더 적합한 타입을 사용하게 되었습니다.
core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSource.kt (1)
27-31: 새로운 리뷰 관련 메서드 추가가 적절합니다.fetchQuestions(), fetchReviewsCount(), fetchMyReviews() 메서드가 추가되어 리뷰 기능의 API 표면이 확장되었습니다.
app/src/main/java/team/retum/jobisandroidv2/root/RootScreen.kt (1)
59-62: 리뷰 관련 콜백 추가 및 전달이 올바르게 구현되었습니다.새로운 콜백들(onReviewFilterClick, onSearchReviewClick, onReviewDetailClick)이 Root → RootScreen → review 컴포저블로 일관되게 전달되고 있으며, onPostReviewClick의 시그니처도 추가 파라미터를 받도록 적절히 업데이트되었습니다.
Also applies to: 99-101, 135-138, 185-189
app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt (1)
201-203: reviewId 타입이 Long으로 변경된 것이 적절합니다.이전 String 타입에서 Long으로 변경되어 도메인 모델과 일관성을 갖추게 되었습니다.
app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt (1)
118-133: 포스트 리뷰 네비게이션 체인이 올바르게 구성되었습니다.postReview → postNextReview → postExpectReview → postReviewComplete 흐름이 적절한 콜백과 함께 구성되었습니다. 각 단계가 필요한 데이터를 다음 단계로 전달하고 있습니다.
core/network/src/main/java/team/retum/network/api/ReviewApi.kt (1)
24-47: 리뷰 API 확장이 적절하게 구현되었습니다.fetchReviews가 페이지네이션 및 다중 필터링을 지원하도록 확장되었고, 새로운 엔드포인트들(fetchQuestions, fetchReviewsCount, fetchMyReviews)이 추가되어 리뷰 기능을 충분히 지원합니다. 모든 쿼리 파라미터가 nullable로 선언되어 선택적 필터링이 가능합니다.
feature/review/src/main/java/team/retum/review/ui/ReviewFilterScreen.kt (1)
47-66: UI 구조와 컴포넌트 구성이 잘 되어 있습니다.ReviewFilter 진입점부터 세부 섹션들(Skills, Years, InterviewType, Location)까지 잘 구조화되어 있으며, 애니메이션과 상태 관리가 적절하게 구현되었습니다. FlowRow를 사용한 레이아웃과 색상 전환 애니메이션이 좋은 사용자 경험을 제공합니다.
Also applies to: 68-119, 123-373
feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt (4)
77-110: 명시적 데이터 플로우로 개선된 구조ViewModel 상태에 직접 의존하지 않고 명시적인 파라미터를 통한 데이터 전달 방식으로 변경하여 테스트 가능성과 재사용성이 향상되었습니다.
162-172: 열거형 매핑 로직이 올바르게 구현됨
InterviewType과InterviewLocation열거형의 모든 케이스를 exhaustive하게 처리하고 있어 안전합니다.
194-201: 깔끔한 위임 패턴
InterviewReview가ReviewContent로 명확하게 위임하고 있습니다.
207-207: 빈 값 검증 로직 확인 필요현재
!review.answer.isBlank() || !review.question.isBlank()로직은 둘 중 하나라도 비어있지 않으면 콘텐츠를 표시합니다. 의도가 "둘 다 존재해야 표시"라면&&연산자를 사용해야 합니다.의도한 로직이 다음 중 어느 것인지 확인하세요:
- 현재: 질문 또는 답변 중 하나라도 있으면 표시
- 제안: 질문과 답변이 모두 있어야 표시 (
&&사용)// 제안하는 변경 if (!review.answer.isBlank() && !review.question.isBlank()) { // show content }
feature/mypage/src/main/java/team/retum/jobis/ui/MyPageScreen.kt
Outdated
Show resolved
Hide resolved
...re/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt
Outdated
Show resolved
Hide resolved
feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt
Outdated
Show resolved
Hide resolved
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt
Outdated
Show resolved
Hide resolved
feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt
Show resolved
Hide resolved
feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt
Show resolved
Hide resolved
| companion object { | ||
| var code: Long? = null | ||
| var year: Int? = null | ||
| var interviewType: InterviewType? = null | ||
| var location: InterviewLocation? = null | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
companion object의 mutable static state를 제거하세요.
companion object에 var로 선언된 필터 상태는 여러 문제를 야기합니다:
- ViewModel 인스턴스 간 상태 공유로 인한 예기치 않은 동작
- 메모리 누수 위험 (정적 참조가 계속 유지)
- 단위 테스트 불가능 (테스트 간 격리 불가)
ReviewsFilterState에 동일한 속성이 이미 존재하여 중복
모든 필터 상태는 ReviewsFilterState 인스턴스를 통해 관리되어야 합니다.
다음 diff를 적용하여 companion object를 제거하세요:
- companion object {
- var code: Long? = null
- var year: Int? = null
- var interviewType: InterviewType? = null
- var location: InterviewLocation? = null
- }
-
init {Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt
around lines 22 to 27, remove the companion object that defines mutable static
vars (code, year, interviewType, location) and stop using static state; instead,
add those properties to and manage them via the existing ReviewsFilterState
instance held by the ViewModel (initialize/update/read from the state object,
expose changes via LiveData/StateFlow as appropriate), eliminate any references
to ReviewFilterViewModel.Companion.* across the codebase, and adjust
constructors/usage so each ViewModel instance uses its own ReviewsFilterState to
avoid shared mutable static state.
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (11)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt (2)
22-27: companion object의 mutable static state를 제거해야 합니다.이전 리뷰에서 지적된 critical 이슈가 여전히 해결되지 않았습니다. companion object의 가변 정적 변수들은 다음 문제를 야기합니다:
- ViewModel 인스턴스 간 상태 공유로 인한 예측 불가능한 동작
- 메모리 누수 위험
- 테스트 격리 불가능
ReviewsFilterState에 동일한 속성이 이미 존재하여 중복모든 필터 상태는
ReviewsFilterState를 통해 관리되어야 합니다.다음 diff를 적용하여 companion object를 제거하세요:
- companion object { - var code: Long? = null - var year: Int? = null - var interviewType: InterviewType? = null - var location: InterviewLocation? = null - } - init {
29-31: init에서getLocalYears()를 호출해야 합니다.
getLocalYears()가 호출되지 않아ReviewsFilterState.years가 항상 빈 리스트로 유지됩니다. 이는 년도 필터 UI가 제대로 작동하지 않는 원인이 됩니다.다음 diff를 적용하세요:
init { fetchCodes() + getLocalYears() }feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
20-29: JSON 역직렬화 오류 처리 확인 필요
it.getReviewData()(라인 25)가 JSON 파싱 실패 시 예외를 던질 수 있습니다. 이전 리뷰에서 지적된 것처럼,toReviewData()구현에 try-catch 블록이 없는 경우 앱이 크래시될 수 있습니다.
getReviewData()및toReviewData()구현에서 JSON 파싱 실패와 URL 디코딩 실패를 처리하고, 실패 시 적절한 에러 UI를 표시하거나 안전하게 뒤로 이동하도록 개선하세요.feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
38-41: 예외 메시지 누락 및 오류 처리 미흡라인 39에서 메시지 없이
NullPointerException()을 던지는 것은 디버깅을 어렵게 만듭니다. 또한toReviewData()호출 시 JSON 파싱 실패를 처리하지 않아 앱 크래시 위험이 있습니다.다음과 같이 개선하세요:
internal fun NavBackStackEntry.getReviewData(): PostReviewData { - val reviewData = arguments?.getString(ResourceKeys.REVIEW_DATA) ?: throw NullPointerException() - return reviewData.toReviewData() + val raw = requireNotNull(arguments?.getString(ResourceKeys.REVIEW_DATA)) { + "Missing '${ResourceKeys.REVIEW_DATA}' argument for $NAVIGATION_POST_NEXT_REVIEW" + } + return try { + raw.toReviewData() + } catch (e: Exception) { + throw IllegalArgumentException("Failed to parse review data: ${e.message}", e) + } }feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt (2)
24-29: 에러 처리 누락으로 실패 시 사용자 피드백 없음
fetchReviewDetailsUseCase가 실패할 경우 아무런 상태 변화도 없어서 사용자는 로딩이 끝났는지조차 알 수 없습니다. 최소한 실패 플래그를 상태에 기록해 UI가 에러를 표시하거나 재시도 버튼을 노출할 수 있도록 해야 합니다.internal fun fetchReviewDetails() { viewModelScope.launch(Dispatchers.IO) { - fetchReviewDetailsUseCase(state.value.reviewId).onSuccess { - setState { state.value.copy(reviewDetail = it) } - } + fetchReviewDetailsUseCase(state.value.reviewId) + .onSuccess { + setState { state.value.copy(reviewDetail = it, hasError = false) } + } + .onFailure { + setState { state.value.copy(hasError = true) } + } } }internal data class ReviewDetailsState( + val hasError: Boolean, val selectedTabIndex: Int, val reviewId: Long, val reviewDetail: FetchReviewDetailEntity, ) { companion object { fun getInitialState() = ReviewDetailsState( + hasError = false, selectedTabIndex = 0, reviewId = 0L, reviewDetail = FetchReviewDetailEntity(
45-59: 초기 상태가 실제 데이터처럼 보이는 기본값 사용초기 상태에서
InterviewType.INDIVIDUAL과InterviewLocation.GYEONGGI를 넣으면 데이터 로딩 전에도 실제 값처럼 렌더링돼 사용자가 혼동할 수 있습니다. 중립값(OTHER)으로 바꾸거나 로딩 상태를 분리해 주세요.type = InterviewType.INDIVIDUAL, - location = InterviewLocation.GYEONGGI, + type = InterviewType.OTHER, + location = InterviewLocation.OTHER,feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt (1)
173-177: 마지막 페이지 인덱스 하드코딩으로 흐름 깨짐질문 수가 3개가 아니면
pagerState.currentPage != 2조건이 바로 틀어져서 다음/완료 전환이 망가집니다. 마지막 페이지를 동적으로 계산하세요.JobisButton( text = stringResource(id = R.string.next), color = ButtonColor.Primary, onClick = { setQuestion() coroutineScope.launch { - if (pagerState.currentPage != 2) pagerState.animateScrollToPage(pagerState.currentPage + 1) else onPostExpectReviewClick() + val lastPageIndex = pagerState.pageCount - 1 + if (pagerState.currentPage < lastPageIndex) { + pagerState.animateScrollToPage(pagerState.currentPage + 1) + } else { + onPostExpectReviewClick() + } } }, )app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt (1)
55-58: 콜백 시그니처 불일치 확인 필요
navigator::navigateToPostReview가 메서드 참조로 전달되고 있으나,navigateToPostReview는(companyName: String, companyId: Long)파라미터를 필요로 합니다. 이는 파라미터가 없는 콜백으로 전달될 수 없습니다.다음 스크립트로 실제 호출 방식을 확인하여 컴파일 오류 여부를 검증하세요:
#!/bin/bash # root 함수의 onPostReviewClick 파라미터 타입 확인 ast-grep --pattern $'fun NavGraphBuilder.root( $$$ onPostReviewClick: $TYPE, $$$ )' # RootScreen에서 onPostReviewClick 호출 방식 확인 rg -n 'onPostReviewClick' --type=kt -A 2 -B 2feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt (2)
95-103: 데이터 유효성 검증 권장
year와interviewerCount를 검증 없이toString()으로 변환하고 있습니다. 서버에서 0이나 음수 값이 전달될 경우 의미 없는 문자열이 UI에 표시될 수 있습니다.도메인 계층(
FetchReviewDetailEntity)에 유효성 검증을 추가하는 것을 권장합니다. 다음 스크립트로 현재 검증 로직 여부를 확인하세요:#!/bin/bash # FetchReviewDetailEntity에 유효성 검증 어노테이션이 있는지 확인 rg -n '@field:|@Min|@Max|@Positive' core/domain/src/main/java/team/retum/usecase/entity/FetchReviewDetailEntity.kt # 응답 모델에서 유효성 검증 확인 fd FetchReviewDetailResponse.kt --exec cat {}
60-64: TODO 주석 제거 필요프로덕션 코드에 TODO 주석이 남아있습니다. 실제 데이터 처리가 완료되었다면 주석을 제거하고, 미완료 작업이라면 구현을 완료해야 합니다.
다음과 같이 TODO 주석을 제거하세요:
LaunchedEffect(reviewId) { - // TODO : 실 값 들어왔을 때 UI 호출 reviewDetailsViewModel.setReviewId(reviewId) reviewDetailsViewModel.fetchReviewDetails() }feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (1)
159-177: 면접관 수 파싱 시 예외 처리 누락
count.toInt()가 빈 문자열이나 숫자가 아닌 입력을 받으면NumberFormatException이 발생합니다. 안전한 파싱과 유효성 검증을 추가해야 합니다.다음 diff를 적용하여 안전한 파싱을 구현하세요:
internal fun onNextClick() { with(state.value) { - if (interviewType != null && interviewLocation != null) { - postSideEffect( - PostReviewSideEffect.MoveToNext( - companyId = companyId, - interviewerCount = count.toInt(), - jobCode = selectedTech ?: 0, - interviewType = interviewType, - location = interviewLocation, - ), - ) + val parsedCount = count.toIntOrNull() + if (parsedCount == null || parsedCount <= 0) { + postSideEffect(PostReviewSideEffect.BadRequest) + return + } + if (interviewType != null && interviewLocation != null) { + postSideEffect( + PostReviewSideEffect.MoveToNext( + companyId = companyId, + interviewerCount = parsedCount, + jobCode = selectedTech ?: 0, + interviewType = interviewType, + location = interviewLocation, + ), + ) } else { postSideEffect( PostReviewSideEffect.SelectTechAndCount ) } } }
🧹 Nitpick comments (4)
feature/mypage/src/main/java/team/retum/jobis/ui/MyPageScreen.kt (2)
15-16: 미사용 import를 제거하거나 LazyColumn 사용을 고려하세요.
LazyColumn과items를 import했지만 실제로는forEach를 사용하고 있습니다.
198-205: 이전 NPE 위험이 해결되었습니다. 성능 최적화를 위해 LazyColumn 사용을 고려하세요.
reviewableItem을 로컬 변수로 캡처하여 이전 리뷰에서 지적된 NPE 위험이 해결되었습니다.다만 Compose에서
forEach를 사용하면 각 항목이 개별적으로 추적되지 않아 재구성 시 비효율적일 수 있습니다. 목록이 길어질 경우 이미 import한LazyColumn과items()를 사용하는 것을 권장합니다.적용 가능한 diff:
- state.reviewableCompany?.let { reviewable -> - reviewable.forEach { reviewableItem -> - WriteInterviewReview( - companyName = reviewableItem.name, - onClick = { onPostReviewClick(reviewableItem.name, reviewableItem.id) }, - ) - } - } + state.reviewableCompany?.let { reviewable -> + LazyColumn { + items( + items = reviewable, + key = { it.id } + ) { reviewableItem -> + WriteInterviewReview( + companyName = reviewableItem.name, + onClick = { onPostReviewClick(reviewableItem.name, reviewableItem.id) }, + ) + } + } + }feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (1)
29-45: 사용하지 않는 파라미터 제거 고려
onBackPressed파라미터가 정의되어 있지만(라인 31) 실제로 사용되지 않습니다. 이 파라미터가 향후 사용 예정이 아니라면 제거를 고려하세요.internal fun PostReviewComplete( - onBackPressed: () -> Unit, navigateToMyPage: () -> Unit, postReviewViewModel: PostReviewViewModel = hiltViewModel(), )feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (1)
66-88: 예외 처리 범위 확장 권장
postReview메서드가BadRequestException만 처리하고 있습니다. 네트워크 오류나 기타 예외 상황에 대한 처리를 추가하면 사용자 경험이 개선됩니다.다음과 같이 일반 예외 처리를 추가하는 것을 고려해보세요:
internal fun postReview(reviewData: PostReviewData) { viewModelScope.launch(Dispatchers.IO) { postReviewUseCase( postReviewRequest = PostReviewEntity( interviewType = reviewData.interviewType, location = reviewData.location, companyId = reviewData.companyId, jobCode = reviewData.jobCode, interviewerCount = reviewData.interviewerCount, qnaElements = reviewData.qnaElements.map { it.toEntity() }, question = reviewData.question, answer = reviewData.answer, ), ).onSuccess { postSideEffect(PostReviewSideEffect.Success) }.onFailure { when (it) { is BadRequestException -> { postSideEffect(PostReviewSideEffect.BadRequest) } + else -> { + postSideEffect(PostReviewSideEffect.BadRequest) + } } } } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (35)
app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt(4 hunks)app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt(3 hunks)core/data/src/main/java/team/retum/data/repository/review/ReviewRepository.kt(1 hunks)core/data/src/main/java/team/retum/data/repository/review/ReviewRepositoryImpl.kt(2 hunks)core/domain/src/main/java/team/retum/usecase/entity/FetchReviewDetailEntity.kt(1 hunks)core/domain/src/main/java/team/retum/usecase/usecase/review/FetchReviewDetailUseCase.kt(1 hunks)core/network/src/main/java/team/retum/network/api/ReviewApi.kt(2 hunks)core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSource.kt(1 hunks)core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSourceImpl.kt(2 hunks)core/network/src/main/java/team/retum/network/model/response/FetchReviewDetailResponse.kt(1 hunks)feature/company/src/main/java/team/retum/company/navigation/CompanyDetailsNavigation.kt(1 hunks)feature/company/src/main/java/team/retum/company/ui/CompanyDetailsScreen.kt(4 hunks)feature/home/src/main/java/team/retum/home/ui/ApplyCompanyItem.kt(1 hunks)feature/mypage/src/main/java/team/retum/jobis/navigation/MyPageNavigation.kt(2 hunks)feature/mypage/src/main/java/team/retum/jobis/ui/MyPageScreen.kt(4 hunks)feature/mypage/src/main/java/team/retum/jobis/viewmodel/MyPageViewModel.kt(2 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewScreen.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostExpectReviewViewModel.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt(1 hunks)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt(1 hunks)feature/post-review/src/main/res/values/strings.xml(1 hunks)feature/review/build.gradle.kts(1 hunks)feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt(2 hunks)feature/review/src/main/java/team/retum/review/ui/ReviewScreen.kt(1 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt(2 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt(1 hunks)feature/review/src/main/java/team/retum/review/viewmodel/ReviewViewModel.kt(1 hunks)feature/review/src/main/java/team/retum/review/viewmodel/SearchReviewsViewModel.kt(1 hunks)feature/review/src/main/res/values/strings.xml(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- feature/mypage/src/main/java/team/retum/jobis/navigation/MyPageNavigation.kt
- feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt
- feature/review/src/main/java/team/retum/review/viewmodel/SearchReviewsViewModel.kt
- feature/review/build.gradle.kts
- feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewScreen.kt
- core/domain/src/main/java/team/retum/usecase/entity/FetchReviewDetailEntity.kt
- feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt
- feature/company/src/main/java/team/retum/company/navigation/CompanyDetailsNavigation.kt
- feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostExpectReviewViewModel.kt
- feature/review/src/main/java/team/retum/review/ui/ReviewScreen.kt
🧰 Additional context used
🧬 Code graph analysis (12)
feature/mypage/src/main/java/team/retum/jobis/viewmodel/MyPageViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewDetailsViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (3)
app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt (1)
navigateToMyPage(134-136)feature/mypage/src/main/java/team/retum/jobis/navigation/MyPageNavigation.kt (1)
navigateToMyPage(33-38)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)
app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt (7)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewNavigation.kt (1)
postReview(15-33)feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
postNextReview(18-32)feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
postExpectReview(16-30)feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt (1)
postReviewComplete(10-22)feature/review/src/main/java/team/retum/review/navigation/ReviewDetailsNavigation.kt (1)
reviewDetails(13-29)feature/review/src/main/java/team/retum/review/navigation/ReviewFilterNavigation.kt (1)
reviewFilter(10-18)feature/review/src/main/java/team/retum/review/navigation/SearchReviewsNavigation.kt (1)
searchReview(10-22)
feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt (5)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
navigateToPostExpectReview(32-34)app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt (1)
navigateToPostExpectReview(154-156)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/textfield/JobisTextField.kt (1)
JobisTextField(355-436)feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostNextReviewViewModel.kt (2)
setAnswer(34-44)setQuestion(50-64)
feature/review/src/main/java/team/retum/review/ui/ReviewDetailsScreen.kt (5)
core/design-system/src/main/java/team/retum/jobisdesignsystemv2/appbar/JobisTopAppBar.kt (1)
JobisSmallTopAppBar(124-144)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/tab/TabBar.kt (1)
TabBar(21-68)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/text/JobisText.kt (1)
JobisText(24-45)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/card/JobisCard.kt (1)
JobisCard(43-81)core/design-system/src/main/java/team/retum/jobisdesignsystemv2/empty/EmptyContent.kt (1)
EmptyContent(21-56)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (1)
PostReviewComplete(29-45)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (1)
setState(60-64)
feature/review/src/main/java/team/retum/review/viewmodel/ReviewViewModel.kt (2)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (2)
setState(60-64)postSideEffect(106-110)feature/review/src/main/java/team/retum/review/viewmodel/SearchReviewsViewModel.kt (1)
fetchReviewsUseCase(19-67)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostNextReviewScreen.kt (1)
PostNextReview(47-90)
feature/post-review/src/main/java/team/retum/post/review/viewmodel/PostReviewViewModel.kt (1)
core/common/src/main/java/team/retum/common/base/BaseViewModel.kt (2)
setState(60-64)postSideEffect(106-110)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
feature/post-review/src/main/java/team/retum/post/review/ui/PostExpectReviewScreen.kt (1)
PostExpectReview(34-76)
🪛 detekt (1.23.8)
feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt
[warning] 39-39: A call to the default constructor of an exception was detected. Instead one of the constructor overloads should be called. This allows to provide more meaningful exceptions.
(detekt.exceptions.ThrowingExceptionsWithoutMessageOrCause)
🔇 Additional comments (22)
feature/home/src/main/java/team/retum/home/ui/ApplyCompanyItem.kt (1)
48-53: 이전 리뷰 코멘트가 완벽하게 반영되었으며, when 표현식이 완전 매칭입니다.검증 결과,
ApplyStatusenum의 모든 10개 케이스가 when 표현식에서 처리되고 있습니다:
DOC_FAILED가 error 색상 분기에 올바르게 추가됨 (이전 리뷰 반영 완료)SEND와PROCESSING이 secondary 색상에 적절하게 매핑됨- 모든 상태가 의미론적으로 올바른 색상에 분류됨
else 분기 제거로 인해 when 표현식이 완전 매칭이 되었으며, 컴파일 시점에 누락된 케이스를 감지할 수 있습니다. 코드 변경사항은 문제가 없습니다.
feature/review/src/main/java/team/retum/review/viewmodel/ReviewFilterViewModel.kt (1)
49-56:getLocalYears()가 coroutine 없이 순수 계산만 수행합니다.현재 구현은
setState를 직접 호출하여 년도 범위를 계산합니다. 계산이 매우 가볍기 때문에 별도의 dispatcher가 필요하지 않으며, 이는 적절한 구현입니다. 다만 init에서 호출되지 않는 문제는 여전히 해결되어야 합니다.feature/review/src/main/res/values/strings.xml (1)
1-42: 문자열 리소스 구조가 잘 정리되었습니다.이전 리뷰에서 제기된 제거된 문자열 리소스 참조 문제는 작성자가 확인 완료했습니다. 새로 추가된 문자열 리소스들은 리뷰 상세, 필터, 검색 화면을 위한 포괄적인 내용을 포함하고 있으며, 명명 규칙과 구조가 일관적입니다.
feature/mypage/src/main/java/team/retum/jobis/viewmodel/MyPageViewModel.kt (2)
101-107: 리뷰 가능한 회사 목록 저장 변경 사항 검증 완료변경사항이 제대로 구현되었음을 확인했습니다. ViewModel의 상태 필드 타입 변경(
List<ReviewableCompaniesEntity.ReviewableCompanyEntity>?)이 UI 컴포넌트에서 올바르게 처리되고 있습니다.MyPageScreen.kt의 UI 레이어에서 null 안전성 검사와 리스트 반복 처리가 구현되어 있습니다:
- Null 체크:
state.reviewableCompany?.let { reviewable ->- 리스트 반복:
reviewable.forEach { reviewableItem ->별도의 조치가 필요하지 않습니다.
124-144: 통합 검증 완료 - 모든 우려사항 확인됨
reviewableCompany필드의 타입 변경(단일 엔티티 → 리스트)이 일관성 있게 구현되었습니다.
- MyPageScreen.kt (198-205):
state.reviewableCompany?.let으로 안전하게 처리하고forEach를 통해 각 회사를 반복 렌더링- 콜백 연결: 각 항목의
onClick에서onPostReviewClick(reviewableItem.name, reviewableItem.id)정확하게 전달- 상태 초기화: ViewModel에서 정상적으로 null로 설정되고, 로드 시 리스트 할당 (
it.companies)- 빈 상태 처리: null/빈 리스트 모두 안전하게 처리 (렌더링 안 함)
모든 원래 검증 요청사항이 확인되었으며 구현이 완전합니다.
feature/company/src/main/java/team/retum/company/ui/CompanyDetailsScreen.kt (2)
156-156: 콜백 전달 방식이 올바르게 업데이트되었습니다.새로운 시그니처
() -> Unit에 맞춰navigateToReviews를 직접 전달하도록 변경한 것이 적절합니다. 이전에는companyId와companyName파라미터와 함께 호출했지만, 이제는 파라미터 없는 콜백으로 올바르게 전달하고 있습니다.
54-55: 네비게이션 시그니처 변경은 올바르게 구현되었습니다.검증 결과, 변경 사항이 일관성 있게 적용되어 있습니다:
navigateToReviewDetails: (Long) → Unit- ReviewDetails 화면은 reviewId만으로 필요한 모든 데이터(회사명 포함)를 조회navigateToReviews: () → Unit- Review 목록 화면이 별도의 매개변수 없이 전체 리뷰 조회- 모든 호출 지점이 업데이트됨 (MainNavigation.kt, CompanyDetailsScreen.kt)
- Review 엔티티에 companyName, companyId 포함되어 있어 네비게이션을 통한 추가 전달 불필요
리뷰 화면들은 reviewId만 받아 백엔드에서 전체 리뷰 데이터를 조회하도록 설계되어 있으므로, 회사 정보 접근에 문제가 없습니다.
core/domain/src/main/java/team/retum/usecase/usecase/review/FetchReviewDetailUseCase.kt (1)
10-12: 타입 일관성 개선 완료
reviewId타입을Long으로 변경하여 데이터 레이어 전반의 타입 일관성을 개선했습니다. 불필요한 타입 변환을 제거하고 타입 안정성을 높였습니다.core/network/src/main/java/team/retum/network/model/response/FetchReviewDetailResponse.kt (1)
8-28: 응답 모델 구조 검증 필요
FetchReviewDetailResponse에qnaResponse: List<QnAs>(라인 18)와 별도로 최상위 레벨에question,answer필드(라인 19-20)가 존재합니다. 이 구조가 의도된 것인지 확인이 필요합니다:
- 만약 최상위 필드가 "예상 질문"을 나타내는 것이라면 명확한 네이밍(예:
expectedQuestion,expectedAnswer)을 권장합니다.- 만약 중복 데이터라면 제거를 검토하세요.
백엔드 API 명세를 확인하여 이 필드들의 의도와 사용처를 명확히 해주세요.
feature/post-review/src/main/java/team/retum/post/review/navigation/PostReviewCompleteNavigation.kt (1)
1-26: LGTM!표준 Compose Navigation 패턴을 올바르게 구현했습니다. 라우트 상수 정의, NavGraphBuilder 확장, NavController 확장이 모두 일관성 있게 작성되었습니다.
feature/post-review/src/main/java/team/retum/post/review/navigation/PostExpectReviewNavigation.kt (1)
32-34: URI 인코딩 적용 완료
Uri.encode()를 사용하여 JSON 문자열을 안전하게 인코딩했습니다. 이전 리뷰 피드백이 반영되었습니다.feature/post-review/src/main/java/team/retum/post/review/ui/PostReviewCompleteScreen.kt (1)
47-88: LGTM!완료 화면 UI 구현이 깔끔하고 디자인 시스템을 일관성 있게 사용했습니다.
core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSource.kt (2)
15-23: 필터 파라미터 null 허용 설계 적절함모든 필터 파라미터를 nullable로 설계하여 선택적 필터링을 유연하게 지원할 수 있습니다.
25-31: LGTM!
reviewId타입을Long으로 변경하고 새로운 데이터 조회 메서드들을 추가하여 API 표면을 확장했습니다. 타입 일관성이 개선되었습니다.feature/post-review/src/main/java/team/retum/post/review/navigation/PostNextReviewNavigation.kt (2)
22-30: navArgument 타입 지정 완료, JSON 파싱 오류 처리 추가 필요
navArgument에type = NavType.StringType를 올바르게 지정했습니다(라인 24).그러나
it.getReviewData()(라인 27) 호출 시 JSON 파싱 실패 예외를 처리하지 않으면 앱이 크래시될 수 있습니다. try-catch로 감싸고 실패 시 적절한 오류 처리를 추가하세요.
34-36: URI 인코딩 적용 완료
Uri.encode()를 사용하여 JSON 문자열을 안전하게 인코딩했습니다.core/network/src/main/java/team/retum/network/datasource/review/ReviewDataSourceImpl.kt (2)
21-39: LGTM!많은 파라미터를 가진 메서드에서 named arguments를 사용하여 가독성과 정확성을 높였습니다.
RequestHandler를 통한 일관된 오류 처리 패턴도 올바르게 적용되었습니다.
41-51: LGTM!
reviewId타입을Long으로 정확히 사용하고, 새로운 데이터 조회 메서드들을 일관된 패턴으로 구현했습니다.app/src/main/java/team/retum/jobisandroidv2/JobisNavigator.kt (1)
19-220: 네비게이션 구조 개선이 잘 이루어졌습니다.면접 후기 작성 플로우를 위한 새로운 네비게이션 메서드들이 추가되었고, 파라미터 전달도 적절하게 구현되었습니다. 이전 리뷰에서 지적된 사용되지 않는 파라미터 문제도 해결된 것으로 확인됩니다.
app/src/main/java/team/retum/jobisandroidv2/navigation/MainNavigation.kt (1)
118-133: post-review 네비게이션 플로우가 잘 구성되었습니다.새로운 면접 후기 작성 플로우를 위한 네비게이션 그래프가 적절히 연결되어 있습니다. 각 단계별 화면 전환과 백 네비게이션 처리가 올바르게 구현되었습니다.
core/network/src/main/java/team/retum/network/api/ReviewApi.kt (1)
25-47: API 확장이 잘 설계되었습니다.
fetchReviews의 쿼리 파라미터 방식 변경과 새로운 엔드포인트 추가가 적절합니다. nullable 쿼리 파라미터를 사용하여 선택적 필터링을 지원하고,reviewId를 String에서 Long으로 변경하여 타입 안전성도 개선되었습니다.feature/post-review/src/main/res/values/strings.xml (1)
1-64: 문자열 리소스가 잘 구성되었습니다.면접 후기 작성 플로우를 위한 포괄적인 문자열 리소스가 추가되었습니다. 한글 맞춤법도 올바르게 적용되었고(이전 리뷰에서 지적된 "잘못된" 띄어쓰기 수정됨), 일관된 명명 규칙을 따르고 있습니다.
개요
https://drive.google.com/file/d/1kPvDZ2Ta0tqk69P0ZkUZwY8VD3ph81UC/view?usp=share_link
면접 후기 작성 플로우 영상입니다
용량 제한으로 구글 드라이브로 올립니다
작업 내용
할 말
Summary by CodeRabbit
새로운 기능
개선사항