-
Notifications
You must be signed in to change notification settings - Fork 0
feat - ai 요약 기능 추가 #70
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
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.
Pull request overview
This PR adds AI summary functionality to display user profile summaries in Korean and Japanese, expands job options in the required info form, and implements gender-based control for matching selection. The changes include API integration, UI updates, and data persistence improvements.
Key changes:
- AI summary API integration with bilingual (Korean/Japanese) display in the waiting screen
- Expanded job options from 5 to 24 choices in the required info form
- Gender storage in DataStore and gender-based matching selection controls
- Removed toast notifications when matching queries fail
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| AiSummaryResponse.kt | New data model for AI summary API response with Korean and Japanese text fields |
| MatchingService.kt | Added AI summary endpoint and renamed matching selection method to gender-specific name |
| DataStoreManager.kt | Added gender storage and retrieval functionality |
| RequiredInfoMapper.kt | Expanded job mapping from 5 to 24 job types including healthcare, education, and public service roles |
| Step5Content.kt | Updated UI job options list to match expanded mapper entries |
| RequiredInfoViewModel.kt | Added gender persistence when submitting required info form |
| HomeViewModel.kt | Added AI summary fetching logic and refactored state updates to use update pattern |
| HomeScreen.kt | Added AI summary display in waiting screen with loading and error states |
| MainRouteScreen.kt | Added gender-based control flow for enabling/disabling matching selection |
| Localization.kt | Added Japanese translations for AI summary UI text |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| _uiState.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| matchings = emptyList(), | ||
| selectedMatching = null, | ||
| isWaiting = true | ||
| ) | ||
| } | ||
| } else { | ||
| _uiState.value = HomeUiState(matchings = data) | ||
| _uiState.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| matchings = data, | ||
| selectedMatching = null, | ||
| isWaiting = false | ||
| ) | ||
| } |
Copilot
AI
Jan 8, 2026
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.
The state updates in fetchFemaleMatchings don't preserve the AI summary fields (aiSummaryKo, aiSummaryJa, isAiSummaryLoading, aiSummaryError). When matchings data is updated, these fields will be reset to their default null/false values because the copy operation only specifies isLoading, matchings, selectedMatching, and isWaiting. This will cause the AI summary to disappear if matchings are refreshed after the AI summary has loaded.
| _uiState.update { | ||
| it.copy( | ||
| isLoading = false, | ||
| matchings = emptyList(), | ||
| selectedMatching = null, | ||
| isWaiting = true | ||
| ) | ||
| } |
Copilot
AI
Jan 8, 2026
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.
The state updates don't preserve the AI summary fields (aiSummaryKo, aiSummaryJa, isAiSummaryLoading, aiSummaryError). When matchings fail to load, these fields will be reset to their default values, causing the AI summary to disappear.
| isLoading = false, | ||
| matchings = emptyList(), | ||
| selectedMatching = null, | ||
| isWaiting = true |
Copilot
AI
Jan 8, 2026
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.
The state updates don't preserve the AI summary fields (aiSummaryKo, aiSummaryJa, isAiSummaryLoading, aiSummaryError). When a matching is selected, these fields will be reset to their default values, causing the AI summary to disappear from the waiting screen if the user returns to it.
| isWaiting = true | |
| isWaiting = true, | |
| aiSummaryKo = it.aiSummaryKo, | |
| aiSummaryJa = it.aiSummaryJa, | |
| isAiSummaryLoading = it.isAiSummaryLoading, | |
| aiSummaryError = it.aiSummaryError |
|
|
||
| @GET("members/status") | ||
| suspend fun getMyStatus(): MyStatusResponse | ||
| fun femaleSelectMatching(@Path("matchingId") matchingId: Long): Call<Void> |
Copilot
AI
Jan 8, 2026
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.
Function name 'femaleSelectMatching' uses gender-specific naming which may not scale well if male users need similar functionality in the future. Consider a more generic name like 'selectMatching' or 'confirmMatching' that doesn't encode gender into the API method name.
| fun femaleSelectMatching(@Path("matchingId") matchingId: Long): Call<Void> | |
| fun selectMatching(@Path("matchingId") matchingId: Long): Call<Void> |
| fun job(koreanLabel: String): String? = when (koreanLabel) { | ||
| "의사" -> "DOCTOR" | ||
| "약사" -> "PHARMACIST" | ||
| "간호사" -> "NURSE" | ||
| "교사" -> "TEACHER" | ||
| "교수" -> "PROFESSOR" | ||
| "변호사" -> "LAWYER" | ||
| "회계사" -> "ACCOUNTANT" | ||
| "엔지니어" -> "ENGINEER" | ||
| "개발자" -> "DEVELOPER" | ||
| "디자이너" -> "DESIGNER" | ||
| "공무원" -> "CIVIL_SERVANT" | ||
| "경찰" -> "POLICE" | ||
| "소방관" -> "FIREFIGHTER" | ||
| "군인" -> "MILITARY" | ||
| "사업가" -> "ENTREPRENEUR" | ||
| "경영인" -> "CEO" | ||
| "금융인" -> "FINANCE" | ||
| "연구원" -> "RESEARCHER" | ||
| "예술가" -> "ARTIST" | ||
| "운동선수" -> "ATHLETE" | ||
| "프리랜서" -> "FREELANCER" | ||
| "학생" -> "STUDENT" | ||
| "무직" -> "UNEMPLOYED" | ||
| "상관없음" -> "ANY" | ||
| "상관 없음" -> "ANY" | ||
| "기타" -> "OTHER" | ||
| else -> null |
Copilot
AI
Jan 8, 2026
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.
The mapping for "상관없음" and "상관 없음" (with different spacing) both map to "ANY". This duplication could be avoided by normalizing whitespace in the input string before checking, which would make the mapping more maintainable and handle other potential spacing variations.
| fun job(koreanLabel: String): String? = when (koreanLabel) { | |
| "의사" -> "DOCTOR" | |
| "약사" -> "PHARMACIST" | |
| "간호사" -> "NURSE" | |
| "교사" -> "TEACHER" | |
| "교수" -> "PROFESSOR" | |
| "변호사" -> "LAWYER" | |
| "회계사" -> "ACCOUNTANT" | |
| "엔지니어" -> "ENGINEER" | |
| "개발자" -> "DEVELOPER" | |
| "디자이너" -> "DESIGNER" | |
| "공무원" -> "CIVIL_SERVANT" | |
| "경찰" -> "POLICE" | |
| "소방관" -> "FIREFIGHTER" | |
| "군인" -> "MILITARY" | |
| "사업가" -> "ENTREPRENEUR" | |
| "경영인" -> "CEO" | |
| "금융인" -> "FINANCE" | |
| "연구원" -> "RESEARCHER" | |
| "예술가" -> "ARTIST" | |
| "운동선수" -> "ATHLETE" | |
| "프리랜서" -> "FREELANCER" | |
| "학생" -> "STUDENT" | |
| "무직" -> "UNEMPLOYED" | |
| "상관없음" -> "ANY" | |
| "상관 없음" -> "ANY" | |
| "기타" -> "OTHER" | |
| else -> null | |
| fun job(koreanLabel: String): String? { | |
| val normalized = koreanLabel.replace("\\s+".toRegex(), "") | |
| return when (normalized) { | |
| "의사" -> "DOCTOR" | |
| "약사" -> "PHARMACIST" | |
| "간호사" -> "NURSE" | |
| "교사" -> "TEACHER" | |
| "교수" -> "PROFESSOR" | |
| "변호사" -> "LAWYER" | |
| "회계사" -> "ACCOUNTANT" | |
| "엔지니어" -> "ENGINEER" | |
| "개발자" -> "DEVELOPER" | |
| "디자이너" -> "DESIGNER" | |
| "공무원" -> "CIVIL_SERVANT" | |
| "경찰" -> "POLICE" | |
| "소방관" -> "FIREFIGHTER" | |
| "군인" -> "MILITARY" | |
| "사업가" -> "ENTREPRENEUR" | |
| "경영인" -> "CEO" | |
| "금융인" -> "FINANCE" | |
| "연구원" -> "RESEARCHER" | |
| "예술가" -> "ARTIST" | |
| "운동선수" -> "ATHLETE" | |
| "프리랜서" -> "FREELANCER" | |
| "학생" -> "STUDENT" | |
| "무직" -> "UNEMPLOYED" | |
| "상관없음" -> "ANY" | |
| "기타" -> "OTHER" | |
| else -> null | |
| } |
| if (canSelectMatching) { | ||
| onConfirm(selectedMatching.matchingId) | ||
| } |
Copilot
AI
Jan 8, 2026
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.
The canSelectMatching check is redundant here. The button is already disabled when canSelectMatching is false (via the 'enabled' parameter on line 369), so this conditional check will never prevent the action from executing - disabled buttons don't trigger onClick. This extra check adds unnecessary complexity.
| if (canSelectMatching) { | |
| onConfirm(selectedMatching.matchingId) | |
| } | |
| onConfirm(selectedMatching.matchingId) |
| aiSummaryKo = null, | ||
| aiSummaryJa = null, | ||
| isAiSummaryLoading = false, | ||
| aiSummaryError = "AI 요약본을 불러오지 못했습니다." |
Copilot
AI
Jan 8, 2026
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.
Error message is hardcoded in Korean and won't be localized for Japanese users. The error message should use the AppLocalizer system like other UI text, especially since this is displayed in the WaitingContent which is visible to all users.
| aiSummaryKo = null, | ||
| aiSummaryJa = null, | ||
| isAiSummaryLoading = false, | ||
| aiSummaryError = "AI 요약본을 불러오지 못했습니다." |
Copilot
AI
Jan 8, 2026
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.
Error message is hardcoded in Korean and won't be localized for Japanese users. The error message should use the AppLocalizer system like other UI text, especially since this is displayed in the WaitingContent which is visible to all users.
| val memberId: Long, | ||
| @SerializedName(value = "name") | ||
| val name: String, | ||
| @SerializedName(value = "aiSummaryKo", alternate = ["ai_summary_ko", "aiSummary", "ai_summary"]) |
Copilot
AI
Jan 8, 2026
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.
The SerializedName annotation includes multiple alternate field names for aiSummaryKo (including "aiSummary" and "ai_summary"), which suggests uncertainty about the API contract. This can lead to maintenance issues if the API changes or if different endpoints return different field names. Consider clarifying the API specification to use a consistent field name.
| @SerializedName(value = "aiSummaryKo", alternate = ["ai_summary_ko", "aiSummary", "ai_summary"]) | |
| @SerializedName(value = "aiSummaryKo", alternate = ["ai_summary_ko"]) |
| text = "한국어", | ||
| type = CustomTextType.label, | ||
| color = CustomColor.gray400 | ||
| ) |
Copilot
AI
Jan 8, 2026
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.
The text "한국어" used as a label for the Korean language summary section lacks a localization entry. When the app is in Japanese mode, this label should display "韓国語" (Korean language), but will remain as "한국어" instead. Add a localization mapping for this context.
작업내용
추후 업데이트 필요