-
Notifications
You must be signed in to change notification settings - Fork 0
[Feature/#129] onboarding first UI refact #133
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
[Feature/#129] onboarding first UI refact #133
Conversation
Walkthroughํ์ด API๋ฅผ ํธ๋ฆฌ ๊ตฌ์กฐ๋ก ์ ํํ๊ณ ๋๋ฉ์ธ ๋ชจ๋ธ(Region, TownLite), ๋ฆฌํฌ์งํ ๋ฆฌ/์๊ฒฉ ๋ ์ด์ด์ UI/๋ทฐ๋ชจ๋ธ์ ์ข/์ฐ ํจ๋ ํํ๋ก ์ฌ๊ตฌ์ฑํ์ผ๋ฉฐ, ์จ๋ณด๋ฉยท๋งตยทOAuth ๊ด๋ จ UIยท๋ฌธ๊ตฌยท๋์์ธ ์์ฐ๊ณผ ๋์์ธ ์์คํ ์ปดํฌ์ ๋ธ ์ผ๋ถ๊ฐ ํจ๊ป ๋ณ๊ฒฝ๋์์ต๋๋ค. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor UI as FavoriteTownScreen
participant VM as FavoriteViewModel
participant Repo as FavoriteTownRepository
participant DS as FavoriteTownRemoteDataSource
participant Svc as FavoriteTownService
UI->>VM: LoadTownTree intent
VM->>Repo: getTownTree()
Repo->>DS: getTownTree()
DS->>Svc: GET /api/towns
Svc-->>DS: BaseResponse<TownTreeResponseDto>
DS-->>Repo: TownTreeResponseDto
Repo-->>VM: Result<Pair<List<Region>, Map<RegionId,List<TownLite>>>>
VM->>VM: update state (regions, townsByRegion, selectedRegionId)
VM-->>UI: emit CourseState
sequenceDiagram
autonumber
actor User
participant UI as FavoriteTownScreen
participant VM as FavoriteViewModel
participant Repo as FavoriteTownRepository
User->>UI: SelectRegion(regionId)
UI->>VM: Intent.SelectRegion(regionId)
VM->>VM: selectedRegionId = regionId
User->>UI: SelectTown(townId)
UI->>VM: Intent.SelectTown(townId)
VM->>VM: selectedTownId = townId
User->>UI: ConfirmSelection
UI->>VM: Intent.ConfirmSelection
VM->>Repo: patchUserFavoriteTown(selectedTownId, favoriteTownIdList)
Repo-->>VM: Result<Unit>
VM-->>UI: SideEffect(success/failure)
Estimated code review effort๐ฏ 4 (Complex) | โฑ๏ธ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touchesโ Failed checks (2 warnings)
โ Passed checks (3 passed)
โจ Finishing touches
๐งช Generate unit tests
Tip ๐ฎ Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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: 7
๐งน Nitpick comments (15)
domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepository.kt (2)
7-7: ๋ฐํ ํ์ ๊ฐ๋ ์ฑ ๊ฐ์ (์์ฑ์ ์๋ฏธ ๋๋ฌ๋๊ฒ Pair โ ์ ์ฉ ํ์ /alias ์ ์)
Result<Pair<List<Region>, Map<Long, List<TownLite>>>>๋ ์๋ฏธ ํ์ ์ด ์ด๋ ต๊ณ ,regions์townsByRegion์ ํค ์ ํฉ์ฑ(ํค=Region.id) ๋ถ๋ณ์กฐ๊ฑด์ด ๋ฌธ์ํ๋์ง ์์ต๋๋ค. ์ ์ฉ ํ์ ์ ์ฐ๊ฑฐ๋ ์ต์typealias๋ฅผ ๋์ ํ๋ ํธ์ด ๊ฐ๋ ์ฑ๊ณผ ์์ ์ฑ์ ์ ๋ฆฌํฉ๋๋ค.์๋์ฒ๋ผ ์๊ทธ๋์ฒ๋ง ๋ฐ๊พธ๊ณ , ํ์ผ ์๋จ(์ธํฐํ์ด์ค ์ธ๋ถ)์ alias๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
- suspend fun getTownTree(): Result<Pair<List<Region>, Map<Long, List<TownLite>>>> + suspend fun getTownTree(): Result<TownTree>์ถ๊ฐ(ํ์ผ ์๋จ):
typealias TownTree = Pair<List<Region>, Map<Long, List<TownLite>>>๋ํ KDoc์ โtownsByRegion์ ํค๋ regions[i].id์ ๋์ผโ ๊ฐ์ ๋ถ๋ณ์กฐ๊ฑด์ ๋ช ์ํด ์ฃผ์ธ์.
10-10: ํ๋ผ๋ฏธํฐ ๋ช ์ ํฉ์ฑ ์ ๋ฆฌ(favoriteTownList โ favoriteTownIdList)DTO ํ๋(
favoriteTownIdList)์ ์๋ฏธ(ID ๋ชฉ๋ก)๊ฐ ๋ง๋๋ก ์ธํฐํ์ด์ค ํ๋ผ๋ฏธํฐ๋ช ๋ ํต์ผํ๋ ๊ฒ ์ข์ต๋๋ค.- favoriteTownList: List<Long> + favoriteTownIdList: List<Long>feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/CourseState.kt (2)
6-11: ๊ตฌ ํ๋ก์ฐ ์์กด ํ๋ ์ ๋ฆฌ: townInfo ์ ๊ฑฐ ๊ฒํํธ๋ฆฌ ๊ธฐ๋ฐ์ผ๋ก ์ ํ๋๋ฉฐ
FavoriteTownInfoEntity๋ ๋ ์ด์ ํ์ ์์ด ๋ณด์ ๋๋ค. ํ์ ์ด feature ๋ชจ๋์์ ์ ๊ทผ ๊ฐ๋ฅํ์ง ์๋ค๋ฉด ์ปดํ์ผ ์ด์๋ ์ ๋ฐํฉ๋๋ค. ๋ฏธ์ฌ์ฉ์ด๋ฉด ์ ๊ฑฐํด ์ฃผ์ธ์.data class CourseState( val selectedTownId: Long? = null, val selectedRegionId: Long? = null, val regions: List<Region> = emptyList(), - val townsByRegion: Map<Long, List<TownLite>> = emptyMap(), - val townInfo: FavoriteTownInfoEntity? = null + val townsByRegion: Map<Long, List<TownLite>> = emptyMap() ) : UiState์ถ๊ฐ๋ก
townsByRegion์ ๋ ฌ ๋ณด์ฅ์ด ํ์ํ๋ฉดLinkedHashMap์ ์ง ๋๋ ์ ๋ ฌ ์ ๋ต์ ๋ช ์ํด ์ฃผ์ธ์.
8-9: ํค ํ์ ์ ๋๋ฉ์ธ์ผ๋ก ์น๊ฒฉ(Map<Long,โฆ> โ Map<Region,โฆ>) ๊ณ ๋ คํค๋ฅผ
Long์ผ๋ก ๋๋ฉด regions์ ๋งคํ ๋ถ์ผ์น๊ฐ ์๊ฒจ๋ ์ปดํ์ผ ๋จ๊ณ์์ ํ์ง ๋ถ๊ฐํฉ๋๋ค.Map<Region, List<TownLite>>๋ก ์ฌ๋ฆฌ๋ฉด ์ ํฉ์ฑ์ด ๊ฐํ๋ฉ๋๋ค. ๋น์ฉ ๋๋น ํจ๊ณผ๊ฐ ํฌ์ง ์๋ค๋ฉด ์ ์งํด๋ ๋ฉ๋๋ค.data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/dto/TownTreeResponseDto.kt (1)
13-17: ๋ ๋์ ๋น ๋ฆฌ์คํธ ๊ธฐ๋ณธ๊ฐ์ผ๋ก NPEยท๋ณด์ผ๋ฌํ๋ ์ดํธ ๊ฐ์
subTowns๋ฅผemptyList()๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋๋ฉด ํธ์ถ๋ถ์orEmpty()๊ฐ ๋ถํ์ํ๊ณ NPE ์ํ์ด ์ค์ด๋ญ๋๋ค.data class TownNodeDto( @SerialName("townId") val townId: Long, @SerialName("townName") val townName: String? = null, @SerialName("name") val name: String? = null, - @SerialName("subTowns") val subTowns: List<TownNodeDto>? = null + @SerialName("subTowns") val subTowns: List<TownNodeDto> = emptyList() )๋ฐฑ์๋๊ฐ ํ๋๋ฅผ ์์ ํ ์๋ตํ๋ ๊ฒฝ์ฐ์๋ ๋์ฝ๋ฉ ์ ์ ๋์ํฉ๋๋ค.
data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepositoryImpl.kt (1)
24-32: ํธ๋ฆฌ ๊น์ด๊ฐ 2๋จ๊ณ๋ฅผ ์ด๊ณผํ๋ ๊ฒฝ์ฐ ๋ฐ์ดํฐ ๋๋ฝ ๊ฐ๋ฅ์ฑํ์ฌ ๋งคํ์
subTowns1๋จ๊ณ๋ง ๊ณ ๋ คํฉ๋๋ค. ๋ฐฑ์๋๊ฐ ๋ ๊น์ ํธ๋ฆฌ๋ฅผ ์ค ์ ์๋์ง ํ์ธ ํ์ํฉ๋๋ค. ํ์ ์ ์ฌ๊ท/ํ๋ซํ ๋ก์ง์ผ๋ก ๋ณด๊ฐํ์ธ์.์ํ์๋ฉด ์ฌ๊ท ํ๋ซํ ์ ํธ(
List<TownNodeDto>.flattenTo(level: Int)) ์ด์ ์ ๊ณต ๊ฐ๋ฅํฉ๋๋ค.feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteViewModel.kt (3)
38-39: ์ด๊ธฐ ์ ํ ์ง์ญ ํค ์ ํจ์ฑ ๋ณด์ฅ ํ์
regions.firstOrNull()?.id๊ฐtownsByRegion์ ํค๋ก ์กด์ฌํ์ง ์์ ์ ์์ต๋๋ค(๋ฐ์ดํฐ ๋ถ์ผ์น/ํํฐ๋ง ๋ฑ). ์์ ๊ฐ๋๋ฅผ ๊ถ์ฅํฉ๋๋ค.- selectedRegionId = regions.firstOrNull()?.id, + selectedRegionId = regions.firstOrNull()?.id + ?.takeIf { townsByRegion.containsKey(it) } + ?: townsByRegion.keys.firstOrNull(),
44-44: ๋ก๊น : ๋ฆด๋ฆฌ์ค ๋น๋์์ ๋ถํ์ํ ๋ ธ์ถ/์ฑ๋ฅ ์ํฅ ์ต์ํ
Log.*๋์ ๋ํผ(Timber ๋ฑ) ์ฌ์ฉ๊ณผ ๋ก๊ทธ ๋ ๋ฒจ/๋น๋ํ์ ๊ฐ๋ ๊ถ์ฅ.Also applies to: 56-57
30-47: ์๋ฌ/๋ก๋ฉ UI ์ํ ๋ ธ์ถ์ด ์์ต๋๋ค๋คํธ์ํฌ ์คํจ ์ ์ฌ์ฉ์ ํผ๋๋ฐฑ๊ณผ ์ฌ์๋ UI ์ ์ด๋ฅผ ์ํด
CourseState์isLoading,error(๋๋errorMsg)๋ฅผ ์ถ๊ฐํ๊ณreduce๋ก ๊ด๋ฆฌํ์ธ์.Also applies to: 49-59
feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownScreen.kt (3)
113-118: ํจ์น ๊ฒฐ๊ณผ ํ์ธ ์์ด ์ฆ์ ๋ค๋น๊ฒ์ด์
ConfirmSelection๋ฐํ ์งํ ๋ฐ๋กnavigateToBack()์ ํธ์ถํฉ๋๋ค. ์คํจ ์ ์ฌ์ฉ์ ํผ๋ ๊ฐ๋ฅ. ์ฑ๊ณต ์์๋ง ๋ค๋น๊ฒ์ด์ ํ๋๋ก ์ฌ์ด๋์ดํํธ/์ํ๋ฅผ ํ์ฉํ์ธ์.์ฑ๊ณต/์คํจ ์ ํ ์คํธ/์ค๋ต๋ฐ ๋๋ ๋ผ์ฐํ ์ ์ํํ๋
CourseSideEffect์ค๊ณ๋ฅผ ๊ณ ๋ คํ์ธ์.
25-27: ์ ๊ทผ์ฑ(a11y): ํด๋ฆญ ๊ฐ๋ฅํ ์ปจํ ์ด๋์ ์ญํ ๋ถ์ฌ
Box,Row์ ํด๋ฆญ๋ง ์๊ณ ์๋ฏธ๋ก ์ ์ญํ ์ด ์์ต๋๋ค. ์คํฌ๋ฆฐ๋ฆฌ๋ ์ฌ์ฉ์ฑ์ ์ํด Role ์ง์ ์ด ํ์ํฉ๋๋ค.+import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.semantics @@ - Box( + Box( modifier = Modifier .fillMaxWidth() .height(56.dp) .background(bg) - .clickable { onSelect(region.id) } + .clickable { onSelect(region.id) } + .semantics { role = Role.Button } .padding(horizontal = 16.dp), @@ - Row( + Row( modifier = Modifier .fillMaxWidth() .height(46.dp) - .clickable { onSelect(town.id) } + .clickable { onSelect(town.id) } + .semantics { role = Role.Button } .padding(horizontal = 16.dp),Also applies to: 151-153, 191-193
140-141:LazyColumn.items์contentTypeํํธ ์ถ๊ฐ๋ก ์ฌ๊ตฌ์ฑ ๋น์ฉ ์ํ๋์ง ์์ดํ ์ด๋ฏ๋ก
contentType์ ๊ณ ์ ๊ฐ์ผ๋ก ์ ๊ณตํ๋ฉด ๋ฆฌ์ฌ์ดํด/์ธก์ ์ต์ ํ์ ๋์๋ฉ๋๋ค.- items(regions, key = { it.id }) { region -> + items(regions, key = { it.id }, contentType = { "region" }) { region -> @@ - items(towns, key = { it.id }) { town -> + items(towns, key = { it.id }, contentType = { "town" }) { town ->Also applies to: 182-182
feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt (3)
166-171: ์์ด์ฝ ๋์ฒด ํ ์คํธ ๋ถ์ ํ ๋ฐ ์ค๋ณต ๋ญ๋ ๊ฐ๋ฅ์ฑApple ์์ด์ฝ์
contentDescription์ด "kakao_logo"๋ก ๋์ด ์๊ณ , ๋ฒํผ ๋ด๋ถ์ ํ ์คํธ๊ฐ ์์ผ๋ฏ๋ก ์์ด์ฝ์ ์ฅ์์ฉ์ผ๋กnull์ด ์ ์ ํฉ๋๋ค.- Icon( - painter = painterResource(R.drawable.ic_apple_logo), - contentDescription = "kakao_logo", + Icon( + painter = painterResource(R.drawable.ic_apple_logo), + contentDescription = null, tint = Color.Unspecified,
173-173: ๋ฌธ์์ด ๋ฆฌ์์ค ์ฐธ์กฐ ๋ฐฉ์ ์ผ๊ดํ์์์๋
kakao_login์ ์ ์ ์ํฌํธ๋ก ์ฐ๊ณ , ์ฌ๊ธฐ์๋ FQCN์ ์ฌ์ฉํฉ๋๋ค. ๋์ผ ํจํด์ผ๋ก ์ ๋ฆฌํด ๊ฐ๋ ์ฑ์ ๋์ด์ธ์.- text = stringResource(com.teamsolply.solply.oauth.R.string.apple_login), + text = stringResource(apple_login),์ถ๊ฐ ์ํฌํธ:
+import com.teamsolply.solply.oauth.R.string.apple_login
148-177: ์ค๋ณต UI ์ถ์ถ ์ ์: SocialLoginButton ์ปดํฌ์ ๋ธ์นด์นด์ค/์ ํ ๋ฒํผ์ด ๋ฐฐ๊ฒฝ์ยท์์ด์ฝยทํ ์คํธ๋ง ๋ค๋ฅธ ๋์ผ ๋ ์ด์์์ ๋๋ค. ์ฌ์ฌ์ฉ ์ปดํฌ์ ๋ธ๋ก ์ถ์ถํ๋ฉด ์ ์ง๋ณด์์ฑ๊ณผ ์ผ๊ด์ฑ์ด ํฅ์๋ฉ๋๋ค.
์์:
@Composable private fun SocialLoginButton( bg: Color, iconRes: Int, text: String, textColor: Color, onClick: () -> Unit, modifier: Modifier = Modifier ) { /* ๋์ผ ๋ ์ด์์ ๊ตฌํ */ }
๐ Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
๐ Files selected for processing (19)
core/designsystem/src/main/res/drawable/ic_apple_logo.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_logo_full_vector.xml(1 hunks)data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/dto/TownTreeResponseDto.kt(1 hunks)data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepositoryImpl.kt(1 hunks)data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/source/FavoriteTownRemoteDataSource.kt(1 hunks)domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/Region.kt(1 hunks)domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/TownLite.kt(1 hunks)domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepository.kt(1 hunks)feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownContract.kt(1 hunks)feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownScreen.kt(3 hunks)feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteViewModel.kt(2 hunks)feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/CourseState.kt(1 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt(0 hunks)feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt(2 hunks)feature/oauth/src/main/res/values/strings.xml(1 hunks)feature/place/src/main/java/com/teamsolply/solply/place/PlaceScreen.kt(0 hunks)remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/datasource/FavoriteTownRemoteDataSourceImpl.kt(2 hunks)remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/di/FavoriteTownRemoteDataModule.kt(1 hunks)remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/service/FavoriteTownService.kt(1 hunks)
๐ค Files with no reviewable changes (2)
- feature/place/src/main/java/com/teamsolply/solply/place/PlaceScreen.kt
- feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt
๐งฐ Additional context used
๐งฌ Code graph analysis (2)
feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteViewModel.kt (6)
core/ui/src/main/java/com/teamsolply/solply/ui/base/BaseViewModel.kt (1)
reduce(44-46)data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepositoryImpl.kt (1)
patchUserFavoriteTown(37-46)remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/service/FavoriteTownService.kt (1)
patchUserFavoriteTown(14-17)data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/source/FavoriteTownRemoteDataSource.kt (1)
patchUserFavoriteTown(8-10)domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepository.kt (1)
patchUserFavoriteTown(8-11)remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/datasource/FavoriteTownRemoteDataSourceImpl.kt (1)
patchUserFavoriteTown(17-21)
feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownScreen.kt (1)
feature/course/src/main/java/com/teamsolply/solply/course/component/FavoriteTownTopBar.kt (1)
FavoriteTownTopBar(21-58)
๐ Additional comments (12)
domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/Region.kt (1)
3-6: ๋๋ฉ์ธ ๋ชจ๋ธ Region ๋์ โ LGTMํ๋ ์ต์ํยท๋ถ๋ณ์ฑ ๋ณด์ฅ๋๋
data class๋ก ์ ์ ํฉ๋๋ค.domain/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/TownLite.kt (1)
1-6: ๊ฒฝ๋ ํ์ด ๋ชจ๋ธ(TownLite) ์ถ๊ฐ โ LGTM๋ชฉ๋ก/๋งตํ์ ์ ํฉํ ๊ตฌ์กฐ๋ก ๋ฌด๋ํฉ๋๋ค.
remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/datasource/FavoriteTownRemoteDataSourceImpl.kt (1)
4-4: DTO import ๋ณ๊ฒฝ โ LGTM์๋น์ค/์ธํฐํ์ด์ค ๋ณ๊ฒฝ๊ณผ ์ผ๊ด๋ฉ๋๋ค.
remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/di/FavoriteTownRemoteDataModule.kt (1)
16-16: DI ๋ฐ์ธ๋ฉ ์๊ทธ๋์ฒ ์ ๋ฆฌ โ LGTM
@Binds @Singleton์ ์ง๋๊ณ , ๊ตฌํ/์ธํฐํ์ด์ค ๋งคํ์ด ์ฌ๋ฐ๋ฆ ๋๋ค.data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/source/FavoriteTownRemoteDataSource.kt (1)
4-8: ํ์ธ ์๋ฃ โ getTownList ํธ์ถ ์์
์ ์ฅ์ ์ ์ญ ๊ฒ์ ๊ฒฐ๊ณผ getTownList ํธ์ถ์ด ๋ฐ๊ฒฌ๋์ง ์์๊ณ ํธ์ถ๋ถ๋ getTownTree๋ก ์ผ๊ด๋๊ฒ ์ ํ๋์ด ์์ต๋๋ค.feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownContract.kt (1)
23-23: ์น์ธ โ SelectRegion ์ฒ๋ฆฌ ๋ฐ UI ๋ฐ์ธ๋ฉ ํ์ธ๋จ
ViewModel(handleIntent)์ SelectRegion ๋ถ๊ธฐ(reduce { copy(selectedRegionId = intent.regionId) })๊ฐ ์๊ณ , FavoriteTownScreen์ LeftRegionPane onSelect์์ FavoriteTownIntent.SelectRegion(id)๋ฅผ ํธ์ถํฉ๋๋ค. feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteViewModel.kt, feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownScreen.ktremote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/service/FavoriteTownService.kt (1)
11-12: ์๋ํฌ์ธํธ ๋ณ๊ฒฝ(/api/users/towns โ /api/towns) โ ๋ฐฑ์๋ ๊ณ์ฝ(ํธ๋ฆฌ ์๋ต) ํ์ธ ํ์GET /api/towns์ด ํธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ฐํํ๋์ง(๊ถํยท์บ์ยท๋ก์ผ์ผ ํฌํจ) ๋ฐฑ์๋ ์คํ์ผ๋ก ํ์ธ.
๋ ํฌ ๊ฒ์ ๊ฒฐ๊ณผ ํ๋์ฝ๋ฉ ํธ์ถ์ ์๋ ํ์ผ์์๋ง ๋ฐ๊ฒฌ๋จ:
remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/service/FavoriteTownService.kt๋ค๋ฅธ ํด๋ผ์ด์ธํธ(ํ๋ก ํธ/์ฑ/์๋ฒ)์์ ๊ธฐ์กด ๊ฒฝ๋ก๋ฅผ ํ๋์ฝ๋ฉํ ๊ณณ์ด ์๋์ง๋ ์ถ๊ฐ ์ ๊ฒ.
feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownScreen.kt (1)
45-50: ๋ฆฌ๋ทฐ ์ฝ๋ฉํธ๊ฐ ๋ถ์ ํํฉ๋๋ค โ uiState๋ CourseState๊ฐ ์๋๋๋คviewModel.uiState๋ FavoriteTownState์ด๋ฉฐ FavoriteTownScreen์ com.teamsolply.solply.course.favoriteTown.model.CourseState๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ์ต๋๋ค. s๋ฅผ ๊ทธ๋๋ก ์ ๋ฌํ๋ฉด ํ์ ๋ถ์ผ์น๋ก ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. ํ์ฌ CourseState๋ก ๋งคํํ๋ ์ฝ๋๋ ์๋์ ๋ณํ์ด๋ฉฐ, townInfo ํ๋๋ feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/model/CourseState.kt ๋ฐ feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteTownContract.kt์ ์ ์๋์ด ์์ด โ๋ ๊ฑฐ์ ์ฐธ์กฐโ ์ง์ ์ ๋ง์ง ์์ต๋๋ค.
Likely an incorrect or invalid review comment.
feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt (1)
24-24: tint ์ ์ด๋ฅผ ์ํ Color ์ํฌํธ ์ถ๊ฐ๋ ์ ์ ํฉ๋๋ค
Color.Unspecified๋ก ๋ฒกํฐ ๋ณธ์(ํ์ดํธ)์ ์ ์งํ๋ ๋ชฉ์ ์ ๋ถํฉํฉ๋๋ค.core/designsystem/src/main/res/drawable/ic_logo_full_vector.xml (2)
7-11: ๊ฒฝ๋ก ์ ๋ฐ์ดํธ ์์ฒด๋ ๋ฌธ์ ์์ด ๋ณด์ ๋๋ค๋ทฐํฌํธ์ dp๊ฐ 1:1๋ก ๋ง์ถฐ์ ธ ์์ด ์ค์ผ์ผ ์๊ณก ์ฐ๋ ค๊ฐ ๋ฎ์ต๋๋ค.
3-5: ๋ก๊ณ ์ธ๋ก ์ฌ์ด์ฆ 61dp โ 58dp ๋ณ๊ฒฝ ์ํฅ ํ์ธfeature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt (๋ผ์ธ 87)์์ ic_logo_full_vector๋ฅผ ์ฌ์ฉํฉ๋๋ค โ Image์ Modifier์ ๋ช ์์ height/width/size๊ฐ ์๋์ง, ๋๋ ๋ถ๋ชจ ๋ ์ด์์์ด ๊ณ ์ ๋์ดยท๋น์จ์ ๊ฐ์ ํด ํจ๋ฉยท์ ๋ ฌ์ด ์ด๊ธ๋๋์ง ํ์ธํ์ธ์. ๋ฌธ์ ๋ฐ์ ์ ์คํฌ๋ฆฐ์ท ๊ฐฑ์ ๋๋ ์๋น์ฒ๋ฅผ wrap/content ๊ธฐ์ค์ผ๋ก ์กฐ์ ํ์ธ์.
core/designsystem/src/main/res/drawable/ic_apple_logo.xml (1)
6-8: Apple ๋ก๊ณ ยทSign in with Apple ๋ฒํผ ๊ฐ์ด๋ ์ค์ ํ์ธ ํ์๊ฒ์ฆ ๊ฒฐ๊ณผ: ์์คํ ์ ๊ณต ๋ฒํผ์ ์ํฉ์ ๋ฐ๋ผ "Sign in with Apple" ๋๋ "Continue with Apple" ๋ ์ด๋ธ์ ํ์ฉํ๋, ์ค์๋ฅผ ์ํด์ ๊ณต์ ๋ฒํผ(ASAuthorization/JS) ์ฌ์ฉ ๋๋ Apple HIG์ ๋ช ์๋ ์ ํํ ๋ฌธ๊ตฌยท์์(ํ/๋ฐฑ)ยท๋ชจ์๋ฆฌยท์ต์ ํฌ๊ธฐยท์ฌ๋ฐฑยท๊ฐ๊ฒฉ ๊ท์น์ ๋ฐ๋ผ์ผ ํฉ๋๋ค.
- ์์น: core/designsystem/src/main/res/drawable/ic_apple_logo.xml โ ํ์ฌ ์ปค์คํ ๋ฒกํฐ ์์ด์ฝ ์ฌ์ฉ ํ์ธ.
- ์กฐ์น(ํ1):
- ์์คํ ์ ๊ณต Sign in with Apple ๋ฒํผ์ผ๋ก ๊ต์ฒด, ๋๋
- HIG ๊ท๊ฒฉ(์ ํํ ๋ฌธ๊ตฌยท์์ยท์ต์ ํฌ๊ธฐยท๋ชจ์๋ฆฌ ๋ฐ๊ฒฝยท๋ด/์ธ๋ถ ์ฌ๋ฐฑยท๊ฐ๊ฒฉ ๋ฑ)์ ์ ํํ ์ ์ฉํ๊ณ ๋ณ๊ฒฝ์ฌํญ(๋์์ธ ๊ฐ์ด๋/์น์/์คํฌ๋ฆฐ์ท)์ PR์ ์ฒจ๋ถํด ์ค์ ์ฌ๋ถ๋ฅผ ์ฆ๋น.
| override suspend fun getTownTree(): | ||
| Result<Pair<List<Region>, Map<Long, List<TownLite>>>> = runCatching { | ||
| val tree = remoteDataSource.getTownTree() | ||
|
|
||
| val selectedTown = Town( | ||
| townId = dto.selectedTown.townId, | ||
| townName = dto.selectedTown.townName | ||
| ) | ||
|
|
||
| val favoriteTownList = dto.favoriteTownList.map { | ||
| Town( | ||
| townId = it.townId, | ||
| townName = it.townName | ||
| val regions = tree.towns.map { region -> | ||
| Region( | ||
| id = region.townId, | ||
| name = region.townName ?: region.name.orEmpty() | ||
| ) | ||
| } | ||
|
|
||
| FavoriteTownInfoEntity( | ||
| selectedTown = selectedTown, | ||
| favoriteTownList = favoriteTownList | ||
| ) | ||
| val townsByRegion: Map<Long, List<TownLite>> = tree.towns.associate { region -> | ||
| val towns = region.subTowns.orEmpty().map { sub -> | ||
| TownLite( | ||
| id = sub.townId, | ||
| name = sub.townName ?: sub.name.orEmpty() | ||
| ) | ||
| } | ||
| region.townId to towns | ||
| } | ||
|
|
||
| regions to townsByRegion | ||
| } |
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.
Coroutine ์ทจ์๊ฐ runCatching์ ์ํด ์ผ์ผ์ง ์ ์์
runCatching์ CancellationException๊น์ง ํฌ์ฐฉํ์ฌ ์ทจ์ ์ ํ๊ฐ ๋งํ ์ ์์ต๋๋ค. ์ ์ฅ์ ๊ณ์ธต์์๋ ์ทจ์๋ฅผ ๋ฐ๋์ ์ฌ์ ํํด์ผ ํฉ๋๋ค.
์๋์ฒ๋ผ try/catch๋ก ์ทจ์๋ง ์ฌ๋์ง๋๋ก ์์ ํด ์ฃผ์ธ์.
- override suspend fun getTownTree():
- Result<Pair<List<Region>, Map<Long, List<TownLite>>>> = runCatching {
- val tree = remoteDataSource.getTownTree()
-
- val regions = tree.towns.map { region ->
- Region(
- id = region.townId,
- name = region.townName ?: region.name.orEmpty()
- )
- }
-
- val townsByRegion: Map<Long, List<TownLite>> = tree.towns.associate { region ->
- val towns = region.subTowns.orEmpty().map { sub ->
- TownLite(
- id = sub.townId,
- name = sub.townName ?: sub.name.orEmpty()
- )
- }
- region.townId to towns
- }
-
- regions to townsByRegion
- }
+ override suspend fun getTownTree():
+ Result<Pair<List<Region>, Map<Long, List<TownLite>>>> {
+ return try {
+ val tree = remoteDataSource.getTownTree()
+
+ val regions = tree.towns.map { region ->
+ Region(
+ id = region.townId,
+ name = region.townName ?: region.name.orEmpty()
+ )
+ }
+
+ val townsByRegion: Map<Long, List<TownLite>> = tree.towns.associate { region ->
+ val towns = region.subTowns.orEmpty().map { sub ->
+ TownLite(
+ id = sub.townId,
+ name = sub.townName ?: sub.name.orEmpty()
+ )
+ }
+ region.townId to towns
+ }
+
+ Result.success(regions to townsByRegion)
+ } catch (t: kotlinx.coroutines.CancellationException) {
+ throw t
+ } catch (t: Throwable) {
+ Result.failure(t)
+ }
+ }๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override suspend fun getTownTree(): | |
| Result<Pair<List<Region>, Map<Long, List<TownLite>>>> = runCatching { | |
| val tree = remoteDataSource.getTownTree() | |
| val selectedTown = Town( | |
| townId = dto.selectedTown.townId, | |
| townName = dto.selectedTown.townName | |
| ) | |
| val favoriteTownList = dto.favoriteTownList.map { | |
| Town( | |
| townId = it.townId, | |
| townName = it.townName | |
| val regions = tree.towns.map { region -> | |
| Region( | |
| id = region.townId, | |
| name = region.townName ?: region.name.orEmpty() | |
| ) | |
| } | |
| FavoriteTownInfoEntity( | |
| selectedTown = selectedTown, | |
| favoriteTownList = favoriteTownList | |
| ) | |
| val townsByRegion: Map<Long, List<TownLite>> = tree.towns.associate { region -> | |
| val towns = region.subTowns.orEmpty().map { sub -> | |
| TownLite( | |
| id = sub.townId, | |
| name = sub.townName ?: sub.name.orEmpty() | |
| ) | |
| } | |
| region.townId to towns | |
| } | |
| regions to townsByRegion | |
| } | |
| override suspend fun getTownTree(): | |
| Result<Pair<List<Region>, Map<Long, List<TownLite>>>> { | |
| return try { | |
| val tree = remoteDataSource.getTownTree() | |
| val regions = tree.towns.map { region -> | |
| Region( | |
| id = region.townId, | |
| name = region.townName ?: region.name.orEmpty() | |
| ) | |
| } | |
| val townsByRegion: Map<Long, List<TownLite>> = tree.towns.associate { region -> | |
| val towns = region.subTowns.orEmpty().map { sub -> | |
| TownLite( | |
| id = sub.townId, | |
| name = sub.townName ?: sub.name.orEmpty() | |
| ) | |
| } | |
| region.townId to towns | |
| } | |
| Result.success(regions to townsByRegion) | |
| } catch (t: kotlinx.coroutines.CancellationException) { | |
| throw t | |
| } catch (t: Throwable) { | |
| Result.failure(t) | |
| } | |
| } |
๐ค Prompt for AI Agents
In
data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepositoryImpl.kt
around lines 13 to 35, runCatching wraps the whole coroutine block and will
catch CancellationException, preventing coroutine cancellation from propagating;
change to an explicit try/catch: perform the remoteDataSource call and mapping
inside a try, catch CancellationException and rethrow it immediately, and only
catch/handle other exceptions (wrapping them in a Result.failure or return
value) so cancellation is not swallowed.
| ): Result<Unit> = runCatching { | ||
| val req = PatchUserFavTownRequestDto( | ||
| selectedTownId = selectedTownId, | ||
| favoriteTownIdList = favoriteTownList | ||
| ) | ||
| remoteDataSource.patchUserFavoriteTown(req) | ||
| } |
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.
์ฌ๊ธฐ๋ ์ทจ์ ์ฌ์ ํ ํ์(runCatching ๋์ผ ์ด์)
patchUserFavoriteTown๋ ์ทจ์ ์ ํ๋ฅผ ๋ณด์ฅํ๋๋ก ๋ณ๊ฒฝํด ์ฃผ์ธ์.
- ): Result<Unit> = runCatching {
- val req = PatchUserFavTownRequestDto(
- selectedTownId = selectedTownId,
- favoriteTownIdList = favoriteTownList
- )
- remoteDataSource.patchUserFavoriteTown(req)
- }
+ ): Result<Unit> {
+ return try {
+ val req = PatchUserFavTownRequestDto(
+ selectedTownId = selectedTownId,
+ favoriteTownIdList = favoriteTownList
+ )
+ Result.success(remoteDataSource.patchUserFavoriteTown(req))
+ } catch (t: kotlinx.coroutines.CancellationException) {
+ throw t
+ } catch (t: Throwable) {
+ Result.failure(t)
+ }
+ }๋ํ ์ธํฐํ์ด์ค ํ๋ผ๋ฏธํฐ๋ช
์ favoriteTownIdList๋ก ํต์ผํ๋ฉด ๊ฐ๋
์ฑ์ด ํฅ์๋ฉ๋๋ค.
๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ): Result<Unit> = runCatching { | |
| val req = PatchUserFavTownRequestDto( | |
| selectedTownId = selectedTownId, | |
| favoriteTownIdList = favoriteTownList | |
| ) | |
| remoteDataSource.patchUserFavoriteTown(req) | |
| } | |
| ): Result<Unit> { | |
| return try { | |
| val req = PatchUserFavTownRequestDto( | |
| selectedTownId = selectedTownId, | |
| favoriteTownIdList = favoriteTownList | |
| ) | |
| Result.success(remoteDataSource.patchUserFavoriteTown(req)) | |
| } catch (t: kotlinx.coroutines.CancellationException) { | |
| throw t | |
| } catch (t: Throwable) { | |
| Result.failure(t) | |
| } | |
| } |
๐ค Prompt for AI Agents
In
data/course/src/main/java/com/teamsolply/solply/course/favoriteTown/repository/FavoriteTownRepositoryImpl.kt
around lines 40-46, replace the runCatching block so CancellationException is
not swallowed: call remoteDataSource.patchUserFavoriteTown inside a try { ... }
that returns Result.success(Unit) on success, catch CancellationException and
rethrow it, and catch other exceptions to return Result.failure(exception). Also
change the parameter name passed to PatchUserFavTownRequestDto from
favoriteTownList to favoriteTownIdList to match the interface and improve
readability.
|
|
||
| is FavoriteTownIntent.LoadFavoriteTownList -> loadTownTree() | ||
| is FavoriteTownIntent.SelectTown -> reduce { copy(selectedTownId = intent.townId) } | ||
| is FavoriteTownIntent.SelectRegion -> reduce { copy(selectedRegionId = intent.regionId) } |
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.
์ง์ญ ๋ณ๊ฒฝ ์ ์ ํ๋ ๋๋ค๋ฅผ ์ด๊ธฐํํ์ง ์์ ์๋ชป๋ ์ํ๊ฐ ์ ์ง๋ฉ๋๋ค.
์ง์ญ์ ๋ฐ๊พธ๋ฉด ์ด์ ์ง์ญ์์ ์ ํํ๋ selectedTownId๋ฅผ ์ด๊ธฐํํด์ผ ํฉ๋๋ค. ๋ฒํผ ํ์ฑ ์ํ๊ฐ ๊ฑฐ์ง ์์ฑ์ผ๋ก ๋จ์ ์ ์์ต๋๋ค.
- is FavoriteTownIntent.SelectRegion -> reduce { copy(selectedRegionId = intent.regionId) }
+ is FavoriteTownIntent.SelectRegion -> reduce {
+ copy(selectedRegionId = intent.regionId, selectedTownId = null)
+ }๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| is FavoriteTownIntent.SelectRegion -> reduce { copy(selectedRegionId = intent.regionId) } | |
| is FavoriteTownIntent.SelectRegion -> reduce { | |
| copy(selectedRegionId = intent.regionId, selectedTownId = null) | |
| } |
๐ค Prompt for AI Agents
In
feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteViewModel.kt
around line 24, the reducer for FavoriteTownIntent.SelectRegion only updates
selectedRegionId and fails to clear selectedTownId, leaving a stale town
selection; update the reduce call to also reset selectedTownId (e.g., set it to
null or default unselected value) when changing region so the UI button state
and selection reflect the new region.
| val favoriteTownIdList = currentState.townInfo.favoriteTownList.map { it.townId } | ||
| val s = uiState.value | ||
| val selectedTownId = s.selectedTownId ?: return | ||
| val favoriteTownIdList = s.townsByRegion.values.flatten().map { it.id } |
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.
์ ํธ ๋๋ค ๋ชฉ๋ก ๊ตฌ์ฑ ์ค๋ฅ โ ์ ์ฒด ๋๋ค ID๋ฅผ ์๋ฒ์ ์ ์กํ๊ณ ์์ต๋๋ค.
ํ์ฌ favoriteTownIdList๊ฐ ๋ชจ๋ ์ง์ญ์ ๋ชจ๋ ๋๋ค ID๋ฅผ ํํํํด ์ ์กํฉ๋๋ค. ๋จ์ผ ์ ํ UX๋ผ๋ฉด ์ ํํ ๋๋ค๋ง ์ ๋ฌํด์ผ ํฉ๋๋ค.
์๋์ฒ๋ผ ์์ ํ์ธ์:
- val favoriteTownIdList = s.townsByRegion.values.flatten().map { it.id }
+ // ๋จ์ผ ์ ํ UX ๊ธฐ์ค: ์ ํํ ๋๋ค๋ง ์ ํธ ๋ชฉ๋ก์ผ๋ก ์ ์ก
+ val favoriteTownIdList = listOf(selectedTownId)๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| val favoriteTownIdList = s.townsByRegion.values.flatten().map { it.id } | |
| // ๋จ์ผ ์ ํ UX ๊ธฐ์ค: ์ ํํ ๋๋ค๋ง ์ ํธ ๋ชฉ๋ก์ผ๋ก ์ ์ก | |
| val favoriteTownIdList = listOf(selectedTownId) |
๐ค Prompt for AI Agents
In
feature/course/src/main/java/com/teamsolply/solply/course/favoriteTown/FavoriteViewModel.kt
around line 52, the code currently flattens all regions and sends every town id
to the server; instead, use the view stateโs selected town only. Replace the
flatten/map with logic that picks the single selected town id (e.g., use
state.selectedTown?.id or derive the selected id from state), guard for
null/absence and return or handle the error case, and send that single id to the
server rather than the whole list.
| rippleEnabled = false | ||
| ) { | ||
| kakaoLoginClick() | ||
| }, |
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.
Apple ๋ฒํผ ํด๋ฆญ ์ ์นด์นด์ค ๋ก๊ทธ์ธ ํธ๋ฆฌ๊ฑฐ๋จ
Apple ๋ก๊ทธ์ธ ๋ฒํผ onClick์ด kakaoLoginClick()์ ํธ์ถํฉ๋๋ค. ๊ธฐ๋ฅ ์คํด๋ฅผ ์ ๋ฐํ๋ ๋ช
๋ฐฑํ ๋ฒ๊ทธ์
๋๋ค. ๋ณ๋์ appleLoginClick ์ฝ๋ฐฑ์ผ๋ก ๋ถ๋ฆฌํ์ธ์.
์ ์ฉ diff(ํด๋น ๋ธ๋ก ๋ด ์์ ):
- .customClickable(
- rippleEnabled = false
- ) {
- kakaoLoginClick()
- },
+ .customClickable(
+ rippleEnabled = false
+ ) {
+ appleLoginClick()
+ },์ปดํฌ์ ๋ธ ์๊ทธ๋์ฒ์ ํธ์ถ๋ถ ๋ณด์(๋ธ๋ก ์ธ ์ถ๊ฐ ๋ณ๊ฒฝ):
@@
-fun OauthScreen(
- kakaoLoginClick: () -> Unit,
- modifier: Modifier = Modifier
-) {
+fun OauthScreen(
+ kakaoLoginClick: () -> Unit,
+ appleLoginClick: () -> Unit,
+ modifier: Modifier = Modifier
+) {
@@
- OauthScreen(
- kakaoLoginClick = { viewModel.sendIntent(OauthIntent.KakaoLoginClick) }
- )
+ OauthScreen(
+ kakaoLoginClick = { viewModel.sendIntent(OauthIntent.KakaoLoginClick) },
+ appleLoginClick = { /* TODO: Apple ๋ก๊ทธ์ธ ์ฐ๋ */ }
+ )๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| rippleEnabled = false | |
| ) { | |
| kakaoLoginClick() | |
| }, | |
| fun OauthScreen( | |
| kakaoLoginClick: () -> Unit, | |
| appleLoginClick: () -> Unit, | |
| modifier: Modifier = Modifier | |
| ) { | |
| // ... (other code) | |
| rippleEnabled = false | |
| ) { | |
| appleLoginClick() | |
| }, | |
| // ... (other code) | |
| } | |
| OauthScreen( | |
| kakaoLoginClick = { viewModel.sendIntent(OauthIntent.KakaoLoginClick) }, | |
| appleLoginClick = { /* TODO: Apple ๋ก๊ทธ์ธ ์ฐ๋ */ } | |
| ) |
๐ค Prompt for AI Agents
In feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt around
lines 158-161 the Apple login button onClick incorrectly calls
kakaoLoginClick(), causing the Apple button to trigger Kakao login; change the
button to call a new appleLoginClick() callback instead, add appleLoginClick as
a parameter to the composable signature (with a sensible default or required
lambda), update all call sites to pass the appropriate Apple login handler, and
ensure rippleEnabled and other button props remain unchanged.
| <string name="kakao_login">์นด์นด์ค๋ก ๊ณ์ํ๊ธฐ</string> | ||
| <string name="apple_login">Apple๋ก ๊ณ์ํ๊ธฐ</string> |
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.
๐งฉ Analysis chain
๋ผ๋ฒจ ๋ฌธ๊ตฌ ๊ฐ์ด๋ ์ ํฉ์ฑ ํ์ธ
- โApple๋ก ๊ณ์ํ๊ธฐโ ๋ผ๋ฒจ์ด ๊ณต์ ๊ฐ์ด๋์์ ํ์ฉ๋๋ ํํ์ธ์ง ํ์ธ ํ์. ์ผ๋ฐ์ ์ผ๋ก โSign in with Appleโ ๊ณ ์ ๋ฌธ๊ตฌ๋ฅผ ์๊ตฌํฉ๋๋ค. ํ์ ์ ๋ผ๋ฒจ/์์ฐ์ ๊ฐ์ด๋์ ๋ง๊ฒ ๊ต์ฒดํด ์ฃผ์ธ์.
๐ Web query:
Sign in with Apple branding guidelines label localization Korean
๐ก Result:
Use the system-provided Sign in with Apple button (ASAuthorizationAppleIDButton / web UI) โ it auto-localizes the title and accessibility label into Korean. If you must create a custom button, follow Appleโs HIG exactly (only use the allowed title variants) and localize them into Korean as the system does (common/localized forms):
- Sign in with Apple โ Apple๋ก ๋ก๊ทธ์ธ. [1][2]
- Sign up with Apple โ Apple๋ก ๊ฐ์ . [1][2]
- Continue with Apple โ Apple๋ก ๊ณ์. [1][2]
Primary sources and guidance:
- Apple Human Interface Guidelines โ Sign in with Apple (use system button, allowed titles, auto-translation). [1]
- Korean translation of Apple HIG (shows the localized phrasing). [2]
If you want, I can fetch screenshots or the exact Korean accessibility labels used by Appleโs system button.
Apple HIG ์ค์ โ Apple ๋ฒํผ ๋ผ๋ฒจ ์์ ํ์
strings.xml์ "Apple๋ก ๊ณ์ํ๊ธฐ"๋ Apple HIG ๊ถ์ฅ ํ๊ตญ์ด ํ์ค("Apple๋ก ๊ณ์")๊ณผ ๋ถ์ผ์นํฉ๋๋ค. ๊ฐ๋ฅํ ๊ฒฝ์ฐ ์์คํ ๋ฒํผ(ASAuthorizationAppleIDButton)์ ์ฌ์ฉํด ์๋ ํ์งํํ์ธ์. ์ง์ ์ปค์คํ ๋ฒํผ์ ์ฌ์ฉํด์ผ ํ๋ฉด ๋ผ๋ฒจ์ ๊ถ์ฅ ํ๊ธฐ์ผ๋ก ๊ต์ฒดํ์ธ์.
์์น: feature/oauth/src/main/res/values/strings.xml (lines 3-4)
<string name="kakao_login">์นด์นด์ค๋ก ๊ณ์ํ๊ธฐ</string>
<string name="apple_login">Apple๋ก ๊ณ์ํ๊ธฐ</string>
์กฐ์น: 'Apple๋ก ๊ณ์ํ๊ธฐ' โ 'Apple๋ก ๊ณ์'์ผ๋ก ๊ต์ฒดํ๊ฑฐ๋ ์์คํ ๋ฒํผ์ผ๋ก ๋์ฒด.
๐ค Prompt for AI Agents
feature/oauth/src/main/res/values/strings.xml around lines 3-4: the Apple button
label "Apple๋ก ๊ณ์ํ๊ธฐ" does not follow Apple HIG Korean recommendation; replace the
string value with "Apple๋ก ๊ณ์" or, preferably, use the system
ASAuthorizationAppleIDButton so the label is automatically localized; update the
<string name="apple_login"> entry to "Apple๋ก ๊ณ์" (or switch to the system button
implementation) and ensure any usages reference the updated resource.
| override suspend fun getTownTree(): TownTreeResponseDto { | ||
| return service.getTownTree().data | ||
| } |
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.
.data null ๊ฐ๋ฅ์ฑ ๋ฐฉ์น โ NPE/๋ถ์์ ์ํ ์ ํ ์ํ
service.getTownTree().data๊ฐ null์ผ ์ ์์ผ๋ฉด NPE ๋๋ ์์ ๊ณ์ธต ๋ถ์์ ์ด ๋ฐ์ํฉ๋๋ค. ๋ช
์์ ์ฒ๋ฆฌ๋ก ๋ฐฉ์ดํด ์ฃผ์ธ์.
์ ์ฉ ์ ์:
- override suspend fun getTownTree(): TownTreeResponseDto {
- return service.getTownTree().data
- }
+ override suspend fun getTownTree(): TownTreeResponseDto {
+ val resp = service.getTownTree()
+ // ์ ํ 1) ์์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋์ฒด
+ return resp.data ?: TownTreeResponseDto()
+ // ์ ํ 2) ๊ณ์ฝ ์๋ฐ์ ์ฆ์ ์คํจ๋ก ์ฒ๋ฆฌ
+ // return requireNotNull(resp.data) { "getTownTree(): response.data is null" }
+ }์ ํ 1์ UI์ ๋น ์ํ๋ฅผ ์ ๋ฌํ๊ณ , ์ ํ 2๋ ์ฅ์ ๋ฅผ ์กฐ๊ธฐ ๊ฐ์งํฉ๋๋ค. ํ ์ ์ฑ ์ ๋ง์ถฐ ํํด์ฃผ์ธ์.
๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override suspend fun getTownTree(): TownTreeResponseDto { | |
| return service.getTownTree().data | |
| } | |
| override suspend fun getTownTree(): TownTreeResponseDto { | |
| val resp = service.getTownTree() | |
| // ์ ํ 1) ์์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋์ฒด | |
| return resp.data ?: TownTreeResponseDto() | |
| // ์ ํ 2) ๊ณ์ฝ ์๋ฐ์ ์ฆ์ ์คํจ๋ก ์ฒ๋ฆฌ | |
| // return requireNotNull(resp.data) { "getTownTree(): response.data is null" } | |
| } |
๐ค Prompt for AI Agents
In
remote/course/src/main/java/com/teamsolply/solply/course/favoriteTown/datasource/FavoriteTownRemoteDataSourceImpl.kt
around lines 13-15, the code returns service.getTownTree().data directly which
can be null; handle this explicitly to avoid NPEs. Fix by either (A) returning a
safe default/empty TownTreeResponseDto when data is null (construct and return
an empty DTO consistent with the model so the UI gets an empty state), or (B)
fail fast by detecting null and throwing a clear, typed exception (e.g.,
IllegalStateException or a custom RemoteDataException) with a descriptive
message so callers can handle the error; implement one of these two behaviors
consistently and add a brief comment indicating which choice was made.
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: 4
Caution
Some comments are outside the diff and canโt be posted inline due to platform limitations.
โ ๏ธ Outside diff range comments (1)
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingScreen.kt (1)
177-189: ๋ง์ง๋ง ํ์ด์ง์์ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์คํฌ๋กค ๊ฐ๋ฅ์ฑ โ ํฌ๋์ ์ํ.page 2์์ currentPage + 1๋ก ์คํฌ๋กคํ๋ฉด ์ด ํ์ด์ง ์๊ฐ 3(0..2)์ผ ๋ ๋ฒ์๋ฅผ ์ด๊ณผํฉ๋๋ค. ๊ฐ๋ ํ ์๋ฃ ๋ก์ง์ผ๋ก ๋ถ๊ธฐํด์ฃผ์ธ์.
2 -> NamingScreen( @@ - onNextClick = { - scope.launch { - pagerState.scrollToPage(pagerState.currentPage + 1) - } - }, + onNextClick = { + scope.launch { + val next = pagerState.currentPage + 1 + if (next < state.totalPageCount) { + pagerState.scrollToPage(next) + } else { + // OnBoarding ์๋ฃ ์ฒ๋ฆฌ ํธ๋ฆฌ๊ฑฐ + checkOnBoardingSuccess(true) + } + } + },Also applies to: 183-185
๐งน Nitpick comments (20)
core/designsystem/src/main/res/drawable/ic_cross.xml (1)
2-5: ํ๋์ฝ๋ฉ ์์ โ ํดํธ ์ ์ฉ์ผ๋ก ํ ๋ง ํธํ์ฑ ๊ฐ์ ์ ์.๋คํฌ ๋ชจ๋/ํ ๋ง ์ ๊ต์ฒด๋ฅผ ์ํด ๋ฒกํฐ ๋ฃจํธ์ tint๋ฅผ ์ ์ธํ๊ณ , ์๋น ์ธก(Icon/ImageView)์์ ํดํธ๋ฅผ ์ ์ดํ ์ ์๊ฒ ํด๋๋ ํธ์ด ์์ ํฉ๋๋ค.
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> + android:viewportHeight="24" + android:tint="?attr/colorControlNormal">Also applies to: 10-11, 15-17
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/component/OnBoardingTownBottomSheet.kt (2)
20-21: ์ปจํ ์ด๋ ์์ ํ๋์ฝ๋ฉ ๋์ Material ํ ๋ง ์ฌ์ฉ ๊ถ์ฅ.SolplyTheme.colors.white๋ ๋คํฌ ๋ชจ๋์์ ๋ถ์กฐํ๋ฅผ ์ผ์ผํฌ ์ ์์ต๋๋ค. Material3 ์ ์ฒด๊ณ๋ฅผ ์ฌ์ฉํด ์ผ๊ด์ฑ์ ํ๋ณดํ์ธ์.
+import androidx.compose.material3.MaterialTheme @@ - containerColor = SolplyTheme.colors.white, + containerColor = MaterialTheme.colorScheme.surface,
21-21: ๋๋๊ทธ ํธ๋ค ์ ๊ฑฐ ์ ์ ๊ทผ์ฑ ๊ณ ๋ ค.dragHandle = null์ด๋ฉด ์ฌ์ฉ์๊ฐ ์ํธ๋ฅผ ๋๋๊ทธ ๊ฐ๋ฅํ๋ค๋ ๋จ์๋ฅผ ์์ต๋๋ค. ๋์์ธ ์ ์ฝ์ด ์๋ค๋ฉด ๊ธฐ๋ณธ ํธ๋ค์ ๋ ธ์ถํ๊ฑฐ๋, ๋์ฒด๋ก ๋ซ๊ธฐ ๋ฒํผ/์ค๋ช (semantics)์ ์ ๊ณตํ์ธ์.
- dragHandle = null + dragHandle = { androidx.compose.material3.SheetDefaults.DragHandle() }feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingScreen.kt (3)
122-127: barText ๋ชจ๋ ๋น ๋ฌธ์์ด โ ์ฌ์ฉ์ ํผ๋๋ฐฑ ๋ถ์กฑ.๋จ๊ณ๋ณ ์๋ด ํ ์คํธ๊ฐ ๋น์ด ์์ต๋๋ค. ๋ฆด๋ฆฌ์ค ์ string ๋ฆฌ์์ค๋ก ๊ต์ฒดํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
137-142: isTownSelected ๋ค์ด๋ฐ์ด ๋จ๊ณ๋ณ ์๋ฏธ์ ๋ถ์ผ์น.ํผ์๋/๋๋ค์ ๋จ๊ณ์์๋ ํด๋น ํ๋๊ทธ๋ฅผ ์ฌ์ฉํฉ๋๋ค. BackHeader API๊ฐ ํ์ฉํ๋ค๋ฉด isStepCompleted ๋ฑ ์ค๋ฆฝ์ ์ด๋ฆ์ผ๋ก ์ ๋ ฌ์ ์ ์ํฉ๋๋ค.
83-89: onBoardingButtonClick ํ๋ผ๋ฏธํฐ ๋ฏธ์ฌ์ฉ โ ์ ๊ฑฐํ๊ฑฐ๋ ๋ฒํผ์ ์ฐ๊ฒฐOnBoardingScreen ์ ์ธ(feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingScreen.kt:83)์ onBoardingButtonClick์ด ์ ์ธ๋์ด ์์ผ๋ ํ์ผ ๋ด์์ ํธ์ถ/์ฌ์ฉ๋์ง ์์ต๋๋ค(์ปดํฌ์ ๋ธ ํธ์ถ๋ถ์์ ์ ๋ฌ๋ง ํจ โ ๋ผ์ธ 63). ๋ถํ์ํ๋ฉด ์๊ทธ๋์ฒ์์ ์ ๊ฑฐํ๊ฑฐ๋ ๋ฒํผ ํด๋ฆญ/์ก์ ์ ์ฐ๊ฒฐํ์ธ์.
domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/TownEntity.kt (1)
8-11: null ๋์ ๋น ๋ฆฌ์คํธ ์ฌ์ฉ์ผ๋ก ๋ ์์ ์ฑ/๋ถ๊ธฐ ๋จ์ํ.subTowns๋ฅผ nullable๋ก ๋๋ฉด UI์์ ๋ฐ๋ณต์ ์ผ๋ก null ์ฒดํฌ๊ฐ ํ์ํฉ๋๋ค. ๋น ๋ฆฌ์คํธ ๊ธฐ๋ณธ๊ฐ์ ์ฌ์ฉํ๋ฉด ๋ฃจํ/์ฐ์ฐ์ด ๋จ์ํด์ง๋๋ค.
data class SubTownEntity( val townId: Long, - val townName: String, - val subTowns: List<SubTownEntity>? = null + val townName: String, + val subTowns: List<SubTownEntity> = emptyList() )ํ์ ์, ๊ธฐ์กด ํธ์ถ๋ถ์์
?.orEmpty()๋ฑ์ ์ ๊ฑฐํ ์ ์์ต๋๋ค.data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt (2)
17-19: subTowns๋ null ๋์ ๋น ๋ฆฌ์คํธ ์ฌ์ฉ์ ๊ณ ๋ คํ์ธ์.UI/๋๋ฉ์ธ์์ ๋ ์ฒ๋ฆฌ ๋น์ฉ์ ์ค์ด๋ ค๋ฉด emptyList๋ก ํต์ผํ๋ ํธ์ด ์์ ํฉ๋๋ค.
๊ฐ๋ฅํ๋ฉด ๋ค์์ฒ๋ผ ๋งคํผ/๋ชจ๋ธ์ ๋ง์ถ๋ ๊ฒ์ ์ ์ํฉ๋๋ค:
- subTowns = this.subTowns?.map { it.toSubEntity() } + subTowns = this.subTowns.orEmpty().map { it.toSubEntity() }๋๋ฉ์ธ
SubTownEntity.subTowns๊ฐ ๋น๋ ๋ฆฌ์คํธ๋ฅผ ํ์ฉํ๋๋ก ํจ๊ป ์ ๋ ฌ ํ์ํฉ๋๋ค.
1-1: ํ์ผ๋ช ์คํ์ ๋๋ค.
TownEntitiyMapper.ktโTownEntityMapper.kt๋ก ๋ณ๊ฒฝ ๊ถ์ฅํฉ๋๋ค.feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt (1)
52-54: ์ง์ญ ๋ณ๊ฒฝ ์ ๊ธฐ์กด ์ ํ ํ์ด์ ์ด๊ธฐํํ๋ ๊ฒ ์์ ํฉ๋๋ค.๋ค๋ฅธ ์ง์ญ์ผ๋ก ๋ฐ๊พธ๋ฉด
selectedTownId๊ฐ ํด๋น ์ง์ญ์ ์ํ์ง ์์ ์ ์์ต๋๋ค.๋ค์๊ณผ ๊ฐ์ด ๋ฆฌ์ ์ ์ ์ํฉ๋๋ค:
- reduce { copy(selectedRegionId = intent.regionId) } + reduce { copy(selectedRegionId = intent.regionId, selectedTownId = null) }core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt (1)
100-105: Color.Unspecified๋ก border๊ฐ ์์ฑ๋ฉ๋๋ค โ null๋ก ๋ถ๊ธฐํ์ธ์.
BaseButton์borderColor != null์ด๋ฉด ํญ์ border๋ฅผ ๊ทธ๋ฆฝ๋๋ค. Unspecified๋ฅผ ๋๊ธฐ๋ฉด ๋ถํ์ํ BorderStroke๊ฐ ์๊น๋๋ค.๋ค์๊ณผ ๊ฐ์ด ์์ ๊ถ์ฅ:
- val borderColor = when { - selected -> SolplyTheme.colors.red300 - else -> Color.Unspecified - } + val borderColor = when { + selected -> SolplyTheme.colors.red300 + else -> null + }data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt (2)
8-10: ํ์ ํ๋์ ๊ธฐ๋ณธ๊ฐ ์ ๊ณต์ ๊ฒํ ํ์ธ์.์๋ฒ๊ฐ ํญ์
towns๋ฅผ ๋ณด๋ธ๋ค๋ฉด ํ ์ํ๋ OK์ง๋ง, ๋ฐฉ์ด์ ์ผ๋ก ๊ธฐ๋ณธ๊ฐ์ด ๋ซ์ต๋๋ค(ํ์ฑ ์คํจ ์ํ).- val towns: List<TownDto> + val towns: List<TownDto> = emptyList()
17-20: subTowns๋ ๋น๋ ๋ฆฌ์คํธ๋ก ํต์ผํ๋ ํธ์ด ๊น๋ํฉ๋๋ค.null ๋์ ๋น ๋ฆฌ์คํธ๋ฅผ ์ฐ๋ฉด ๋งคํผ/UI์์ ?. ์ฒ๋ฆฌ ์ ๊ฑฐ ๊ฐ๋ฅ.
- @SerialName("subTowns") - val subTowns: List<TownDto>? = null + @SerialName("subTowns") + val subTowns: List<TownDto> = emptyList()๋งคํผ์์๋
orEmpty()์ ๊ฑฐ ๊ฐ๋ฅํ๋๋ก ์ ๋ ฌํด ์ฃผ์ธ์.feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt (1)
16-19: selectedRegionId ๊ธฐ๋ณธ๊ฐ 1 ํ๋์ฝ๋ฉ์ ์ํํ ์ ์์ต๋๋ค.ํน์ ์ง์ญ์ผ๋ก ๊ณ ์ ์ด๊ธฐ ์ํ๊ฐ ์๋๋์ง ์์๋ค๋ฉด null๋ก ์์ํ๋ ํธ์ด ์์ ํฉ๋๋ค(์ด๊ธฐ ๋ฏธ์ ํ ํํ).
- val selectedRegionId: Long? = 1, + val selectedRegionId: Long? = null,์ด๊ธฐ null ์ํ๋ฅผ ์ฒ๋ฆฌํ์ง ์๋ ํ๋ฉด/๋ก์ง์ด ์๋์ง ํจ๊ป ํ์ธ ๋ถํ๋๋ฆฝ๋๋ค.
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (6)
3-3: ๋ถํ์ํ ๋๋ฒ๊ทธ ๋ก๊ทธ ์ ๊ฑฐ์๋ฏธ ์๋ ํ๊ทธ/๋ก๊ทธ๊ฐ ๋จ์ ์์ต๋๋ค. ๋ฆด๋ฆฌ์ค ์ ์ ๊ฑฐํ๊ฑฐ๋ DEBUG ๋น๋์์๋ง ์ถ๋ ฅ๋๋๋ก ์ฒ๋ฆฌํด์ฃผ์ธ์.
-import android.util.Log @@ - Log.d("asdasdasd", state.selectedTownId.toString())Also applies to: 54-55
28-28: ํ๋์ฝ๋ฉ๋ ๋ฌธ์์ด์ stringResource๋ก ์นํ (i18n/๋ฒ์ญ ๋๋น)UI ํ ์คํธ๋ฅผ ๋ฆฌ์์ค๋ก ๋ถ๋ฆฌํด์ฃผ์ธ์. ๋ฆฌ์์ค ID๋ ์์์ด๋ฉฐ ํ๋ก์ ํธ ์ปจ๋ฒค์ ์ ๋ง๊ฒ ์กฐ์ ๋ฐ๋๋๋ค.
import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource @@ - Text( - text = "๋ฐ๊ฐ์์!\n๊ฐ์ฅ ๋จผ์ ์ถ์ฒ ๋ฐ๊ณ ์ถ์ ๋๋ค๋ฅผ\n์ ํํด์ฃผ์ธ์.", + Text( + text = stringResource(R.string.onboarding_select_town_title), style = SolplyTheme.typography.display20Sb, color = SolplyTheme.colors.black, modifier = Modifier .padding(top = 24.dp, bottom = 28.dp) ) @@ - Text( - text = "๋๋ค ์ค์ ", + Text( + text = stringResource(R.string.onboarding_town_setting_title), modifier = Modifier.padding( start = 16.dp, top = 24.dp, bottom = 20.dp ), style = SolplyTheme.typography.title18Sb, color = SolplyTheme.colors.black ) @@ - SolplyBasicButton( - text = "์๋ฃ", + SolplyBasicButton( + text = stringResource(R.string.common_done), modifier = Modifier .align(Alignment.BottomCenter) .padding(bottom = 24.dp, start = 20.dp, end = 20.dp), onClick = { if (isButtonEnabled) { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) } }, selected = isButtonEnabled, textStyle = SolplyTheme.typography.button16M, textColor = if (isButtonEnabled) SolplyTheme.colors.white else SolplyTheme.colors.gray800, enabledBackgroundColor = SolplyTheme.colors.gray900, disabledBackgroundColor = SolplyTheme.colors.gray300 ) @@ - SolplyBasicButton( - text = "๋ค์", + SolplyBasicButton( + text = stringResource(R.string.common_next), modifier = Modifier .padding(bottom = 24.dp), onClick = { if (isButtonEnabled) { onNextClick() } }, selected = isButtonEnabled, textStyle = SolplyTheme.typography.button16M, textColor = if (isButtonEnabled) SolplyTheme.colors.white else SolplyTheme.colors.gray800, enabledBackgroundColor = SolplyTheme.colors.gray900, disabledBackgroundColor = SolplyTheme.colors.gray300 )์ถ๊ฐ๋ก strings.xml์ ๋ค์ ํค๋ฅผ ์ ์ํด์ฃผ์ธ์:
- onboarding_select_town_title
- onboarding_town_setting_title
- common_done
- common_next
Also applies to: 66-71, 102-110, 149-163, 169-183
100-111: BottomSheet ๋ด๋ถ 600dp ๊ณ ์ ๋์ด โ ํ๋ฉด ๋์ํ์ผ๋ก์์ ํ๋ฉด/๋ถํ ํ๋ฉด์์ ์ค๋ฒํ๋ก์ฐ/ํด๋ฆฌํ ๊ฐ๋ฅ์ฑ์ด ์์ต๋๋ค. ๋น์จ ๊ธฐ๋ฐ ๋์ด์ ํ๋จ ๋ฒํผ ์์ญ ์ฌ๋ฐฑ์ ๊ถ์ฅํฉ๋๋ค.
- Column(modifier = Modifier.height(600.dp)) { + Column( + modifier = Modifier + .fillMaxHeight(0.85f) + .padding(bottom = 88.dp) // ํ๋จ '์๋ฃ' ๋ฒํผ ๊ฒน์นจ ๋ฐฉ์ง + ) {
200-201: LazyColumn์ key ์ถ๊ฐํ์ฌ ์ฌ๊ตฌ์ฑ ์์ ์ฑ/์ฑ๋ฅ ๊ฐ์์์ ํค๋ฅผ ์ง์ ํ๋ฉด ์คํฌ๋กค ์ค ์ํ ๋ณด์กด ๋ฐ ๋ถํ์ํ ์ฌ์กฐํฉ์ ์ค์ผ ์ ์์ต๋๋ค.
- itemsIndexed(items = regions) { index: Int, item: SubTownEntity -> + itemsIndexed(items = regions, key = { _, item -> item.townId }) { index: Int, item: SubTownEntity ->- itemsIndexed(items = towns) { index, item -> + itemsIndexed(items = towns, key = { _, item: SubTownEntity -> item.townId }) { index, item ->Also applies to: 258-259
22-29: a11y: ripple ์๋ customClickable์ ์ญํ /์ ๊ทผ์ฑ semantics ์ถ๊ฐ ๊ถ์ฅ์๊ฐ์ ํผ๋๋ฐฑ์ด ์๋ ํด๋ฆญ ์์ญ์ ๋ณด์กฐ๊ธฐ๊ธฐ์์ ๋ฒํผ ์ญํ ์ธ์ง๊ฐ ๋จ์ด์ง ์ ์์ต๋๋ค. Role ์ง์ ๋ฑ semantics๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์.
+import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.semantics @@ .background(bg) .customClickable(rippleEnabled = false) { onSelect(item.townId) } + .semantics { role = Role.Button } .drawBehind {modifier = Modifier .fillMaxWidth() .height(46.dp) - .customClickable(rippleEnabled = false) { onSelect(item.townId) } + .customClickable(rippleEnabled = false) { onSelect(item.townId) } + .semantics { role = Role.Button } .drawBehind {๊ฐ๋ฅํ๋ฉด TalkBack์ฉ contentDescription/์ํ ์ฝ๊ธฐ๋ ๊ฒํ ํด์ฃผ์ธ์(์: โ์ ํ๋จ/๋ฏธ์ ํโ).
Also applies to: 206-214, 263-271
73-91: ๋ฏธ์ธ ์ต์ ํ: ์ ํ๋ ๋๋ค๋ช ๊ณ์ฐ ์บ์๋งค recomposition๋ง๋ค flatMap/find๋ฅผ ์ํํฉ๋๋ค. derivedState๋ก ์บ์ํ๋ฉด ๊ฐ๋ ์ฑ๊ณผ ๋ฏธ์ธ ์ฑ๋ฅ์ด ๊ฐ์ ๋ฉ๋๋ค.
์์:
val selectedTownName by remember(state.selectedTownId, townList.towns) { derivedStateOf { townList.towns.asSequence() .flatMap { it.subTowns.orEmpty().asSequence() } .firstOrNull { it.townId == state.selectedTownId } ?.townName } }๊ทธ๋ฆฌ๊ณ text = selectedTownName ?: "" ๋ก ์ฌ์ฉ.
๐ Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
๐ Files selected for processing (12)
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt(1 hunks)core/designsystem/src/main/res/drawable/ic_cross.xml(1 hunks)data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt(1 hunks)data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt(1 hunks)domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/TownEntity.kt(1 hunks)feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt(1 hunks)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt(2 hunks)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingScreen.kt(3 hunks)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt(2 hunks)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/component/OnBoardingTownBottomSheet.kt(1 hunks)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt(3 hunks)remote/onboarding/src/main/java/com/teamsolply/solply/onboarding/service/OnBoardingService.kt(1 hunks)
๐ง Files skipped from review as they are similar to previous changes (1)
- feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt
๐งฐ Additional context used
๐งฌ Code graph analysis (2)
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (2)
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt (2)
AddLocalAreaButton(91-130)SolplyBasicButton(63-89)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/component/OnBoardingTownBottomSheet.kt (1)
OnBoardingTownBottomSheet(10-25)
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt (1)
core/ui/src/main/java/com/teamsolply/solply/ui/base/BaseViewModel.kt (1)
reduce(44-46)
๐ Additional comments (8)
remote/onboarding/src/main/java/com/teamsolply/solply/onboarding/service/OnBoardingService.kt (1)
19-20: ์๋ํฌ์ธํธ ๋ณ๊ฒฝ์ผ๋ก ์ธ์ฆยท์คํค๋งยท์บ์ฑ ์ ๊ฒ ํ์api/users/towns โ api/towns๋ก ์ ์ ์ปจํ ์คํธ๊ฐ ๋น ์ก์ต๋๋ค. ์๋ ํญ๋ชฉ์ ์ต์ํ์ผ๋ก ํ์ธํ์ธ์.
์ํฅ ๋ฒ์(์ฐธ๊ณ ํ์ผ):
- remote/onboarding/src/main/java/com/teamsolply/solply/onboarding/service/OnBoardingService.kt โ @get("api/towns")
- remote/onboarding/src/main/java/com/teamsolply/solply/onboarding/datasource/OnBoardingRemoteDataSourceImpl.kt โ onBoardingService.getAllTowns().data
- data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt โ towns: List
- data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt โ towns.map { it.toSubEntity() }
- data/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepositoryImpl.kt โ responseDto.toEntity()
- feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt โ repository.getAllTowns() ํธ์ถ์ง
ํ์ธ ํญ๋ชฉ:
- ์ธ์ฆ: ์๋ฒ๊ฐ ์ด์ ์ธ์ฆ(์ ์ ์ปจํ ์คํธ)์ ์๊ตฌํ๋์ง ํ์ธ. ์๊ตฌ ์ Retrofit ํธ์ถ์ Authorization ํค๋(๋๋ OkHttp ์ธํฐ์ ํฐ) ์ ์ฉ ์ฌ๋ถ ๊ฒ์ฌ.
- ์๋ต ์คํค๋ง/๋งคํ: TownDto์ toSubEntity() ๊ตฌํ์ด ์ค์ ๋ก "ํธ๋ฆฌ ๊ตฌ์กฐ(๋ถ๋ชจโ์์)"๋ฅผ ํํํ๋์ง ๊ฒ์ฆ โ ํธ๋ฆฌ๋ฉด ๋ฉ์๋๋ช (getTownTree ๋ฑ) ๋ช ํํ ๊ณ ๋ ค.
- ์บ์ฑ/์ฑ๋ฅ: ์ ์ฒด ํ์ด ํธ๋ฆฌ๊ฐ ํด ๊ฒฝ์ฐ ์บ์ ์ ๋ต ๋๋ ํ์ด์ง/์ง์ฐ ๋ก๋ฉ ํ์์ฑ ๊ฒํ .
๊ฒ์ฆ์ ์คํจํ๊ฑฐ๋ ์ถ๊ฐ ํ์ธ์ด ํ์ํ๋ฉด toSubEntity ๊ตฌํ ๋ฐ Authorization ์ฒ๋ฆฌ ์์น๋ฅผ ์๋ ค์ฃผ๋ฉด ์ฌํ์ธํฉ๋๋ค.
data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt (1)
10-11: TownEntity.towns ๋งคํ ์ ํ์ ๊น๋ํฉ๋๋ค.๋จ์ ๋ฆฌ์คํธ ๋งคํ์ผ๋ก ์ ๋ฆฌ๋ ์ ์ข์ต๋๋ค. ๋ฐฑ์๋ ์๋ต์ ํธ๋ฆฌ ์ต๋ ๊น์ด(์ํ/๊ณผ๋ํ ์ค์ฒฉ ์์)๋ง ํ๋ฒ ํ์ธ ๋ถํ๋๋ฆฝ๋๋ค.
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt (2)
48-50: ๋ฐํ ์ํธ ํ ๊ธ ๋ก์ง ๊ฐ๊ฒฐํ๊ณ ๋ช ํํฉ๋๋ค.
57-57: ํ์ด ์ ํ ํ ๊ธ UX๋ ์ ์ ํฉ๋๋ค.feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt (2)
13-14: TownEntity ์ด๊ธฐํ ๋ฐฉ์์ ๋ช ํํ๊ณ ์์ ํฉ๋๋ค.
30-31: ์ Intent ์ถ๊ฐ๋ ์ ์ ํฉ๋๋ค.๋ทฐ/์คํฌ๋ฆฐ์์ ์๋น(collect/handle)๊น์ง ์ฐ๊ฒฐ๋ง ํ์ธํด ์ฃผ์ธ์.
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt (1)
95-97: ํ์ ํธํ์ฑ ํ์ธ โ ๋ด๋ถ ํธ์ถ์ ์์ ํ์ง๋ง ์ธ๋ถ ์๋น์ ํ์ธ ๊ถ์ฅ๋ฆฌํฌ์งํ ๋ฆฌ ๊ฒ์ ๊ฒฐ๊ณผ AddLocalAreaButton ํธ์ถ์ ๋ค์ ๋ ๊ณณ์์๋ง ๋ฐ๊ฒฌ๋ฉ๋๋ค: feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (์ฝ ๋ผ์ธ 76, 82). ๋ ํธ์ถ ๋ชจ๋ text=โฆ ๋ก ๋ช ๋ช ์ธ์๋ฅผ ์ฌ์ฉํ๋ฏ๋ก ํ์ฌ ๋ณ๊ฒฝ์ผ๋ก๋ ๋ด๋ถ ์ปดํ์ผ ํ์์ด ๋ฐ์ํ์ง ์์ต๋๋ค. ์ด API๊ฐ ์ธ๋ถ(๋ค๋ฅธ ๋ชจ๋/๋ผ์ด๋ธ๋ฌ๋ฆฌ)์๋ ๋ ธ์ถ๋์ด ์๋ค๋ฉด ๊ตฌ ์๊ทธ๋์ฒ ์ค๋ฒ๋ก๋(+@deprecated) ์ถ๊ฐ๋ฅผ ๊ถ์ฅํฉ๋๋ค.
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (1)
112-146: 2โPane(์ง์ญ/๋๋ค) ๋ถํ ๊ณผ Intent ์ฐ๋ ๊ตฌ์กฐ๋ ๋ช ํํฉ๋๋ค์ํ ์ ํ/์๋ ์ฒ๋ฆฌ ํ๋ฆ์ด ๊น๋ํ๋ฉฐ, Divider/์ ํ ์คํ์ผ๋ง๋ ์ผ๊ด์ ์ ๋๋ค. ๊ณ์ ์ด ํจํด ์ ์งํ๋ฉด ์ข๊ฒ ์ต๋๋ค.
| BaseButton( | ||
| onClick = onClick, | ||
| modifier = modifier.size(95.dp), | ||
| modifier = modifier.fillMaxWidth(), |
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.
๐งฉ Analysis chain
๋ด๋ถ ์ฝํ ์ธ ์ ์ธ๋ถ modifier ์ฌ์ฌ์ฉ์ ๋ ์ด์์/์ ์ค์ฒ ๋ถ์์ฉ์ ์ ๋ฐํฉ๋๋ค.
modifier.fillMaxWidth()๋ฅผ ์ธ๊ณฝ์ ์ ์ฉํ ๋ค, ๋์ผ modifier๋ฅผ ์์ด์ฝ/ํ
์คํธ์ ์ฌ์ฌ์ฉํ๋ฉด weight/size/testTag/ํด๋ฆญ ๋ฑ์ด ์์๊น์ง ์ ํ๋ฉ๋๋ค. ๋ด๋ถ์๋ Modifier๋ฅผ ์ฌ์ฉํ์ธ์. ๋ํ a11y ๋ฌธ์์ด์ stringResource๋ก ๋ถ๋ฆฌํ์ธ์.
- modifier = modifier.fillMaxWidth(),
+ modifier = modifier.fillMaxWidth(),
@@
- if (text.isNullOrEmpty()) {
+ if (text.isNullOrEmpty()) {
Icon(
painter = painterResource(R.drawable.ic_cross),
- contentDescription = "show_more",
- modifier = modifier.padding(vertical = 14.dp),
+ contentDescription = stringResource(R.string.cd_add_local_area),
+ modifier = Modifier.padding(vertical = 14.dp),
tint = Color.Unspecified
)
} else {
Text(
text = text,
- modifier = modifier.padding(vertical = 14.dp),
+ modifier = Modifier.padding(vertical = 14.dp),
style = SolplyTheme.typography.body16R,
color = SolplyTheme.colors.gray900
)
}์ถ๊ฐ๋ก stringResource import ๋ฐ R.string.cd_add_local_area ๋ฌธ์์ด ๋ฆฌ์์ค๊ฐ ํ์ํฉ๋๋ค.
Also applies to: 114-121, 122-128
๐ Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching repository for SolplyButton..."
file=$(rg --hidden --files | rg "SolplyButton.kt$" || true)
if [ -z "$file" ]; then
echo "Not found by exact filename; searching for symbol 'SolplyButton'..."
matches=$(rg -n --hidden -S "SolplyButton" || true)
echo "$matches"
file=$(echo "$matches" | head -n1 | cut -d: -f1 || true)
fi
if [ -n "$file" ]; then
echo "Found: $file"
echo "----- full file (first 260 lines) -----"
sed -n '1,260p' "$file" || true
echo "----- context lines 80-140 -----"
sed -n '80,140p' "$file" || true
else
echo "SolplyButton not found. Showing Kotlin files with 'button' in name/path (top 50):"
rg --hidden --files | rg -i "button" | head -n 50 || true
fiLength of output: 8033
์์ ํ์: AddLocalAreaButton ๋ด๋ถ์์ ์ ๋ฌ๋ modifier ์ฌ์ฌ์ฉ ๋ฐ ํ๋์ฝ๋ฉ๋ ์ ๊ทผ์ฑ ๋ฌธ์์ด ์ ๊ฑฐ
- ์ ๋ฌ๋ modifier๋ฅผ ์์(Icon/Text)์์ ์ฌ์ฌ์ฉํ์ง ๋ง๊ณ ๋ด๋ถ์์๋ Modifier๋ฅผ ์ฌ์ฉํ์ธ์ (modifier.padding โ Modifier.padding).
- contentDescription ๋ฑ ์ ๊ทผ์ฑ ๋ฌธ์์ด์ ํ๋์ฝ๋ฉํ์ง ๋ง๊ณ stringResource๋ก ๋ถ๋ฆฌํ์ธ์. AddCourseButton ๋ฑ ์ ์ฌ ์์น๋ ํจ๊ป ๊ต์ฒดํ์ธ์.
- modifier = modifier.fillMaxWidth(),
+ modifier = modifier.fillMaxWidth(),
@@
- if (text.isNullOrEmpty()) {
+ if (text.isNullOrEmpty()) {
Icon(
painter = painterResource(R.drawable.ic_cross),
- contentDescription = "show_more",
- modifier = modifier.padding(vertical = 14.dp),
+ contentDescription = stringResource(R.string.cd_add_local_area),
+ modifier = Modifier.padding(vertical = 14.dp),
tint = Color.Unspecified
)
} else {
Text(
text = text,
- modifier = modifier.padding(vertical = 14.dp),
+ modifier = Modifier.padding(vertical = 14.dp),
style = SolplyTheme.typography.body16R,
color = SolplyTheme.colors.gray900
)
}์ถ๊ฐ๋ก stringResource import ๋ฐ R.string.cd_add_local_area ๋ฌธ์์ด ๋ฆฌ์์ค๊ฐ ํ์ํฉ๋๋ค.
๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| modifier = modifier.fillMaxWidth(), | |
| modifier = modifier.fillMaxWidth(), | |
| if (text.isNullOrEmpty()) { | |
| Icon( | |
| painter = painterResource(R.drawable.ic_cross), | |
| contentDescription = stringResource(R.string.cd_add_local_area), | |
| modifier = Modifier.padding(vertical = 14.dp), | |
| tint = Color.Unspecified | |
| ) | |
| } else { | |
| Text( | |
| text = text, | |
| modifier = Modifier.padding(vertical = 14.dp), | |
| style = SolplyTheme.typography.body16R, | |
| color = SolplyTheme.colors.gray900 | |
| ) | |
| } |
๐ค Prompt for AI Agents
In
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt
around line 109, the AddLocalAreaButton is reusing the incoming modifier for
internal child layout and hardcoding accessibility strings; change internal
child modifiers to use Modifier (e.g., replace modifier.padding(...) with
Modifier.padding(...)) so the passed-in modifier is not consumed by children,
replace hardcoded contentDescription/text with
stringResource(R.string.cd_add_local_area) and add the import for
androidx.compose.ui.res.stringResource, ensure R.string.cd_add_local_area exists
in strings.xml, and make the same modifier/contentDescription/stringResource
replacements in AddCourseButton and other similar components.
| android:pathData="M11.995,2V22" | ||
| android:strokeWidth="2" | ||
| android:fillColor="#00000000" | ||
| android:strokeColor="#D1CACD" | ||
| android:strokeLineCap="round"/> | ||
| <path | ||
| android:pathData="M22,11.995L2,11.995" | ||
| android:strokeWidth="2" | ||
| android:fillColor="#00000000" | ||
| android:strokeColor="#D1CACD" | ||
| android:strokeLineCap="round"/> |
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.
๋ฆฌ์์ค ๋ช ๊ณผ ์์ด์ฝ ํํ๊ฐ ๋ถ์ผ์นํฉ๋๋ค (Cross โ Plus).
ํ์ผ๋ช ์ด ic_cross์ธ๋ฐ, ํ์ฌ ๋ฒกํฐ๋ ์์ง/์ํ ์คํธ๋กํฌ๋ก โ+โ๋ฅผ ๊ทธ๋ฆฌ๊ณ ์์ต๋๋ค. ๊ธฐ์กด โ๋ซ๊ธฐ(X)โ ์ฉ๋๋ก ์ฐ์๋ค๋ฉด UI ์คํด๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค. โ+โ๊ฐ ์๋๋ผ๋ฉด ๋ฆฌ์์ค๋ช ์ ic_plus๋ก ๊ต์ฒดํ๊ณ , โXโ๊ฐ ์๋๋ผ๋ฉด ์๋์ฒ๋ผ ๋๊ฐ์ ๊ต์ฐจ๋ก ๋ณต๊ตฌํด์ฃผ์ธ์.
- <path
- android:pathData="M11.995,2V22"
- android:strokeWidth="2"
- android:fillColor="#00000000"
- android:strokeColor="#D1CACD"
- android:strokeLineCap="round"/>
- <path
- android:pathData="M22,11.995L2,11.995"
- android:strokeWidth="2"
- android:fillColor="#00000000"
- android:strokeColor="#D1CACD"
- android:strokeLineCap="round"/>
+ <path
+ android:pathData="M18,6 L6,18"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#D1CACD"
+ android:strokeLineCap="round"/>
+ <path
+ android:pathData="M6,6 L18,18"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#D1CACD"
+ android:strokeLineCap="round"/>๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| android:pathData="M11.995,2V22" | |
| android:strokeWidth="2" | |
| android:fillColor="#00000000" | |
| android:strokeColor="#D1CACD" | |
| android:strokeLineCap="round"/> | |
| <path | |
| android:pathData="M22,11.995L2,11.995" | |
| android:strokeWidth="2" | |
| android:fillColor="#00000000" | |
| android:strokeColor="#D1CACD" | |
| android:strokeLineCap="round"/> | |
| <path | |
| android:pathData="M18,6 L6,18" | |
| android:strokeWidth="2" | |
| android:fillColor="#00000000" | |
| android:strokeColor="#D1CACD" | |
| android:strokeLineCap="round"/> | |
| <path | |
| android:pathData="M6,6 L18,18" | |
| android:strokeWidth="2" | |
| android:fillColor="#00000000" | |
| android:strokeColor="#D1CACD" | |
| android:strokeLineCap="round"/> |
๐ค Prompt for AI Agents
core/designsystem/src/main/res/drawable/ic_cross.xml lines 7-17: ํ์ฌ ํ์ผ๋ช
์
ic_cross์ธ๋ฐ ๋ฒกํฐ๋ ์์ง/์ํ ์คํธ๋กํฌ๋ก โ+โ๋ฅผ ๊ทธ๋ฆฌ๊ณ ์์ด ๋ฆฌ์์ค๋ช
๊ณผ ๋ด์ฉ์ด ๋ถ์ผ์นํฉ๋๋ค; ๋ง์ฝ ์์ด์ฝ ์๋๊ฐ ํ๋ฌ์ค(+)๋ฉด ํ์ผ๋ช
์
ic_plus.xml๋ก ๋ณ๊ฒฝํ๊ณ ๋ชจ๋ ์ฐธ์กฐ๋ฅผ ํจ๊ป ์
๋ฐ์ดํธ(๋ ์ด์์/XML/์คํ์ผ/์ฝ๋์์ ic_cross โ ic_plus)ํ์ธ์; ๋ฐ๋๋ก
๋ซ๊ธฐ(X)๊ฐ ์๋๋ผ๋ฉด ๋ฒกํฐ ๊ฒฝ๋ก๋ฅผ ์์ง/์ํ ์ ๋์ ์ข์-์ฐํ์ ์ฐ์-์ขํ๋ก ๊ต์ฐจํ๋ ๋๊ฐ์ ํจ์ค๋ก ๊ต์ฒดํ๊ณ ๋์ผํ strokeWidth,
strokeColor, strokeLineCap ์์ฑ์ ์ ์งํด ๊ธฐ์กด ๋ ์ด์์ ๋์์ ๋ณด์กดํ์ธ์.
| onBoardingRepository.patchUserInfo( | ||
| selectedTownId = selectedTownId, | ||
| favoriteTownIdList = uiState.value.townList.favoriteTownList.map { it.townId }, | ||
| favoriteTownIdList = uiState.value.townList.towns.map { it.townId }, |
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.
๐งฉ Analysis chain
favoriteTownIdList์ ์ ์ฒด ํ์ด์ ์ ์กํ๋ ๊ฒ์ ๊ณผ๋/์ค์ผ ์ํ์ด ์์ต๋๋ค.
ํ์ฌ๋ ์ ํ๊ณผ ๋ฌด๊ดํ๊ฒ townList.towns.map { it.townId } ์ ์ฒด๋ฅผ ๋ณด๋
๋๋ค. ์ฆ๊ฒจ์ฐพ๊ธฐ ์๋์ ๋ถ์ผ์นํ ์ ์์ด ์๋ฒ ๋ฐ์ดํฐ๊ฐ ์ค์ผ๋ ์ ์์ต๋๋ค.
์์ ๊ธฐ๋ณธ๊ฐ(์ ํ ์์=๋น ๋ฆฌ์คํธ, ๋จ์ผ ์ ํ=๊ทธ ํ๋)์ผ๋ก ์์ ์ ์:
- favoriteTownIdList = uiState.value.townList.towns.map { it.townId },
+ favoriteTownIdList = uiState.value.selectedTownId?.let { listOf(it) } ?: emptyList(),๋ค์ ์คํฌ๋ฆฝํธ๋ก ๋ ํฌ ๋ด ์๋ฏธ/ํธ์ถ๋ถ๋ฅผ ํ์ธํด ์ฃผ์ธ์:
๐ Script executed:
#!/bin/bash
# patchUserInfo ์๊ทธ๋์ฒ์ favoriteTownIdList ์ฌ์ฉ์ฒ ์ ๊ฒ
rg -nP -C3 'patchUserInfo\s*\('
rg -n 'favoriteTownIdList' -C2
# ๊ธฐ์กด favoriteTownList ๋ช
์นญ ์์กด ์ฌ๋ถ ์ ๊ฒ
rg -n 'favoriteTownList' -C2Length of output: 18357
favoriteTownIdList์ ์ ์ฒด ํ์ด ์ ์ก โ ์๋ฒ ๋ฐ์ดํฐ ์ค์ผ ์ํ, ์ฆ์ ์์ ํ์
๊ฒ์ฆ: patchUserInfo ์๊ทธ๋์ฒ๊ฐ favoriteTownIdList: List์ธ ๊ฒ์ด ํ์ธ๋์๊ณ , feature/onboarding/.../OnBoardingViewModel.kt์์ ์ ์ฒด townList.towns.map { it.townId }๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด ํ์ธ๋์์ต๋๋ค.
- favoriteTownIdList = uiState.value.townList.towns.map { it.townId },
+ favoriteTownIdList = uiState.value.selectedTownId?.let { listOf(it) } ?: emptyList(),์์น: feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt (๋ผ์ธ ~121-124)
๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| favoriteTownIdList = uiState.value.townList.towns.map { it.townId }, | |
| favoriteTownIdList = uiState.value.selectedTownId?.let { listOf(it) } ?: emptyList(), |
๐ค Prompt for AI Agents
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt
around line 121-124: currently favoriteTownIdList is set to
uiState.value.townList.towns.map { it.townId } which sends all town IDs to the
server and risks data corruption; change it to send only the user-selected town
IDs (for example filter towns by a selected/checked field or use the view
model's selectedTownIds state), map those to Long, remove duplicates, and ensure
the list is empty if nothing is selected before calling patchUserInfo.
| AddLocalAreaButton( | ||
| text = subTown.townName, | ||
| onClick = { onBoardingIntent(OnBoardingIntent.OnTownSelected(subTown.townId)) }, | ||
| isShowMore = false, | ||
| selected = state.selectedTownId == subTown.townId | ||
| text = townList.towns | ||
| .flatMap { it.subTowns ?: emptyList() } | ||
| .find { it.townId == state.selectedTownId } | ||
| ?.townName ?: "", | ||
| onClick = {}, | ||
| selected = true | ||
| ) | ||
| } |
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.
์ ํ ์ดํ ๋๋ค ๋ณ๊ฒฝ์ด ๋ถ๊ฐํจ (onClick = {})
์ ํ๋ ์ํ์์ ๋ฒํผ ํด๋ฆญ ์ ๋ฐํ ์ํธ๊ฐ ์ด๋ฆฌ์ง ์์ ์ฌ์ฉ์๊ฐ ์ฌ์ ํ์ ํ ์ ์์ต๋๋ค.
- AddLocalAreaButton(
+ AddLocalAreaButton(
text = townList.towns
.flatMap { it.subTowns ?: emptyList() }
.find { it.townId == state.selectedTownId }
?.townName ?: "",
- onClick = {},
+ onClick = { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) },
selected = true
)๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| AddLocalAreaButton( | |
| text = subTown.townName, | |
| onClick = { onBoardingIntent(OnBoardingIntent.OnTownSelected(subTown.townId)) }, | |
| isShowMore = false, | |
| selected = state.selectedTownId == subTown.townId | |
| text = townList.towns | |
| .flatMap { it.subTowns ?: emptyList() } | |
| .find { it.townId == state.selectedTownId } | |
| ?.townName ?: "", | |
| onClick = {}, | |
| selected = true | |
| ) | |
| } | |
| AddLocalAreaButton( | |
| text = townList.towns | |
| .flatMap { it.subTowns ?: emptyList() } | |
| .find { it.townId == state.selectedTownId } | |
| ?.townName ?: "", | |
| onClick = { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) }, | |
| selected = true | |
| ) |
๐ค Prompt for AI Agents
In
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt
around lines 82-90 the AddLocalAreaButton for the selected town uses an empty
onClick (onClick = {}), preventing reopening the bottom sheet and re-selection;
replace the empty lambda with the same click handler used for unselected buttons
(or call the function that opens the bottom sheet / toggles selection state) so
clicking a selected button opens the bottom sheet to allow changing town while
keeping selected = true.
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: 3
Caution
Some comments are outside the diff and canโt be posted inline due to platform limitations.
โ ๏ธ Outside diff range comments (1)
feature/maps/src/main/java/com/teamsolply/solply/maps/component/CourseItem.kt (1)
39-40: ์ปดํ์ผ๋ฌ ์ค๋ฅ: ์ต์์ ํจ์ import ๊ฒฝ๋ก๊ฐ ์๋ชป๋์์ต๋๋คKotlin import๋ FQCN์ด ํ์ํฉ๋๋ค. ํ์ฌ ๋ ์ค์ ์ปดํ์ผ๋์ง ์์ต๋๋ค. ์์ ์์น(
com.teamsolply.solply.ui.extension)๋ก ์์ ํด ์ฃผ์ธ์.-import formatTextToPlaceItem -import formatTextToPlaceItemTitle +import com.teamsolply.solply.ui.extension.formatTextToPlaceItem +import com.teamsolply.solply.ui.extension.formatTextToPlaceItemTitle
โป๏ธ Duplicate comments (1)
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (1)
79-86: ์ ํ๋ ๋๋ค ๋ณ๊ฒฝ ๋ถ๊ฐ ๋ฌธ์ ํ์ฌ ๊ตฌํ์์๋ ๋๋ค๊ฐ ์ ํ๋ ํ
AddLocalAreaButton์onClick์ด ๋น ๋๋ค({})๋ก ์ค์ ๋์ด ์์ด, ์ฌ์ฉ์๊ฐ ์ ํํ ๋๋ค๋ฅผ ๋ค์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค.์ฌ์ฉ์๊ฐ ์ ํ์ ๋ณ๊ฒฝํ ์ ์๋๋ก ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์ธ์:
AddLocalAreaButton( text = townList.towns .flatMap { it.subTowns ?: emptyList() } .find { it.townId == state.selectedTownId } ?.townName ?: "", - onClick = {}, + onClick = { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) }, selected = true )
๐งน Nitpick comments (25)
core/designsystem/src/main/res/drawable/ic_place_navigation.xml (1)
7-8: ํ๋์ฝ๋ฉ๋ ์์(#4F4B4B) โ ํ ๋ง/ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ์ ํ ๊ถ์ฅ๋ฐํ์ ํ ๋ง ๋์(๋ผ์ดํธ/๋คํฌยทํ์ด์ฝํธ๋ผ์คํธ)๊ณผ ๋ธ๋๋ ์ปฌ๋ฌ ๋ณ๊ฒฝ ์ฉ์ด์ฑ์ ์ํด fillColor๋ฅผ ๋์์ธ ์์คํ ์ปฌ๋ฌ(@color/โฆ) ๋๋ ํ ๋ง ์์ฑ(?attr/โฆ)์ผ๋ก ์นํ์ ๊ถ์ฅํฉ๋๋ค. ๋ํ ์ด ์์ด์ฝ์ fill ๊ธฐ๋ฐ์ธ๋ฐ, ํ๋ก์ ํธ ๋ด ๋ค๋ฅธ ์์ด์ฝ์ด stroke ์์ฃผ๋ผ๋ฉด ์คํ์ผ ๊ฐ์ด๋ ์ผ๊ด์ฑ๋ ์ ๊ฒํด ์ฃผ์ธ์. RTL ํ๊ฒฝ์์ ๋ฐฉํฅ ์๋ฏธ๊ฐ ์๋ค๋ฉด ๋ฃจํธ์ android:autoMirrored="true" ์ถ๊ฐ๋ ๊ณ ๋ คํด ์ฃผ์ธ์.
์: ๋ฃจํธ์ ํดํธ ์ ์ฉ(์ฐธ๊ณ ์ฉ, ์ค์ ํ ํฐ๋ช ์ผ๋ก ๊ต์ฒด ํ์)
<vector ... android:tint="?attr/colorOnSurfaceVariant"> <path android:pathData="..." android:fillColor="@android:color/white"/> </vector>core/designsystem/src/main/res/drawable/ic_course_edit.xml (2)
7-8: Figma ๋ด๋ณด๋ด๊ธฐ ์์์ strokeWidth ์ ๋ฆฌ ์ ์strokeWidth="2.31251"์ ๋ ๋๋ง ๋จ๊ณ์์ ๋ฐํฝ์ ์ค๋ ์ด์๋ฅผ ์ ๋ฐํ ์ ์์ต๋๋ค. 2.25 ๋๋ 2.5 ๋ฑ ๊ทธ๋ฆฌ๋ ์นํ๊ฐ์ผ๋ก ์ ๋ฆฌํ๋ฉด ์คํฐ์จ๋ฆฌ์ด์ฑ ํ๋ค๋ฆผ์ ์ค์ผ ์ ์์ต๋๋ค. ์๋์ฒ๋ผ ์ ๊ทํํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
- android:strokeWidth="2.31251" + android:strokeWidth="2.25"
12-13: ๋ณด์กฐ ์ ์ strokeWidth๋ ๋์ผํ๊ฒ ์ ๊ทํ์ฒซ ๋ฒ์งธ ๊ฒฝ๋ก์ ๋์ผํ ๊ฐ์ผ๋ก ํต์ผํด ์๊ฐ์ ์ผ๊ด์ฑ์ ํ๋ณดํ์ธ์.
- android:strokeWidth="2.31251" + android:strokeWidth="2.25"์ถ๊ฐ๋ก strokeColor๊ฐ #B8AFB3๋ก ํ๋์ฝ๋ฉ๋์ด ์์ต๋๋ค. ํ ๋ง/ํ ํฐ(@color ๋๋ ?attr)๋ก ์นํํ๋ฉด ๋คํฌ๋ชจ๋ยท๊ณ ๋๋น ๋ชจ๋ ๋์์ด ์ฌ์์ง๋๋ค.
feature/maps/src/main/java/com/teamsolply/solply/maps/component/CourseItem.kt (5)
131-131: ์ฌ๋ฐฑ ์๋ ๋ช ์: Spacer์ padding ๋์ width ์ฌ์ฉ ๊ถ์ฅ
Spacer์padding(end = 22.dp)๋ ๋์ํ๊ธด ํ์ง๋ง ์๋๊ฐ ๋ถ๋ถ๋ช ํฉ๋๋ค. ํญ์ ๋ช ์ํด ๊ฐ๋ ์ฑ์ ๋์ด์ธ์.- Spacer(modifier = Modifier.padding(end = 22.dp)) + Spacer(modifier = Modifier.width(22.dp))
113-118: ์คํ ์์ : contentDescription ๋ฌธ์์ด
"item_edting"โ"item_editing".- contentDescription = "item_edting", + contentDescription = "item_editing",
121-129: ์์ด์ฝ ํฐ์น ํ๊ฒ 48dp ํ๋ณด๋ถ๋งํฌ ์์ด์ฝ ํด๋ฆญ ์์ญ์ด 48dp ๋ฏธ๋ง์ผ ์ ์์ต๋๋ค. ์ ๊ทผ์ฑ ๊ฐ์ด๋ ์ค์๋ฅผ ์ํด ์ต์ ์ํธ์์ฉ ํฌ๊ธฐ๋ฅผ ๋ถ์ฌํ์ธ์.
- contentDescription = "saved marker", - modifier = Modifier.customClickable(rippleEnabled = false) { - iconClick() - }, + contentDescription = "saved marker", + modifier = Modifier + .minimumInteractiveComponentSize() + .customClickable(rippleEnabled = false) { iconClick() },
147-169: ๋ฌธ์์ด ํ๋์ฝ๋ฉ ์ ๊ฑฐ ๋ฐ ์ ๊ทผ์ฑ ์ญํ (Role) ์ง์ ๊ถ์ฅ"๊ธธ์ฐพ๊ธฐ", "์์ธ๋ณด๊ธฐ"๋ string ๋ฆฌ์์ค๋ก ๋ถ๋ฆฌํ์ธ์. ๋ํ
customClickable์ด TalkBack์ ๋ฒํผ ์ญํ ์ ๋ ธ์ถํ๋์ง ํ์ธ ํ์ํฉ๋๋ค. ๋ ธ์ถํ์ง ์๋๋ค๋ฉดsemantics { role = Role.Button }์ถ๊ฐ๋ฅผ ๊ณ ๋ คํด ์ฃผ์ธ์.
47-47: ํ๋ผ๋ฏธํฐ ๋ช ๋ช ๊ฐ์ ์ ์
placeImageRes: String์ URL์ฒ๋ผ ๋ณด์ ๋๋ค. ์คํด ๋ฐฉ์ง๋ฅผ ์ํดplaceImageUrl๋ฑ์ผ๋ก ๋ณ๊ฒฝ์ ๊ณ ๋ คํ์ธ์.feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (6)
145-160: ๋ฐํ ์ํธ ์๋ฃ ๋ฒํผ ๋์ ๊ฐ์ ํ์๋ฐํ ์ํธ์ ์๋ฃ ๋ฒํผ์ด ๋นํ์ฑํ ์ํ์์๋ ํด๋ฆญ ๊ฐ๋ฅํ๊ฒ ์ค์ ๋์ด ์์ต๋๋ค.
SolplyBasicButton์์selected์์ฑ์ ์คํ์ผ๋ง ๋ณ๊ฒฝํ๊ณ ์ค์ ํด๋ฆญ์ ๋ง์ง ์์ผ๋ฏ๋ก, ๋นํ์ฑํ ์ํ์์์ ๋ถํ์ํ ํด๋ฆญ ์ฒ๋ฆฌ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.ํด๋ฆญ ํธ๋ค๋ฌ๋ฅผ ์กฐ๊ฑด๋ถ๋ก ์ค์ ํ๊ฑฐ๋,
enabled์์ฑ์ ํ์ฉํ๋ ๊ฒ์ ๊ณ ๋ คํด๋ณด์ธ์:SolplyBasicButton( text = "์๋ฃ", modifier = Modifier .align(Alignment.BottomCenter) .padding(bottom = 24.dp, start = 20.dp, end = 20.dp), onClick = { - if (isButtonEnabled) { - onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) - } + onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) }, + enabled = isButtonEnabled, selected = isButtonEnabled, textStyle = SolplyTheme.typography.button16M, textColor = if (isButtonEnabled) SolplyTheme.colors.white else SolplyTheme.colors.gray800, enabledBackgroundColor = SolplyTheme.colors.gray900, disabledBackgroundColor = SolplyTheme.colors.gray300 )์ฐธ๊ณ :
SolplyBasicButton์enabledํ๋ผ๋ฏธํฐ ์ง์์ด ํ์ํ ์ ์์ต๋๋ค.
97-97: ๋ฐํ ์ํธ ๋์ด ํ๋์ฝ๋ฉ๋ฐํ ์ํธ์ ๋์ด๊ฐ 600dp๋ก ํ๋์ฝ๋ฉ๋์ด ์์ด ๋ค์ํ ํ๋ฉด ํฌ๊ธฐ์์ ์ ์ ํ๊ฒ ํ์๋์ง ์์ ์ ์์ต๋๋ค. ํนํ ์์ ํ๋ฉด์์๋ ์ฝํ ์ธ ๊ฐ ์๋ฆด ์ ์์ต๋๋ค.
ํ๋ฉด ํฌ๊ธฐ์ ๋ฐ์ํ๋ ๋์ ๋์ด ์ค์ ์ ๊ณ ๋ คํ์ธ์:
-Column(modifier = Modifier.height(600.dp)) { +Column(modifier = Modifier.fillMaxHeight(0.75f)) {๋๋
WindowInsets์LocalConfiguration์ ํ์ฉํ ๋ ์ ๊ตํ ์ฒ๋ฆฌ๋ฅผ ๊ณ ๋ คํด๋ณด์ธ์.
166-180: ๋ค์ ๋ฒํผ ํด๋ฆญ ๊ฐ๋ฅ ์ํ ์ฒ๋ฆฌ๋ค์ ๋ฒํผ๋ ์๋ฃ ๋ฒํผ๊ณผ ๋์ผํ ๋ฌธ์ ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๋นํ์ฑํ ์ํ์์๋ ํด๋ฆญ์ด ๊ฐ๋ฅํ๋ฉฐ, ๋ด๋ถ์์ ์กฐ๊ฑด ๊ฒ์ฌ๋ฅผ ํ๊ณ ์์ต๋๋ค.
์ผ๊ด์ฑ ์๋ ๋ฒํผ ์ํ ๊ด๋ฆฌ๋ฅผ ์ํด ๋ค์๊ณผ ๊ฐ์ด ๊ฐ์ ํ์ธ์:
SolplyBasicButton( text = "๋ค์", modifier = Modifier .padding(bottom = 24.dp), onClick = { - if (isButtonEnabled) { - onNextClick() - } + onNextClick() }, + enabled = isButtonEnabled, selected = isButtonEnabled, textStyle = SolplyTheme.typography.button16M, textColor = if (isButtonEnabled) SolplyTheme.colors.white else SolplyTheme.colors.gray800, enabledBackgroundColor = SolplyTheme.colors.gray900, disabledBackgroundColor = SolplyTheme.colors.gray300 )
70-89: ๋ถํ์ํ LazyColumn ์ฌ์ฉ๋จ์ผ ์์ดํ ๋ง ํ์ํ๋๋ฐ
LazyColumn์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ณผ๋ํ ๊ตฌํ์ ๋๋ค.๋จ์ํ ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ผ๋ก ์ถฉ๋ถํฉ๋๋ค:
-LazyColumn { - item { - if (state.selectedTownId == null) { - AddLocalAreaButton( - text = "", - onClick = { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) }, - selected = false - ) - } else { - AddLocalAreaButton( - text = townList.towns - .flatMap { it.subTowns ?: emptyList() } - .find { it.townId == state.selectedTownId } - ?.townName ?: "", - onClick = {}, - selected = true - ) - } - } -} +if (state.selectedTownId == null) { + AddLocalAreaButton( + text = "", + onClick = { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) }, + selected = false + ) +} else { + AddLocalAreaButton( + text = townList.towns + .flatMap { it.subTowns ?: emptyList() } + .find { it.townId == state.selectedTownId } + ?.townName ?: "", + onClick = { onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) }, + selected = true + ) +}
216-230: ๋ฐํ ์ํธ ํ ๋๋ฆฌ ๊ทธ๋ฆฌ๊ธฐ ๋ก์ง ์ค๋ณต
LeftRegionPane๊ณผRightTownPane์์ ๋์ผํ ํ ๋๋ฆฌ ๊ทธ๋ฆฌ๊ธฐ ๋ก์ง์ด ์ค๋ณต๋์ด ์์ต๋๋ค.๊ณตํต modifier ํ์ฅ ํจ์๋ก ์ถ์ถํ์ฌ ์ฌ์ฌ์ฉ์ฑ์ ๋์ด์ธ์:
private fun Modifier.drawItemBorder( borderColor: Color, index: Int, drawTop: Boolean = true, drawBottom: Boolean = true ): Modifier = this.drawBehind { val strokeWidth = 1.dp.toPx() val w = size.width val h = size.height if (drawTop && index == 0) { drawLine( color = borderColor, start = Offset(0f, strokeWidth / 2), end = Offset(w, strokeWidth / 2), strokeWidth = strokeWidth ) } if (drawBottom) { drawLine( color = borderColor, start = Offset(0f, h - strokeWidth / 2), end = Offset(w, h - strokeWidth / 2), strokeWidth = strokeWidth ) } }๊ทธ ํ ๊ฐ ์ปดํฌ์ ๋ธ์์:
.drawItemBorder(borderColor, index)
48-50: ๋ฐํ ์ํธ ์ํ ์ฌ์์ฑ ๋ฌธ์
rememberModalBottomSheetState๊ฐ recomposition๋ง๋ค ์๋ก ์์ฑ๋ ์ ์๋ ์์น์ ์์ต๋๋ค. ์ด๋ ๋ถํ์ํ ์ฌ์์ฑ์ ์ผ๊ธฐํ ์ ์์ต๋๋ค.
remember๋ธ๋ก ๋ด์์ ์ด๊ธฐํํ๊ฑฐ๋derivedStateOf๋ฅผ ํ์ฉํ์ฌ ์ต์ ํํ์ธ์:-val sheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = true -) +val sheetState = remember { + rememberModalBottomSheetState( + skipPartiallyExpanded = true + ) +}feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/AddCourseBottomSheet.kt (2)
53-60: ๊ธด ์ฝ์ค๋ช ๋ง์ค์ ์ฒ๋ฆฌ ์ ์courseName๊ฐ ๊ธธ๋ฉด ์ฐ์ธก ์์ด์ฝ๊ณผ ๊ฒน์น ์ ์์ต๋๋ค. ํ ์ค ๊ณ ์ + ๋ง์ค์์ ๊ถ์ฅํฉ๋๋ค.
Text( text = courseName, modifier = Modifier.padding(bottom = 8.dp), color = SolplyTheme.colors.black, - style = SolplyTheme.typography.display20Sb + style = SolplyTheme.typography.display20Sb, + maxLines = 1, + overflow = TextOverflow.Ellipsis )์ถ๊ฐ import:
import androidx.compose.ui.text.style.TextOverflow
60-67: ๋ถ๋งํฌ ์์ด์ฝ ์ ๊ทผ์ฑ/ํฐ์น ํ๊ฒ ๊ฐ์
- ์ต์ ํฐ์น ํ๊ฒ(48dp)๊ณผ ์ํ ๊ธฐ๋ฐ contentDescription์ ๊ถ์ฅํฉ๋๋ค.
- ripple์ ๋ ์ํ๋ผ์ ํผ๋๋ฐฑ์ด ์ฝํฉ๋๋ค. ์์/์ค์ผ์ผ ์ ๋๋ฉ์ด์ ๋ฑ ๋์ฒด ํผ๋๋ฐฑ ๊ณ ๋ ค ๋ถํ๋๋ฆฝ๋๋ค.
- Icon( + Icon( painter = painterResource(if (courseSelected) R.drawable.ic_bookmark_fill else R.drawable.ic_bookmark_empty), - contentDescription = "place_bookmarked", - modifier = Modifier.customClickable(rippleEnabled = false) { + contentDescription = if (courseSelected) "๋ถ๋งํฌ๋จ" else "๋ถ๋งํฌ ํด์ ๋จ", + modifier = Modifier + .size(48.dp) + .customClickable(rippleEnabled = false) { saveCourse() }, tint = SolplyTheme.colors.gray900 )feature/maps/src/main/java/com/teamsolply/solply/maps/MapsScreen.kt (3)
484-531: ์๋จ ๋ค๋น๊ฒ์ด์ ์์ด์ฝ ์ต์ ํฐ์น ์์ญ ํ๋ณด์์ด์ฝ ์ปจํ ์ด๋๊ฐ 40dp์ ๋๋ค. ์ ๊ทผ์ฑ ๊ถ์ฅ์น(48dp)์ ๋ง์ถ๋ฉด ์กฐ์์ฑ์ด ์ข์์ง๋๋ค. minimumInteractiveComponentSize() ์ถ๊ฐ๋ฅผ ๊ถ์ฅํฉ๋๋ค.
Box( modifier = Modifier - .size(40.dp) + .size(40.dp) + .minimumInteractiveComponentSize() .shadow(๋ ๊ณณ ๋ชจ๋ ๋์ผ ์ ์ฉ์ ๊ถ์ฅํฉ๋๋ค.
616-629: ๋ถํ์ํ remember ์ ๊ฑฐ ์ ์getMarkerIcon์ ์์ ๋งคํ์ด๋ผ remember๊ฐ ๋ถํ์ํฉ๋๋ค. ๋จ์ ๋ฐํ์ด ๋ ๊ฐ๊ฒฐํฉ๋๋ค.
-@Composable -private fun getMarkerIcon(index: Int, isSelected: Boolean): Int { - return remember(index, isSelected) { - when (index) { +@Composable +private fun getMarkerIcon(index: Int, isSelected: Boolean): Int { + return when (index) { 0 -> if (isSelected) R.drawable.ic_marker_selected_first else R.drawable.ic_marker_first 1 -> if (isSelected) R.drawable.ic_marker_selected_second else R.drawable.ic_marker_second 2 -> if (isSelected) R.drawable.ic_marker_selected_third else R.drawable.ic_marker_third 3 -> if (isSelected) R.drawable.ic_marker_selected_fourth else R.drawable.ic_marker_fourth 4 -> if (isSelected) R.drawable.ic_marker_selected_fifth else R.drawable.ic_marker_fifth 5 -> if (isSelected) R.drawable.ic_marker_selected_sixth else R.drawable.ic_marker_sixth else -> R.drawable.ic_marker_default - } - } + } }
509-513: contentDescription ๋ฌธ์์ด ๋ฆฌ์์คํ"return back", "return home" ํ๋์ฝ๋ฉ ๋ฌธ์์ด์ ๋ฆฌ์์คํ๊ฐ ํ์ํฉ๋๋ค. TalkBack ํ์ง ๋ฐ i18n์ ์ํด strings.xml๋ก ์ด์ ์ ๊ถ์ฅํฉ๋๋ค.
Also applies to: 526-529
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt (3)
165-186: ํค๋์ ๋ถ๋งํฌ ์์ด์ฝ ์ ๊ทผ์ฑ/ํฐ์น ํ๊ฒ ๊ฐ์์ํ ๊ธฐ๋ฐ ์ค๋ช ๊ณผ 48dp ํฐ์น ํ๊ฒ ๊ถ์ฅ.
- Icon( + Icon( painter = painterResource(if (placeBookmarked) R.drawable.ic_bookmark_fill else R.drawable.ic_bookmark_empty), - contentDescription = "place_bookmarked", - modifier = Modifier.customClickable(rippleEnabled = false) { + contentDescription = if (placeBookmarked) "๋ถ๋งํฌ๋จ" else "๋ถ๋งํฌ ํด์ ๋จ", + modifier = Modifier + .size(48.dp) + .customClickable(rippleEnabled = false) { placeBookMarkClick() }, tint = SolplyTheme.colors.gray900 )
203-241: ์ธ๋ถ ์ฑ ํธ์ถ ์์ ์ฑ ๋ณด๊ฐ(๋งํฌ ์ด๊ธฐ, ๊ธธ์ฐพ๊ธฐ ๋ฒํผ)URL Intent ์คํ ์ resolveActivity ์ฒดํฌ ์์ด startActivity๋ฅผ ํธ์ถํ๋ฉด ActivityNotFoundException ์ํ์ด ์์ต๋๋ค. ๊ฐ๋ ์ถ๊ฐ๋ฅผ ๊ถ์ฅํฉ๋๋ค.
- .customClickable(rippleEnabled = false) { + .customClickable(rippleEnabled = false) { navigateToNaverMapDirections( context = context, destName = placeDetailEntity.placeName, destId = placeDetailEntity.placeDefaultId.toString(), destLongitude = placeDetailEntity.longitude, destLatitude = placeDetailEntity.latitude, destType = placeDetailEntity.placeType ) }SNS ๋งํฌ ์ฒ๋ฆฌ๋ถ๋ ๋์ผํ๊ฒ
intent.resolveActivity(context.packageManager) != null์ฒดํฌ ํ ์คํ์ ๊ถ์ฅํฉ๋๋ค.
354-361: ๋ฌธ๊ตฌ/๋ฒํผ ๋ฌธ์์ด ๋ฆฌ์์คํ"์ฝ์ค ์ ๋ณด ์์ ", "๋ด ์ฝ์ค์ ์ถ๊ฐ", "์ด ์ฝ์ค์ ์ถ๊ฐํ ๋์" ๋ฑ ํ๋์ฝ๋ฉ ๋ฌธ์์ด์ strings.xml๋ก ์ด๋์ ๊ถ์ฅํฉ๋๋ค.
Also applies to: 402-415
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/EditCourseBottomSheet.kt (3)
115-121: BottomSheet ์ํ ์ ์ด ํ์ด๋ฐ ๊ฐ์
LaunchedEffect(Unit) { sheetState.expand() }๋ ์ํธ๊ฐ ํ์๋๊ธฐ ์ ํธ์ถ๋์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ์ ๋ฐ๋ผ ์์ธ๊ฐ ๋ ์ ์์ต๋๋ค. ํ์ ์ฌ๋ถ์ ์ฐ๋ํ์ธ์.- LaunchedEffect(Unit) { - sheetState.expand() - } + LaunchedEffect(renameCourseBottomSheetVisibility) { + if (renameCourseBottomSheetVisibility) { + sheetState.expand() + } + }
263-284: ์๋ง ์์ ๋ฒํผ ์ ๊ทผ์ฑ/์ํ ํผ๋๋ฐฑ64dp ํฌ๊ธฐ๋ ์ถฉ๋ถํ๋, TalkBack ์ค๋ช ๊ณผ ๋๋ฆผ ํผ๋๋ฐฑ(์: scale/์ ๋ณํ)์ด ์์ต๋๋ค. contentDescription ๋ฆฌ์์คํ ๋ฐ ๊ฐ๋จํ ๋๋ฆผ ์ ๋๋ฉ์ด์ ์ ๊ณ ๋ คํด ์ฃผ์ธ์.
310-320: ๋ฌธ์์ด ๋ฆฌ์์คํ"์๋ฃ", "์ฝ์ค ์ ๋ณด ์์ " ๋ฑ ํ๋์ฝ๋ฉ ๋ฌธ์์ด์ strings.xml๋ก ์ด๋ํด i18n ๋๋น๋ฅผ ๊ถ์ฅํฉ๋๋ค.
Also applies to: 380-389
๐ Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
๐ Files selected for processing (17)
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/bottomsheet/SolplyBasicBottomSheet.kt(1 hunks)core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/textfield/SolplyTextField.kt(4 hunks)core/designsystem/src/main/res/drawable/ic_course_edit.xml(1 hunks)core/designsystem/src/main/res/drawable/ic_place_navigation.xml(1 hunks)core/ui/src/main/java/com/teamsolply/solply/ui/extension/Modifier.kt(1 hunks)data/maps/src/main/java/com/teamsolply/solply/maps/dto/response/CourseSaveResponseDto.kt(1 hunks)data/maps/src/main/java/com/teamsolply/solply/maps/repository/MapsRepositoryImpl.kt(1 hunks)domain/maps/src/main/java/com/teamsolply/solply/maps/model/CourseSaveResultEntity.kt(1 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/MapsContract.kt(2 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/MapsScreen.kt(11 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/MapsViewModel.kt(6 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/component/CourseItem.kt(3 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/AddCourseBottomSheet.kt(2 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/EditCourseBottomSheet.kt(9 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt(7 hunks)feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/RenameCourseBottomSheet.kt(1 hunks)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt(3 hunks)
๐งฐ Additional context used
๐งฌ Code graph analysis (5)
feature/maps/src/main/java/com/teamsolply/solply/maps/component/CourseItem.kt (2)
core/ui/src/main/java/com/teamsolply/solply/ui/image/AdaptationImage.kt (1)
AdaptationImage(15-69)core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/chip/PlaceTag.kt (1)
PlaceTag(15-54)
feature/maps/src/main/java/com/teamsolply/solply/maps/MapsViewModel.kt (1)
core/ui/src/main/java/com/teamsolply/solply/ui/base/BaseViewModel.kt (1)
reduce(44-46)
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/EditCourseBottomSheet.kt (4)
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt (1)
SolplyBasicButton(63-89)feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/RenameCourseBottomSheet.kt (1)
RenameCourseBottomSheet(10-25)core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/textfield/SolplyTextField.kt (1)
SolplyRenameCourseTextField(290-309)feature/maps/src/main/java/com/teamsolply/solply/maps/component/dialog/CourseSaveDialog.kt (1)
CourseSaveDialog(15-47)
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt (3)
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/chip/PlaceTag.kt (1)
PlaceTag(15-54)feature/maps/src/main/java/com/teamsolply/solply/maps/util/MapBrowserDirectionsUtil.kt (1)
navigateToNaverMapDirections(19-68)core/ui/src/main/java/com/teamsolply/solply/ui/image/AdaptationImage.kt (1)
AdaptationImage(15-69)
feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt (2)
core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/button/SolplyButton.kt (2)
AddLocalAreaButton(91-130)SolplyBasicButton(63-89)feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/component/OnBoardingTownBottomSheet.kt (1)
OnBoardingTownBottomSheet(10-25)
๐ Additional comments (22)
core/ui/src/main/java/com/teamsolply/solply/ui/extension/Modifier.kt (1)
66-66: ํฌ์ปค์ค ๊ฐ์ ํด์ ๋ณ๊ฒฝ์ด ์ ์ ํฉ๋๋ค.
force = trueํ๋ผ๋ฏธํฐ ์ถ๊ฐ๋ก ๋ ํ์คํ ํฌ์ปค์ค ํด์ ๊ฐ ๊ฐ๋ฅํด์ง๋๋ค. ์จ๋ณด๋ฉ UI ๋ฆฌํฉํ ๋ง์์ ๋ชจ๋ฌ ํ๋ก์ฐ๋ ๋ณต์กํ ๋ ์ด์์์์ ํฌ์ปค์ค๊ฐ ํ์คํ ํด์ ๋๋๋ก ํ๋ ๋ณ๊ฒฝ์ฌํญ์ผ๋ก ๋ณด์ ๋๋ค.core/designsystem/src/main/res/drawable/ic_place_navigation.xml (1)
2-5: ์์ด์ฝ ํฌ๊ธฐ 20dp๋ก ์ค์ ๋จ โ ๋์์ธ ๊ฐ์ด๋(24dp)๊ณผ ์ผ์น ์ฌ๋ถ ๋ฐ ์ฌ์ฉ์ฒ ํ์ธ ํ์
- ํ์ผ: core/designsystem/src/main/res/drawable/ic_place_navigation.xml โ android:width/height/viewport = 20(dp).
- ์กฐ์น: ๊ธฐ๋ณธ ์์ด์ฝ์ด 24dp์ธ ๊ฒฝ์ฐ ์ ๋ ฌยท์ค์ผ์ผ๋งยทํฐ์น ํ๊น ์ํฅ์ด ๋ฐ์ํ ์ ์์ผ๋ ์ค์ ์ฌ์ฉ ์ง์ ์์ ๊ฒ์ฆํ๊ณ ํ์ ์ 24dp๋ก ์กฐ์ .
์ ์ฅ์ ์๋ ๊ฒ์์ด ์คํจํด ์ฌ์ฉ์ฒ๋ฅผ ์ฐพ์ง ๋ชปํจ. ์ฌ์ฉ์ฒ ํ์ธ์ฉ ๋ช ๋ น(์):
git grep -n 'ic_place_navigation' || true
rg -nS --hidden --no-ignore '@drawable/ic_place_navigation|R.drawable.ic_place_navigation|painterResource\s*(\s*R.drawable.ic_place_navigation' -C2 || truecore/designsystem/src/main/res/drawable/ic_course_edit.xml (1)
2-5: ํด๊ฒฐ: ic_course_edit(36dp) โ ์ฌ์ฉ์ฒ ํ์ธ๋จ, ๋ ์ด์์ ์ํฅ ์์feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/EditCourseBottomSheet.kt์์ ์ฐธ์กฐ๋จ(๋ผ์ธ 174: Modifier.size(24.dp) ์ ์ฉ, ๋ผ์ธ 279: ์ฌ์ด์ฆ ๋ฏธ๋ช ์). ํ์ฌ ์ฝ๋์ ์์ด์ฝ์ 24dp๋ก ์ฒ๋ฆฌ๋์ด ๋ ์ด์์ ๋ถ์ผ์น ์ฐ๋ ค๋ ํด์๋ฉ๋๋ค โ ํ์ ์ ๋ผ์ธ 279์ ๋ช ์์ Modifier.size(24.dp) ์ถ๊ฐ ๊ถ์ฅ.
data/maps/src/main/java/com/teamsolply/solply/maps/dto/response/CourseSaveResponseDto.kt (1)
8-11: ์ง๋ ฌํ ํ๋๋ช ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋งคํ๋์์ต๋๋ค.
@SerialName์ด๋ ธํ ์ด์ ์ ํตํ JSON ๋งคํ์ด ์ ํํ๋ฉฐ, ์๋ก์ด ํ๋๋ค์ด ์ ์ ํ ์ถ๊ฐ๋์์ต๋๋ค.core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/textfield/SolplyTextField.kt (3)
42-42: ๋๋ค์ ํนํ ๋ก์ง์ ์ํ ๋งค๊ฐ๋ณ์ ์ถ๊ฐ๊ฐ ์ ์ ํฉ๋๋ค.
isNicknameํ๋๊ทธ๋ฅผ ํตํด ์์ด์ฝ ๋ ๋๋ง ๋ก์ง์ ์ ์ดํ๋ ๋ฐฉ์์ด ๋ช ํํฉ๋๋ค.
98-114: ์กฐ๊ฑด๋ถ ์์ด์ฝ ๋ ๋๋ง ๋ก์ง์ด ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํ๋์์ต๋๋ค.๋๋ค์ ์ ๋ ฅ ํ๋์์๋ง ์ ํจ์ฑ ๊ฒ์ฌ ์์ด์ฝ์ ํ์ํ๊ณ , ๊ทธ ์ธ์๋ ๋น Box๋ฅผ ๋ ๋๋งํ๋ ๋ก์ง์ด ์ ์ ํฉ๋๋ค.
290-309: ์ฝ์ค ์ด๋ฆ ๋ณ๊ฒฝ ์ ์ฉ ํ ์คํธํ๋๊ฐ ์ ์ค๊ณ๋์์ต๋๋ค.
SolplyRenameCourseTextField๋ ์ฝ์ค ์ด๋ฆ ๋ณ๊ฒฝ์ ํนํ๋ ์คํ์ผ(ํ์ ํ ๋๋ฆฌ, ์ ํจ์ฑ ๊ฒ์ฌ ์์)์ ๊ฐ์ง๊ณ ์์ด ์ฉ๋๊ฐ ๋ช ํํฉ๋๋ค.core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/bottomsheet/SolplyBasicBottomSheet.kt (2)
27-29: ๋ฐํ ์ํธ ํฌ๊ธฐ ์๊ณ๊ฐ์ด ์กฐ์ ๋์์ต๋๋ค.์๊ณ๊ฐ ๋ณ๊ฒฝ ์ฌ์ ์ ๋ํ ์ค๋ช ์ด ์์ด ์๋๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต์ต๋๋ค. ํนํ
fullyExpanded๊ฐ 0.93์์ 0.85๋ก ์ค์ด๋ ๊ฒ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค.์๊ณ๊ฐ ๋ณ๊ฒฝ์ ๋ฐฐ๊ฒฝ๊ณผ ๊ธฐ๋ ํจ๊ณผ๋ฅผ ํ์ธํด์ฃผ์๊ณ , ํ์ํ๋ค๋ฉด ์ฃผ์์ผ๋ก ๋ณ๊ฒฝ ์ฌ์ ๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์.
16-18: ํธํ์ฑ ์ํ โ menuContent ํ๋ผ๋ฏธํฐ ์ ๊ฑฐ๋ก ์ธํ ์ปดํ์ผ ์๋ฌ ๊ฐ๋ฅ์ฑSolplyBasicBottomSheet์์ menuContent ๋งค๊ฐ๋ณ์๊ฐ ์ ๊ฑฐ๋์์ต๋๋ค. ๊ธฐ์กด ํธ์ถ๋ถ๊ฐ menuContent๋ฅผ ์ ๋ฌํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค.
๊ฒ์ฆ ์คํฌ๋ฆฝํธ ์คํ ์ค "unrecognized file type: kt" ์ค๋ฅ๋ก ์๋ ํ์ธ์ ์คํจํ์ต๋๋ค. ๋ก์ปฌ์์ ์๋ ๋ช ๋ น ์ค ํ๋๋ฅผ ์คํํด ์ฌ์ฉ์ฒ๋ฅผ ํ์ธํ์ธ์:
# ripgrep ์ฌ์ฉ(๊ถ์ฅ) rg -nP -C2 'SolplyBasicBottomSheet\s*\(' | rg -n 'menuContent|content\s*=' # grep ๋์ฒด grep -RIn --include='*.kt' 'menuContent' .์ฌ์ฉ์ฒ๊ฐ ํ์ธ๋๋ฉด ํธ์ถ๋ถ๋ฅผ API ๋ณ๊ฒฝ์ ๋ง๊ฒ ์์ ํ๊ฑฐ๋ ํธํ์ฑ ๋ ์ด์ด๋ฅผ ์ถ๊ฐํ์ธ์.
data/maps/src/main/java/com/teamsolply/solply/maps/repository/MapsRepositoryImpl.kt (1)
80-84: ๋๋ฉ์ธ ์ํฐํฐ ๋งคํ์ด ์๋ก์ด DTO ๊ตฌ์กฐ์ ์ผ์นํฉ๋๋ค.
updatedCourseId์updatedCourseName์ ์ฌ์ฉํ์ฌCourseSaveResultEntity๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ด ์ ์ ํฉ๋๋ค.feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/RenameCourseBottomSheet.kt (1)
12-25: ๊น๋ํ ๋ฐํ ์ํธ ๋ํผ ๊ตฌํ์ ๋๋ค.
ModalBottomSheet์ ๋ํํ์ฌ ์ผ๊ด๋ ์คํ์ผ(ํฐ์ ๋ฐฐ๊ฒฝ, ๋๋๊ทธ ํธ๋ค ์์)์ ์ ๊ณตํ๋ ๊ตฌ์กฐ๊ฐ ์ข์ต๋๋ค.feature/maps/src/main/java/com/teamsolply/solply/maps/MapsContract.kt (2)
32-35: ์ฝ์ค ์ด๋ฆ ๋ณ๊ฒฝ ๊ธฐ๋ฅ์ ์ํ ์ํ ํ๋๋ค์ด ์ ์ ํ ์ถ๊ฐ๋์์ต๋๋ค.
renameCourseBottomSheetVisibility,newCourseName,newCourseIntroductionํ๋๋ค์ด ๋ช ํํ ๋ค์ด๋ฐ์ผ๋ก ์ ์๋์ด ์์ต๋๋ค.
77-79: ์ฝ์ค ์ด๋ฆ ๋ณ๊ฒฝ์ ์ํ ์ธํ ํธ๋ค์ด ์ ์ค๊ณ๋์์ต๋๋ค.๋ฐํ ์ํธ ๊ฐ์์ฑ ํ ๊ธ๊ณผ ์ด๋ฆ/์๊ฐ ์ ๋ฐ์ดํธ๋ฅผ ์ํ ์ธํ ํธ๋ค์ด ๋ช ํํ๊ฒ ๋ถ๋ฆฌ๋์ด ์์ต๋๋ค.
feature/maps/src/main/java/com/teamsolply/solply/maps/MapsViewModel.kt (5)
123-126: ์ฝ๋ ํฌ๋งทํ ๊ฐ์ ์ ๋๋ค.๋ณ์ ์ ์ธ์ด ๋ ๋ช ํํ๊ฒ ๋ถ๋ฆฌ๋์ด ๊ฐ๋ ์ฑ์ด ํฅ์๋์์ต๋๋ค.
187-188: ์๋ก์ด ์ฝ์ค๋ช /์๊ฐ ํ๋ ์ฌ์ฉ์ด ์ผ๊ด์ฑ ์๊ฒ ์ ์ฉ๋์์ต๋๋ค.๊ธฐ์กด ์ฝ์ค ์ ์ฅ๊ณผ ์ ์ฝ์ค ์ ์ฅ ๋ชจ๋์์
newCourseName๊ณผnewCourseIntroduction์ ์ฌ์ฉํ๊ณ ์์ด ์ผ๊ด์ฑ์ด ์ข์ต๋๋ค.Also applies to: 200-201
255-267: ์ฝ์ค ์ด๋ฆ ๋ณ๊ฒฝ ์ธํ ํธ ์ฒ๋ฆฌ๊ฐ ์ ์ ํ ๊ตฌํ๋์์ต๋๋ค.๋ฐํ ์ํธ ๊ฐ์์ฑ ํ ๊ธ๊ณผ ์ด๋ฆ/์๊ฐ ์ ๋ฐ์ดํธ ๋ก์ง์ด ๊ฐ๋จํ๊ณ ๋ช ํํฉ๋๋ค.
351-353: ์ฝ์ค ์์ธ ์ ๋ณด ๋ก๋ฉ ์ ํธ์ง ํ๋ ์ด๊ธฐํ๊ฐ ์ ์ ํฉ๋๋ค.์ฝ์ค ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ฌ ๋
newCourseName๊ณผnewCourseIntroduction์ ์๋์ผ๋ก ์ค์ ํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ์ต๋๋ค.
398-400: ์ฝ์ค ์ ์ฅ ํ ์์ธ ์ ๋ณด ์๋ก๊ณ ์นจ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๊ตฌํ๋์์ต๋๋ค.์ ์ฅ ์ฑ๊ณต ํ
getCourseDetailInfo๋ฅผ ํธ์ถํ์ฌ ์ ๋ฐ์ดํธ๋ ์ ๋ณด๋ฅผ ๋ค์ ๋ถ๋ฌ์ค๋ ๋ก์ง์ด ์ ์ ํฉ๋๋ค.domain/maps/src/main/java/com/teamsolply/solply/maps/model/CourseSaveResultEntity.kt (1)
4-5: ๊ฒ์ฆ ์คํจ โ courseId ์์ฌ ์ฐธ์กฐ ํ์ธ ํ์rg์ --type=kt ์ค๋ฅ๋ก ์๋ ๊ฒ์์ด ์คํจํ์ต๋๋ค. ์๋ ์์ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํด ์ ์ฅ์ ์ ์ญ์์ courseId ์ฐธ์กฐ๊ฐ ๋จ์์๋์ง ํ์ธํ์ธ์.
#!/bin/bash # Description: courseId ํ๋์ ์์ฌ ์ฐธ์กฐ ํ์ธ (CourseSaveResultEntity.kt ์ ์ธ) # ์ฐ์ rg ์๋, ์คํจ ์ grep์ผ๋ก ๋์ฒด rg -nP -C3 '\bcourseId\b' -g '*.kt' -g '!**/CourseSaveResultEntity.kt' || \ grep -RIn --include='*.kt' -C3 '\bcourseId\b' . | grep -v 'CourseSaveResultEntity.kt' || truefeature/maps/src/main/java/com/teamsolply/solply/maps/MapsScreen.kt (2)
538-561: PlaceDetailBottomSheet ์ธ์ ํ์ฅ ๋ฐ์ ์ ํฉplaceBookmarked, placeDetailEntity, placeBookMarkClick ์ ๋ฌ์ด ์ผ๊ด๋๊ณ , imageInfos โ placeImageUrls ๋งคํ๋ ์ฌ๋ฐ๋ฆ ๋๋ค. LGTM.
573-576: AddCourseBottomSheet ๋ถ๋งํฌ ์ํ ์ ๋ฌ ์ ํฉcourseDetailInfo.isBookmarked๋ฅผ courseSelected๋ก ์ ๋ฌํ๋ ์ฐ๊ฒฐ์ด ์์ฐ์ค๋ฝ์ต๋๋ค. LGTM.
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/AddCourseBottomSheet.kt (1)
41-44: ์๊ทธ๋์ฒ ๋ณ๊ฒฝ ๋ฐ์ ํ์ธ ์๋ฃ
๋ ํฌ์งํ ๋ฆฌ ์ ์ ๊ฒ์ ๊ฒฐ๊ณผ AddCourseBottomSheet ํธ์ถ์ feature/maps/src/main/java/com/teamsolply/solply/maps/MapsScreen.kt ํ ๊ณณ(๋ผ์ธ 565)์๋ง ์์ผ๋ฉฐ saveCourse์ courseSelected ์ธ์๊ฐ ๋ชจ๋ ์ ๋ฌ๋๊ณ ์์ต๋๋ค.
| import com.teamsolply.solply.designsystem.R | ||
| import com.teamsolply.solply.designsystem.component.button.SolplyBasicButton | ||
| import com.teamsolply.solply.designsystem.component.textfield.SolplyRenameCourseTextField | ||
| import com.teamsolply.solply.designsystem.theme.SolplyTheme | ||
| import com.teamsolply.solply.maps.component.CourseItem | ||
| import com.teamsolply.solply.maps.component.dialog.CourseSaveDialog | ||
| import com.teamsolply.solply.maps.courseDetailEntity | ||
| import com.teamsolply.solply.maps.model.CourseSaveType | ||
| import com.teamsolply.solply.maps.model.Place |
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.
์ ์ญ courseDetailEntity ์ฐธ์กฐ๋ก ์ธํ ๋ฒ๊ทธ ๊ฐ๋ฅ์ฑ(๋ฆฌ์คํธ ํฌ๊ธฐ ๋๊ธฐํ ๋ถ์ผ์น)
์ด ์ปดํฌ์ ๋ธ์ places๋ฅผ ์ธ์๋ก ๋ฐ๋๋ฐ, draggableItemsNum ๊ณ์ฐ์ ์ ์ญ com.teamsolply.solply.maps.courseDetailEntity์ ์์กดํฉ๋๋ค. ๋ฐ์ดํฐ ๋ถ์ผ์น/๋ฏธ๋ฐ์ ์ํ์ด ํฝ๋๋ค. ์ธ์ places ๊ธฐ์ค์ผ๋ก ๊ณ์ฐํ์ธ์. ๋ํ ์ ์ญ import๋ฅผ ์ ๊ฑฐํ์ธ์.
-import com.teamsolply.solply.maps.courseDetailEntity
+// import ์ ๊ฑฐ
- val draggableItemSize by remember(courseDetailEntity.places.size) {
- derivedStateOf { courseDetailEntity.places.size }
- }
+ val draggableItemSize by remember(places.size) {
+ derivedStateOf { places.size }
+ }Also applies to: 96-99
๐ค Prompt for AI Agents
In
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/EditCourseBottomSheet.kt
around lines 48-56 (and also apply same change at lines 96-99), the composable
currently uses the global courseDetailEntity to compute draggableItemsNum which
can cause list size/synchronization bugs; remove the global import and any
references to com.teamsolply.solply.maps.courseDetailEntity, compute
draggableItemsNum from the local places parameter (e.g., places.size or the
appropriate filtered/visible count) and pass that local value into any
downstream logic or UI that needs it, ensuring all usages in the file use the
places-derived value instead of the global.
| @@ -1,9 +1,9 @@ | |||
| package com.teamsolply.solply.maps.component.bottomsheet | |||
|
|
|||
| import ClickableAnnotatedText | |||
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.
๐งฉ Analysis chain
์๋ชป๋ import๋ก ์ธํ ์ปดํ์ผ ์ค๋ฅ ๊ฐ๋ฅ์ฑ: ClickableAnnotatedText ๊ฒฝ๋ก ํ์ธ ํ์
import ClickableAnnotatedText๋ FQCN์ด ์์ด ์ปดํ์ผ ์๋ฌ ๊ฐ๋ฅ์ฑ์ด ํฝ๋๋ค. ์ค์ ์ ์ธ ํจํค์ง๋ฅผ ์ฐพ์ ์ ํํ import ํด์ฃผ์ธ์(๋์ผ ํจํค์ง๋ผ๋ฉด import ์ ๊ฑฐ).
๋ค์ ์คํฌ๋ฆฝํธ๋ก ์ ์ธ ์์น๋ฅผ ์ฐพ๊ณ ์ฌ๋ฐ๋ฅธ ํจํค์ง๋ฅผ ํ์ธํ์ธ์.
์์ ์์(ํจํค์ง๋ ์ค์ ๊ฒฐ๊ณผ์ ๋ง๊ฒ ๋ณ๊ฒฝ ํ์):
-import ClickableAnnotatedText
+import com.teamsolply.solply.ui.text.ClickableAnnotatedText๐ Script executed:
#!/bin/bash
# ClickableAnnotatedText ์ ์ธ ์์น์ ํจํค์ง ํ์ธ
rg -nP '^\s*package\s+.*$' -n -C0 $(fd -t f -e kt) >/dev/null 2>&1
rg -nP -C2 '\bfun\s+ClickableAnnotatedText\s*\('Length of output: 435
์ปดํ์ผ ์ค๋ฅ: ClickableAnnotatedText import๋ฅผ FQCN์ผ๋ก ๊ต์ฒดํ์ธ์
ClickableAnnotatedText๋ core/ui์ ํจํค์ง com.teamsolply.solply.ui.text์ ์ ์ธ๋์ด ์์ต๋๋ค (core/ui/src/main/java/com/teamsolply/solply/ui/text/TextUtil.kt:11). ์๋์ฒ๋ผ feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt์ import๋ฅผ ์์ ํ์ธ์.
-import ClickableAnnotatedText
+import com.teamsolply.solply.ui.text.ClickableAnnotatedText๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import ClickableAnnotatedText | |
| import com.teamsolply.solply.ui.text.ClickableAnnotatedText |
๐ค Prompt for AI Agents
In
feature/maps/src/main/java/com/teamsolply/solply/maps/component/bottomsheet/PlaceDetailBottomSheet.kt
around line 3, the import "ClickableAnnotatedText" is unresolved; replace it
with the fully-qualified class name
com.teamsolply.solply.ui.text.ClickableAnnotatedText in the import section
(remove the bare import and add the FQCN import) so the compiler can find the
symbol declared in
core/ui/src/main/java/com/teamsolply/solply/ui/text/TextUtil.kt:11.
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .padding(), | ||
| horizontalArrangement = Arrangement.SpaceEvenly, |
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.
์ปดํ์ผ๋ฌ ์ค๋ฅ: .padding() ์ธ์ ๋๋ฝ
Modifier.padding()์๋ ์ธ์๊ฐ ํ์ํฉ๋๋ค. ํ ์ํ๋ก๋ ์ปดํ์ผ๋์ง ์์ต๋๋ค. ๋ถํ์ํ๋ฉด ์ ๊ฑฐํ๊ฑฐ๋, ํ์ํ ์ฌ๋ฐฑ์ ๋ช
์ํ์ธ์.
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(),
+ Row(
+ modifier = Modifier
+ .fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,๋์: .padding(horizontal = 8.dp) ๋ฑ์ผ๋ก ๊ตฌ์ฒด๊ฐ์ ์ง์ .
๐ Committable suggestion
โผ๏ธ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| modifier = Modifier | |
| .fillMaxWidth() | |
| .padding(), | |
| horizontalArrangement = Arrangement.SpaceEvenly, | |
| Row( | |
| modifier = Modifier | |
| .fillMaxWidth(), | |
| horizontalArrangement = Arrangement.SpaceEvenly, |
๐ค Prompt for AI Agents
In feature/maps/src/main/java/com/teamsolply/solply/maps/component/CourseItem.kt
around lines 141-144, the call to Modifier.padding() is missing required
arguments and causes a compile error; either remove the .padding() call if no
spacing is needed, or replace it with a concrete padding specification (for
example specify horizontal/vertical or all sides such as 8.dp), and if you add
dp values ensure androidx.compose.ui.unit.dp is imported so the code compiles.
๐ PR ์์ฝ
๐ฑ ์์ ํ ๋ด์ฉ
๐ฑ PR ํฌ์ธํธ
๐ธ ์คํฌ๋ฆฐ์ท
Screen_recording_20250917_141721.mp4
๐ฎ ๊ด๋ จ ์ด์
Summary by CodeRabbit
New Features
Style