Android Navigation 쓸까말까

1984tr
nbt-tech
Published in
8 min readNov 25, 2020

최근 Android DevelopersMAD Skills(Modern Android Development)이라는 섹션이 추가 되었습니다. “미친 기술”이라니!!! 왠지 개발 효율을 확 끌어올려 줄 수 있는 무언가가 있을 것만 같아 저도 모르게 클릭을 하게 되었습니다. 대략 읽어보니 Kotlin, Jetpack 등을 사용하여 안드로이드 개발을 더 쉽고 빠르게 할 수 있는 최신 기술들을 소개해주고 있는 것 같았고 그 중 가장 첫번째 시리즈가 바로 Navigation이었습니다.

Navigation Component는 꽤 오래전에 추가가 되었습니다. 2018년 쯤에 공개가 되었고 Android Studio 3.6부터 Template 프로젝트에도 추가 되었습니다. 그 동안 Fragment 전환을 위해 해야 했던 수 많은 반복 작업들.. 코드로 UI를 직접 핸들링하고 애니메이션 처리도 해야하고 Stack 관리도 직접 했던 수고스러움을 모두 해결해준다고 하니 빨리 우리 서비스에 적용해보고 싶었습니다. 하지만 결론부터 말씀드리면 저희 서비스에 Navigation은 아직 적용이 어렵다고 판단하였습니다. 그 동안 가이드 문서만 읽고 지나갔었던 Navigation Component를 실제로 연습해보면서 느꼈던 점들을 몇가지 적어보겠습니다.

이 글에서는 Navigation Component를 구현하는 방법에 대해 설명하고 있지 않습니다. 공식 문서에 너무 설명이 잘 되어 있어 추가로 설명할 것이 없네요. 공식 문서 또는 샘플 코드를 참고하세요😀

문서에도 나와 있듯이 Navigation은 하나의 Activity 안에서 여러 Fragment간의 전환에 중점을 두고 설계가 되었습니다. Activity 기반으로 설계된 저희 서비스 전체 Flow를 하나의 Navigation graph에 표현하는 것은 어려운 일이었습니다.
그렇다면 각 Activity마다 NavHostFragment를 두고 Navigation Graph를 만들어서 관리하면 되지 않을까? 라는 생각이 드실거라 생각됩니다. 하지만 로그인 시나리오를 제외하고 각각의 기능에서 2depth, 3depth 이상 발생되는 시나리오가 없어 억지로 Navigation을 적용할 필요는 없다고 생각되었습니다.
하지만 서비스에 적용을 하기 위해 연습을 하면서 느꼈던 장점들은 분명히 존재하였습니다.

Good

Navigation에서 가장 눈에 띄는 바로 그것! Navigation Editor는 실제로 Fragment간의 연관 관계를 한 눈에 알아 볼수 있게 시각화해주어 아주 편리하다고 느꼈습니다. (아직 버그가 조금 있는 것 같긴 합니다만😅)
서비스의 시나리오를 쉽게 파악 할 수 있고 개발자간 커뮤니케이션에도 많은 도움이 될 것 같습니다.

Sample code의 Navigation Graph

시나리오에 필요한 Fragment와 Action을 추가해주고 코드에서 NavController로 navigate만 호출해주면 별도의 스택관리 없이도 전환이 잘 이루어졌습니다. Deeplink를 통해 2depth 이상의 Fragment에 바로 접근 후 Navigate up 또는 Back pressed 동작을 하여도 back stack 시나리오가 그대로 동작하여 이부분도 굉장히 편리하였습니다.

다음으로 Safe Args가 제공된다는 점입니다. Fragment에서 사용 할 Arguments를 미리 정의해 두면 굳이 코드를 찾아볼 필요도 없고 별도의 타입캐스팅 없이 편리하게 사용할 수 있는 점이 좋았습니다.

Navigation Editor에 Argument를 입력하고 build를 하면 NavArgs를 상속받는 XXXArgs 가 generate 되게 됩니다. 여기에서 parameter에 직접 접근하여 사용할 수 있어 불필요한 코드가 사라지게 됩니다.

data class ConfirmDialogFragmentArgs(
val message: String
) : NavArgs {
fun toBundle(): Bundle {
val result = Bundle()
result.putString("message", this.message)
return result
}
...
}

마지막으로 Deeplink 기능을 제공한다는 것입니다. 이 또한 Editor에서 직접 추가가 가능하기 때문에 Fragment별로 Deeplink를 설정할 수 있고 Deeplink Uri의 path, query를 이용하여 arguments 또한 쉽게 전달 할 수 있습니다.

하지만 장점 만큼이나 아쉬운 점도 많이 있었습니다.

Bad

우선 Activity간 전환 또는 Navigation Graph간의 전환이 매끄럽지 못하다는 것입니다. 설계 자체가 하나의 Activity에서 여러 Fragment들을 전환 하는 것에 초점이 맞춰져 있기 때문에 어쩔 수 없다고 생각하지만 Navigation Flow에서 다음 Activity로 넘어 갈 때는 여전히 코드로 핸들링을 하여야 한다는 점이 아쉬웠습니다.

Graph간 argument 전달이 어려운 것도 아쉬웠습니다. 하나의 Graph에 다른 Graph를 include 하였을 때 argument 전달이 불가능하였습니다. Nested 형태로 사용하게 되도 동일하지만 실제 XML 코드에 argument를 추가하면 전달이 되는 것은 확인 하였습니다.

argument 메뉴에 추가 버튼이 사라졌어요😢

Fragment간 Data를 전달 받는 방법은 아래와 같이 LiveData를 이용하는 방법으로 가이드가 되어 있었는데 boilerplate 코드도 많고 좀 더 효율적인 방법이 없을까 하는 아쉬움이 있었습니다. (혹시 다른 방법이 있다면 알려주세요!)

findNavController().previousBackStackEntry?.savedStateHandle?.set("confirm", false)>>>>override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val savedStateHandle = findNavController().currentBackStackEntry?.savedStateHandle ?: return
savedStateHandle.getLiveData<Boolean>("confirm").observe(viewLifecycleOwner) {
binding.message.text = "confirm: $it"
}
}

Safe Args는 명확하게 Argument 정보를 알 수 있어 좋지만 반대로 Deeplink 등 을 통해 argument 없이 Fragment에 접근하게 되면 Crash가 발생하게 됩니다.

샘플 코드를 작성하면서 느꼈던 것은 네비게이션이나 지도, 카메라 등의 Single Activity를 사용하는 앱에서는 굉장히 유용하겠다는 느낌을 많이 받았습니다. args 전달, deeplink, animation 설정 등의 다양한 기능도 좋지만 무엇보다 stack 관리를 해준다는 것이 굉장히 매력적인 점이라고 생각이 되었습니다.
꼭 Single Activity가 아니더라도 지금 서비스하고 있는 앱의 시나리오를 다시 한번 정리해보면서 기능에 맞게 Activity/Fragment로 리팩토링 하고 Navigation Component를 적용해보는 것도 좋을 것이라 생각됩니다.

Reference

https://developer.android.com/guide/navigation
https://medium.com/androiddevelopers/tagged/mad-skills
https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample

--

--