Skip to content
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

#3 - 기본 화면 전환 구현 #4

Merged
merged 7 commits into from
Dec 14, 2024
Merged

Conversation

sksowk156
Copy link
Contributor

@sksowk156 sksowk156 commented Dec 11, 2024

1. ⭐️ 변경된 내용

  • Feature 모듈 생성
  • NavController로 화면 전환 구현

2. 🖼️ 스크린샷(선택)

이미지 설명 이미지 설명 이미지 설명 이미지 설명

3. 💡 알게된 부분

4. 📌 이 부분은 꼭 봐주세요!

  • Navigation에 Type Safety를 적용하다 보니 feature 모듈들에 를 적용하기 위해서
    ' implementation(libs.kotlinx.serialization.json) '를 추가했는데, plugin에 추가할까 고민하다가 feature에 넣는게 좋을지 library에 kotlinAndroid에 넣는게 좋을지 못 정해서 일단 모든 모듈에 추가해뒀어요. 태규님이 보시고 적절하다 싶으신 곳에 넣어주시면 좋을 것 같아요 (안 넣으셔도 되고)
  • 구조는 authNavGraph 와 HomeRoute가 appState로 묶여있고, HomeRoute 안에 Matching, Etc, MyPage가 HomeState로 묶여있습니다.

@sksowk156 sksowk156 added 리뷰 원해요🔥 피어의 리뷰를 기다리는 ing.. 🔥 ㅈㅎ찐혁 🌙 훗날 세상을 아우를 남자, sksowk156 labels Dec 11, 2024
@sksowk156 sksowk156 requested a review from tgyuuAn December 11, 2024 12:37
@sksowk156 sksowk156 self-assigned this Dec 11, 2024
Copy link
Member

@tgyuuAn tgyuuAn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

진혁님 고생하셨어용

일단 chaeum.xxx 으로 패키지명 하시고,

다음 PR에서 제가 이름 Piece로 바꿔놓겠습니다!!

크게 변경될 점은 없고 약간 수정되어야 할 점이랑 짧은 코멘트 몇 개 남겼어요 ! 체크 부탁드려요.




++

지라 티켓 및 이슈는 PR이 머지되고 닫아주세요!!!!

Comment on lines 4 to 5
alias(libs.plugins.androidx.navigation.safeargs)
alias(libs.plugins.kotlin.serialization)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

safeargs는 생각해보니 이전 프로젝트에서 xml + compose로 작업하다보니 네비게이션이 xml기반이어서 넣었던 것이었어요...!

safeargs 빼고 kotlinserilization만 쓰셔도 될 것 같아요!

죄송합니다 ㅎㅎ,,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아니에요! 요 며칠 circuit 좀 찾아봤는데, circuit은 자체적으로 navigation을 지원해준다고 하더라구요. 그래서 이 부분에 대해서 한번 이야기 해보면 좋을 것 같아요

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 맞아요,,, Circuit을 사용하면 자체적인 ViewModel과 Navigation을 사용하더라고요 ,,,

실제로 circuit을 택할건지에 대한 논의를 추가적으로 해보면 좋을 것 같아요.

Comment on lines 12 to 26
@Composable
fun HomeNavHost(
navController: NavHostController,
modifier: Modifier = Modifier,
) {
NavHost(
navController = navController,
startDestination = MatchingGraph,
modifier = modifier,
) {
matchingNavGraph()
myPageScreen()
etcScreen()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 AppNavHost랑 HomeNavHost차이점이 무엇인지 알 수 있을까요 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 AppNavHost는 Auth랑 Home 화면 전환을 담당하고, Home은 Matching, Mypage, Etc 화면 전환을 담당하게 만들었어요.
처음엔 Auth, Matching, Mypage, Etc 4개로 구성했는데, bottom navigation이 없는 Auth를 포함해서 4개를 하나의 NavHost에서 bottom navigation으로 구현하니까 화면이 전환되기 전에 항상 화면이 Auth인지 확인하는 로직을 추가해야 하더라구요. 그래서 몇몇 샘플 코드들을 보고 Host를 분리하는게 더 좋을 것 같아서 분리하는 방식으로 만들었어요!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러네용 이 때 까지 저는 단일 NavHost로

private fun handleBottomBarState(
    currentRoute: String?,
    setBottomBarState: (Boolean) -> Unit,
): Unit = when (currentRoute) {
    null -> setBottomBarState(false)
    signInNavigationRoute -> setBottomBarState(false)
    signUpNavigationRoute -> setBottomBarState(false)
    splashNavigationRoute -> setBottomBarState(false)
    profileSettingNavigationRoute -> setBottomBarState(false)
    attendanceManagementNavigationRoute -> setBottomBarState(false)
    ManagementSurveyRoute.surveyFormRegistrationRoute -> setBottomBarState(false)
    ManagementSurveyRoute.surveyFormEditRoute("{id}") -> setBottomBarState(false)
    eventRegistrationNavigationRoute -> setBottomBarState(false)
    eventEditNavigationRoute -> setBottomBarState(false)
    SurveyRoute.answerRoute("{id}") -> setBottomBarState(false)
    surveyCheckRoute -> setBottomBarState(false)
    SurveyCheckRoute.surveyDetailRoute("{id}", "{backStack}") -> setBottomBarState(false)
    else -> setBottomBarState(true)
}

와 같은 방식으로 Navigation의 가시성을 조절했었는데 스택 오버플로우 찾아보니 2개로도 구현할 수 있겠군요..

당장은 뭐가 더 좋은 방식인지는 잘 모르겠습니다 ㅎㅎ


단일 네비게이션으로 두고 전환할 때 마다 체크하는 대신 가독성 챙기기 vs

유지보수 측면에서 화면이 늘어날 수록 코드를 수정 혹은 작성해야하는 부분이 적은 대신 2개의 NavHost 사용하기


정도가 되겠네요. 이렇게 가도 좋을 것 같아요..!

Comment on lines 9 to 29
@Composable
fun rememberAppState(
navController: NavHostController = rememberNavController(),
): AppState {
return remember(navController) {
AppState(navController = navController)
}
}

class AppState(
val navController: NavHostController,
) {
fun loginSuccess() {
navController.navigate(HomeGraph) {
popUpTo(navController.graph.findStartDestination().id) {
inclusive = true
}
launchSingleTop = true
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppState를 한 번도 이런식으로 써본 적이 없어서 요 녀석의 역할이 궁금해용

Copy link
Contributor Author

@sksowk156 sksowk156 Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nowinAndroid에서 참고해서 만든 건데 제가 분석한 AppState는 AppNavHost에서 관리하는 화면들에 전역적으로 쓰일 수 있는 상태값들을 저장해서 쓰는 것 같아요!
저희 회사에서는 주로 인터넷 연결 상태 확인, 로그인 상태 확인(중복 로그인시 강제 로그아웃을 위해), 언어 설정 확인, 화면 전환 로직 등을 AppState에서 관리해서 쓰고 있어요!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

한번도 써보지 않았지만 써보면 재밌을 것 같아요....!

Comment on lines 3 to 4
alias(libs.plugins.androidx.navigation.safeargs)
alias(libs.plugins.kotlin.serialization)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

safeargs는 지우셔도 될 것 같고,

아마 모든 feature모듈에 kotlin.serilization 플러그인이 들어가고

json 종속성이 임포트 될 것 같은데,

이 부분 chaem.andorid.feature 플러그인에서 처리해주면 보일러플레이트 코드가 더 줄어들 것 같아요.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 좋아요~ 그럼 제가 feature 모듈에 추가해둘까요??

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 좋아요~ 그럼 제가 feature 모듈에 추가해둘까요??

넵 좋습니다~

}

android {
namespace = "com.example.auth"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

패키지 명이 다른 것 같아요!

여기 gradle 말고 나머지 부분도 마찬가지로요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵넵 수정해둘게요!

Comment on lines +16 to +21
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = "AuthRoute",
fontSize = 30.sp,
modifier = Modifier.clickable { onLoginSuccess() })
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런 UI적인 요소는 Route보다 Screen에 들어가는 것이 맞다고 생각되는데,

그냥 네비게이션 연결용으로 임시로 Route에 ui요소를 둔 것일까요 ?!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵넵 맞아요. 그냥 화면을 표시할려고 하다보니 그렇게 작성했었는데, 실제 개발하실 때는 저 부분 지우고 screen에 정의해서 쓰시면 될 것 같아요!!

Comment on lines +11 to +17
@Serializable
data object AuthGraph

sealed class AuthGraphDest {
@Serializable
data object AuthRoute : AuthGraphDest()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Serializable
data object AuthGraph

이 외부로 공개되는 그래프이고,

sealed class AuthGraphDest {
    @Serializable
    data object AuthRoute : AuthGraphDest()
}

이 AuthGraph내부에서 사용되는 목적지 인가요 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵넵! AuthGraphDest 는 Auth 쪽 화면 전환할 때 쓰기 위해서 만들어 두었어요!

local.properties Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 파일을 Gitignore에 추가해야하겠군요 ,,

Comment on lines 12 to 40
enum class TopLevelDestination(
val selectedIcon: ImageVector,
val unselectedIcon: ImageVector,
val iconText: String,
val titleText: String,
val route: KClass<*>,
) {
MATCHING(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "매칭",
titleText = "매칭",
route = MatchingGraphDest.MatchingRoute::class,
),
MYPAGE(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "마이페이지",
titleText = "마이페이지",
route = MyPageRoute::class,
),
ETC(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "ETC",
titleText = "ETC",
route = EtcRoute::class,
),
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum class로도 잘 동작하지만,

새롭게 업데이트 된 Docs를 따라서 만들어보는 것은 어떨까요?

data class로 정의하고 List로 묶어둔 것 같은데,

진혁님 생각이 궁금해요...!

Copy link
Contributor Author

@sksowk156 sksowk156 Dec 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 이 부분 NowInAndroid 코드를 참고해서 작성한거긴 한데 (https://github.com/android/nowinandroid/blob/main/app/src/main/kotlin/com/google/samples/apps/nowinandroid/navigation/TopLevelDestination.kt)

class HomeState(
    val navController: NavHostController,
) {
    fun navigateToTopLevelDestination(topLevelDestination: TopLevelDestination) {
        val topLevelNavOptions = navOptions {
            popUpTo(navController.graph.findStartDestination().id) {
                saveState = true
            }
            launchSingleTop = true
            restoreState = true
        }

        when (topLevelDestination) {
            TopLevelDestination.MATCHING -> navController.navigateToMatching(topLevelNavOptions)
            TopLevelDestination.MYPAGE -> navController.navigateToMyPage(topLevelNavOptions)
            TopLevelDestination.ETC -> navController.navigateToEtc(topLevelNavOptions)
        }
    }
}

제가 생각한 nowinandorid에서 enum class를 사용한 이유는 화면 전환 navigate 메서드를 정의할 때 enum의 exhaustive 특성을 활용해서 타입 안전성과 가독성을 높이기 위해 쓴 것 같습니다.
list도 동적으로 화면을 추가하거나 뺄 수 있어서 유연성이 좋다고 생각하지만, navigate 메서드를 정의할 때 컴파일 시점에서 확실히 정의되지 않기 때문에 로직을 작성할 때 불편함이 있을 거라고 생각합니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 항상 Enum으로 작성해왔는데 docs에 새롭게 업데이트 되어서 건의해본거였답니다 ㅎㅎㅎㅎㅎ

Enum이 가독성이 더 좋은 것 같긴해요 ..!

Copy link
Member

@tgyuuAn tgyuuAn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가 코멘트 남겼습니다!

작업하신 모든 모듈의 패키지 명만 수정해주세용~~~

Comment on lines 4 to 5
alias(libs.plugins.androidx.navigation.safeargs)
alias(libs.plugins.kotlin.serialization)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 맞아요,,, Circuit을 사용하면 자체적인 ViewModel과 Navigation을 사용하더라고요 ,,,

실제로 circuit을 택할건지에 대한 논의를 추가적으로 해보면 좋을 것 같아요.

Comment on lines 12 to 26
@Composable
fun HomeNavHost(
navController: NavHostController,
modifier: Modifier = Modifier,
) {
NavHost(
navController = navController,
startDestination = MatchingGraph,
modifier = modifier,
) {
matchingNavGraph()
myPageScreen()
etcScreen()
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그러네용 이 때 까지 저는 단일 NavHost로

private fun handleBottomBarState(
    currentRoute: String?,
    setBottomBarState: (Boolean) -> Unit,
): Unit = when (currentRoute) {
    null -> setBottomBarState(false)
    signInNavigationRoute -> setBottomBarState(false)
    signUpNavigationRoute -> setBottomBarState(false)
    splashNavigationRoute -> setBottomBarState(false)
    profileSettingNavigationRoute -> setBottomBarState(false)
    attendanceManagementNavigationRoute -> setBottomBarState(false)
    ManagementSurveyRoute.surveyFormRegistrationRoute -> setBottomBarState(false)
    ManagementSurveyRoute.surveyFormEditRoute("{id}") -> setBottomBarState(false)
    eventRegistrationNavigationRoute -> setBottomBarState(false)
    eventEditNavigationRoute -> setBottomBarState(false)
    SurveyRoute.answerRoute("{id}") -> setBottomBarState(false)
    surveyCheckRoute -> setBottomBarState(false)
    SurveyCheckRoute.surveyDetailRoute("{id}", "{backStack}") -> setBottomBarState(false)
    else -> setBottomBarState(true)
}

와 같은 방식으로 Navigation의 가시성을 조절했었는데 스택 오버플로우 찾아보니 2개로도 구현할 수 있겠군요..

당장은 뭐가 더 좋은 방식인지는 잘 모르겠습니다 ㅎㅎ


단일 네비게이션으로 두고 전환할 때 마다 체크하는 대신 가독성 챙기기 vs

유지보수 측면에서 화면이 늘어날 수록 코드를 수정 혹은 작성해야하는 부분이 적은 대신 2개의 NavHost 사용하기


정도가 되겠네요. 이렇게 가도 좋을 것 같아요..!

Comment on lines 12 to 40
enum class TopLevelDestination(
val selectedIcon: ImageVector,
val unselectedIcon: ImageVector,
val iconText: String,
val titleText: String,
val route: KClass<*>,
) {
MATCHING(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "매칭",
titleText = "매칭",
route = MatchingGraphDest.MatchingRoute::class,
),
MYPAGE(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "마이페이지",
titleText = "마이페이지",
route = MyPageRoute::class,
),
ETC(
selectedIcon = Icons.Filled.Call,
unselectedIcon = Icons.Outlined.Call,
iconText = "ETC",
titleText = "ETC",
route = EtcRoute::class,
),
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 항상 Enum으로 작성해왔는데 docs에 새롭게 업데이트 되어서 건의해본거였답니다 ㅎㅎㅎㅎㅎ

Enum이 가독성이 더 좋은 것 같긴해요 ..!

@tgyuuAn tgyuuAn added 머지 해도될듯염🌟 현재 코드를 기존 코드에 합쳐도 될 것 같다라고 판단..! 🌟 and removed 리뷰 원해요🔥 피어의 리뷰를 기다리는 ing.. 🔥 labels Dec 12, 2024
@sksowk156 sksowk156 merged commit 999720b into develop Dec 14, 2024
1 check passed
@sksowk156 sksowk156 deleted the feature/sksowk156/#3 branch December 14, 2024 05:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ㅈㅎ찐혁 🌙 훗날 세상을 아우를 남자, sksowk156 머지 해도될듯염🌟 현재 코드를 기존 코드에 합쳐도 될 것 같다라고 판단..! 🌟
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE]: 화면 전환 구현
2 participants