Jetpack Navigation
은Android Jetpack
의 일부로,앱 내의 화면 간 이동을 간소화하고 표준화
하는 데 사용되는 컴포넌트입니다.사용자가 앱 내에서 화면 간 이동을 할 때의 과정을 효율적으로 관리해주며, 더 나은 사용자 경험과 개발자 경험이 가능하도록 합니다.
Jetpack Navigation 공식 문서
코드의 간결성과 유지보수성
Jetpack Navigation은 화면 간의 이동 로직을 직관적인 방식
으로 정의할 수 있게 해준다. 이로 인해 코드가 간결해지고 유지보수가 쉬워진다.
안전한 인수 전달
Safe Args
플러그인을 사용하면 프래그먼트 간에 데이터를 안전하게 전달할 수 있으며, 컴파일 시점에 오류를 찾아낼 수 있어서 런타임 오류를 방지
할 수 있다.
Jetpack Navigation
이 나오기 전에는 FragmentManager
와 FragmentTransaction
을 사용하여 프래그먼트 간 이동을 다음과 같이 구현했다.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 첫 번째 프래그먼트를 컨테이너에 추가
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, FirstFragment())
.commit()
}
}
}
class FirstFragment : Fragment() {
fun navigateToSecondFragment() {
val secondFragment = SecondFragment()
// 데이터를 전달하려면 Bundle 객체를 사용
val bundle = Bundle()
bundle.putString("key", "value")
secondFragment.arguments = bundle
// FragmentTransaction을 사용하여 SecondFragment로 이동
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, secondFragment)
.addToBackStack(null)
.commit()
}
}
class SecondFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// FirstFragment에서 전달한 데이터를 받음
val value = arguments?.getString("key")
}
}
하지만, 위 코드처럼 FragmentManager로 프래그먼트 간 이동을 구현면 백스택 관리
, 타입 안전성
등 개발자가 신경써야 할 부분이 많다.
하지만, 이러한 부분들을 Jetpack Naivgation을 사용하면 해결할 수 있다.
백스택 관리
같은 경우는 Jetpack Navigation이 개발자를 대신에 백스택을 관리해준다.
Jetpack Navigation 라이브러리를 사용하는 경우 FragmentManager와의 직접적인 상호작용은 거의 필요하지 않습니다. 개발자를 대신해 이 라이브러리가 FragmentManager를 사용하기 때문입니다.
FragmentManager 공식 문서
타입 안전성
같은 경우는 Safe Args
를 통해 컴파일 과정에서 오류를 찾아내 런타임에 발생할 수 있는 타입 오류를 예방할 수 있다. 아래에서 확인할 것 이다.
NavHostFragment는 실제로 프래그먼트
들이 교체되는 컨테이너
역할을 한다.
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:navGraph="@navigation/nav_graph"
android:layout_width="match_parent"
android:layout_height="match_parent" />
전체 앱의 네비게이션 흐름을 정의하는 XML 파일로서, 각 화면과 이 화면들 간의 이동 경로(action)
를 선언한다.
<navigation xmlns:android="http://schemas.android.com/apk/res/android">
<fragment android:id="@+id/firstFragment" android:name="com.example.FirstFragment">
<action android:id="@+id/action_to_secondFragment"
app:destination="@id/secondFragment"
app:enterAnim="@android:anim/fade_in"
app:exitAnim="@android:anim/fade_out" />
</fragment>
<fragment android:id="@+id/secondFragment" android:name="com.example.SecondFragment" />
</navigation>
<navigation>:
탐색 그래프의 루트, 탐색 그래프는 앱의 화면 간 이동 경로를 정의
<fragment>:
앱 내의 개별 화면(fragment)
fragment class name
<action>:
프래그먼트 간의 탐색 경로를 나타낸다. 예를 들어 첫 번째 프래그먼트에서 두 번째 프래그먼트로 이동하는 동작을 정의할 때 사용한다.
navigate()
의 인자값으로 들어가 코드 내에서 해당 액션을 호출
할 수 있도록 한다.액션이 가리키는 대상
fragment의 ID 위 코드에서는 해당 액션을 호출하면 secondFragment로 이동한다.NavGraph와 현재 상태를 연결하여 실제 이동을 처리한다. NavController
를 통해 다른 프래그먼트로 이동하거나 이동 행동을 관리하는 탐색을 수행하는 컨트롤러
이다. navigate()
함수 인자값은 Navigation Graph에서 선언한 action의 id값
이다.
findNavController().navigate(R.id.action_to_secondFragment)
Safe Args
는 Android Jetpack Navigation 컴포넌트의 일부로, 프래그먼트 간에 데이터를 안전하게 전달
할 수 있는 기능을 제공한다.
프래그먼트 간 데이터를 전달하려면 일반적으로 Bundle
객체를 사용하여 데이터를 전달한다. 하지만 이 방식을 타입 오류
를 일으킬 가능성이 있으며, 잘못된 키 사용
등의 문제가 발생할 수 있다. Safe Args는 이러한 문제를 해결해준다.
즉, Safe Args는 타입 안전성
을 제공한다. Safe Args는 각 프래그먼트 간에 전달되는 인수의 타입을 컴파일 시점에 검사
한다. 잘못된 타입의 데이터가 전달되면 컴파일 오류가 발생
하므로 런타임 오류를 방지
할 수 있다.
소스 프래그먼트에서 데이터 전달
val bundle = Bundle()
bundle.putInt("myKey", 42)
findNavController().navigate(R.id.secondFragment, bundle)
목적 프래그먼트에서 데이터 수신 (잘못된 타입 사용)
val myValue = arguments?.getString("myKey") // 잘못된 타입 사용
위 예시 코드는 런타임에서 에러를 발생시킨다. "myKey"에 연관된 값은 정수이지만, 문자열로 해석하려고 하고 있기 때문이다.
Safe Args를 사용하면, 프래그먼트 간에 전달되는 인수의 타입을 컴파일 시점에 검사하므로 위와 같은 문제를 방지할 수 있다.
NavGraph에서 인수 정의
<fragment android:id="@+id/secondFragment" android:name="com.example.SecondFragment">
<argument
android:name="myIntArg"
app:type="integer"
android:defaultValue="0" />
</fragment>
소스 프래그먼트에서 인수 전달
val action = FirstFragmentDirections.actionToSecondFragment(42)
findNavController().navigate(action)
목적 프래그먼트에서 인수 수신
val args: SecondFragmentArgs by navArgs()
val myIntArg = args.myIntArg // 올바른 타입으로 수신
위 예시 코드는 인수의 타입이 일치하지 않으면 컴파일 오류가 발생하므로 런타임 오류를 방지하고, 코드의 안전성을 높일 수 있따.
buildscript {
repositories {
google()
}
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.6.0"
}
}
apply plugin: "androidx.navigation.safeargs"
dependencies {
implementation "androidx.navigation:navigation-fragment-ktx:2.6.0"
implementation "androidx.navigation:navigation-ui-ktx:2.6.0"
}
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<fragment android:id="@+id/firstFragment"
android:name="com.example.FirstFragment">
<action android:id="@+id/action_to_secondFragment"
app:destination="@id/secondFragment">
<argument
android:name="user"
app:argType="com.example.User" />
</action>
</fragment>
<fragment android:id="@+id/secondFragment"
android:name="com.example.SecondFragment" />
</navigation>
FirstFragment
에서 SecondFragment
로 User 인스턴스 전달
class FirstFragment : Fragment() {
// 코드 생략
fun navigateWithArgs(user: User) {
val action = FirstFragmentDirections.actionToSecondFragment(user)
findNavController().navigate(action)
}
}
SecondFragment
에서 인수를 받는다.class SecondFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val args: SecondFragmentArgs by navArgs()
val user = args.user
}
}