๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿค2024 ์•ˆ๋“œ๋กœ์ด๋“œ/๐Ÿฟ ์˜ํ™” ํ”„๋กœ์ ํŠธ ๊ฐœ๋ฐœ ์ผ์ง€

ํ”„๋กœ์ ํŠธ์— Jetpack Navigation ์ ์šฉํ•˜๊ธฐ

by hyeonha 2024. 10. 15.

๋ชฉ์ฐจ

    ๋“ค์–ด๊ฐ€๊ธฐ ์ „์—

    ์˜ค๋Š˜์€ ์–ด์ œ ํ•˜๋ฃจ๋™์•ˆ ํ”„๋กœ์ ํŠธ์— Navigation ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ ์šฉํ•œ ๊ณผ์ •์„ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

    ์ฒ˜์Œ ์‚ฌ์šฉํ•ด๋ณธ ๋‚ด์šฉ์ด๋ผ์„œ ๊นŒ๋จน์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๊ทธ๋ฆฌ๊ณ  ๊ธฐ์กด ๋ฐฉ์‹ ๋Œ€๋น„ ์ข‹์•˜๋˜ ์ ์„ ๊ธฐ๋กํ•ด๋‘๊ธฐ ์œ„ํ•ด ์ ์–ด๋ณด๊ฒ ๋•…


    ๊ธฐ์กด ๋ฐฉ์‹ (์ด๋™ & ๋ฐ์ดํ„ฐ ์ „๋‹ฌ)

    ์šฐ์„  ์šฐ๋ฆฌ ํ”Œ์  ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

    ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

    - ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ธฐ์ค€

    ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ๋งŒ๋“  ๊ธฐ์ค€์€ ํ™”๋ฉด ๋ณ€๊ฒฝ ์‹œ ๋ณด์—ฌ์•ผํ•˜๋Š” ํฐ ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์žก์•˜๋‹ค.

    ๋ฉ”์ธ ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ๊ฒฝ์šฐ ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”๊ฐ€ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ๊ต์ฒด๋จ์—๋„ ๊ณ„์† ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚˜์•ผํ–ˆ๊ณ , ๊ฐ์ƒ๋ฌธ์„ ์ž‘์„ฑํ•˜๋Š” ํ™”๋ฉด์—์„œ๋Š” ํ•ด๋‹น ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜๋ฐ”๊ฐ€ ํ™”๋ฉด ์ƒ์—์„œ ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์—ˆ๋‹ค.



    ํฌ๊ฒŒ MainActivity์—์„œ๋Š” ๋ฐ”ํ…€๋„ค๋น„๊ฒŒ์ด์…˜ ๋ฐ”์™€ FragmentContainerView๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ ,

    ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜ ์•„์ดํ…œ์˜ ํด๋ฆญ๊ณผ ํ™”๋ฉด ์ƒ์˜ ํด๋ฆญ์„ ํ†ตํ•ด ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™ ๋ฐ ํ™”๋ฉด ์ด๋™์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.
    ์ด ๋•Œ FragmentManager์„ ํ†ตํ•ด ์ด๋™์„ ์ฒ˜๋ฆฌํ•ด์ฃผ์—ˆ๋Š”๋ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ A์—์„œ ํ”„๋ž˜๊ทธ๋จผํŠธ B๋กœ ์ด๋™ํ•ด์•ผํ•  ๊ฒฝ์šฐ ์•กํ‹ฐ๋น„ํ‹ฐ์— ๊ตฌํ˜„ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœ(1)ํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ B๋กœ ์ด๋™(2)ํ•˜๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค. 

        // ์•กํ‹ฐ๋น„ํ‹ฐ์— ๊ตฌํ˜„ํ–ˆ๋˜ ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™ 
       
        fun navigateToReportFragment() {
            supportFragmentManager.beginTransaction()
                .add(R.id.main_fragment_container_view, ReportFragment.newInstance())
                .addToBackStack(null)
                .commit()

     

    // ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™์ด ํ•„์š”ํ•  ๋•Œ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ์—ˆ๋‹ค.
    
    binding.btnReply.setOnClickListener {
                (activity as MainActivity).navigateToReportFragment(reportId)

     

    ๋งŒ์•ฝ ํ”„๋ž˜๊ทธ๋จผํŠธ A -> B๋กœ์˜ ์ด๋™ ์ค‘ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด์•ผํ•œ๋‹ค๋ฉด ์ด๋™ ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ  ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹์ด์—ˆ๋‹ค.

    // ์•กํ‹ฐ๋น„ํ‹ฐ์— ์ž‘์„ฑํ•ด๋‘” ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™ 
      fun navigateToDetailMovieFragment(movieID: Int) {
            supportFragmentManager.beginTransaction()
                .add(R.id.main_fragment_container_view, MovieDetailFragment.newInstance(movieID))
                .addToBackStack(null)
                .commit()
        }
      
    // ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ํด๋ฆญ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™
    override fun onClick(position: Int) {
                        val movieId = movie[position].id
                        (activity as MainActivity).navigateToDetailMovieFragment(movieId)
                    }
                }

     

    ๋„ฃ์–ด์ค€ ๋ฐ์ดํ„ฐ๋Š”  ์ด๋™ํ•œ ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

            val movieId = arguments?.getInt("MOVIE_ID")

     

    ์ด๋Š” MovieDetailFragment ๊ฐ์ฒด ์ƒ์„ฑ ์‹œ  ํ•„์š”ํ•œ argument๋ฅผ ๋ถ™์—ฌ ๋ฐ˜ํ™˜ํ•ด์ฃผ์—ˆ๋‹ค.

        companion object {
        // MovieDetailFragment ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๋ฉ”์„œ๋“œ 
            fun newInstance(
                movieID: Int,
            ): MovieDetailFragment {
            
            // DetailFragment ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค
                val fragment =
                    MovieDetailFragment().apply {
                    
                    // ๊ทธ๋ฆฌ๊ณ  ๊ทธ ํ”„๋ž˜๊ทธ๋จผํŠธ์— movieID๋ผ๋Š” bundle(arguments)๋ฅผ ๋ถ™์—ฌ์ค€๋‹ค.
                        arguments =
                            Bundle().apply {
                                putInt("MOVIE_ID", movieID)
                            }
                    }
                    
                    // argument๊ฐ€ ๋ถ™์–ด์žˆ๋Š” ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
                return fragment
            }
        }

     


    Navigation ์ ์šฉ

    - ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜๊ณผ ์—ฐ๊ฒฐ

    ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ฐ„ ๋„ค๋น„๊ฒŒ์ด์…˜ ์‹œ ๊ธฐ์กด์—๋Š” ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜๋ทฐ ์ด๋ฒคํŠธ๋ฅผ ๋ทฐํŽ˜์ด์ €๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ๊ณ  ์žˆ์—ˆ๋‹ค.

    ๊ทธ๋Ÿฐ๋ฐ Navigation๊ณผ ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์—ฐ๋™ํ•ด์„œ ๋”์šฑ ์งง์€ ์ฝ”๋“œ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธํ–ˆ๋‹ค.

            val navHostFragment =
            
            // supportFragmentManager: ์•กํ‹ฐ๋น„ํ‹ฐ ๋‚ด์—์„œ ํ”„๋ž˜๊ทธ๋จผํŠธ์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. 
            // Id๋กœ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ์ •์˜ํ•ด์ค€ NavHostFragment๋ฅผ ์ฐพ๋Š”๋‹ค.
                supportFragmentManager
                    .findFragmentById(R.id.main_fragment_container_view) as NavHostFragment
                    
            // navController : ์•ฑ ํƒ์ƒ‰ ์ฒ˜๋ฆฌ ๋‹ด๋‹น ๊ฐ์ฒด, ๋ฐฑ์Šคํ…๊ณผ ์—ฌ๋Ÿฌ ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ฐ„ ํƒ์ƒ‰์„ ๊ด€๋ฆฌํ•œ๋‹ค.        
            navController = navHostFragment.navController
            
            binding.navBar.setupWithNavController(navController)

     

    ๊ทธ๋ฆฌ๊ณ  ๋ฐ”ํ…€ ๋„ค๋น„๊ฒŒ์ด์…˜์˜ ์•„์ดํ…œ๊ณผ ์ด๋™ํ•  ํ™”๋ฉด์„ ์—ฐ๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค.

    ์ด ๋•Œ ์ฃผ์˜ํ•  ์ !!! 

    itemId์™€ nav_graph์—์„œ ์ •์˜ํ•ด์ค€ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ id๊ฐ€ ๋™์ผํ•ด์•ผ ์ •์ƒ์ž‘๋™ํ•œ๋‹ค.

    ๋‚˜๋Š” ๋ณ„๊ฐœ๋ผ๊ณ  ์ƒ๊ฐํ•˜์˜€๋Š”๋ฐ id๊ฐ€ ๋‹ฌ๋ผ์„œ ํด๋ฆญํ•ด๋„ ํ”„๋ž˜๊ทธ๋จผํŠธ๋‚˜ ํ™”๋ฉด์ด ์ด๋™ํ•˜์ง€ ์•Š์•˜๋‹ค.. ใ… ใ… ใ… 

    binding.navBar.setOnItemSelectedListener {
                when (it.itemId) {
                    R.id.allMovieReportFragment -> {
                        if (navController.currentDestination?.id != R.id.allMovieReportFragment) {
                            navController.navigate(R.id.allMovieReportFragment)
                        }
                        return@setOnItemSelectedListener true
                    }
                    R.id.myPageFragment -> {
                        if (navController.currentDestination?.id != R.id.myPageFragment) {
                            navController.navigate(R.id.myPageFragment)
                        }
                        return@setOnItemSelectedListener true
                    }
    
                    R.id.writeActivity -> {
                        val intent = Intent(this, WriteActivity::class.java)
                        startActivity(intent)
                        return@setOnItemSelectedListener true
                    }
                    else -> {
                        return@setOnItemSelectedListener false
                    }
                }
            }

     

    id๊ฐ€ ๋งž์ง€ ์•Š์œผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉฐ ์ด๋ฒคํŠธ๊ฐ€ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค.

    java.lang.IllegalArgumentException: Navigation action/destination com.example:id/navigateToWriteReport cannot be found from the current destination Destination(com.example:id/writeReportFragment) label=WriteReportFragment class=WriteReportFragmenIgnoring onNavDestinationSelected for MenuItem id/write as it cannot be found from the current destination Destination(com.example:id/allMovieReportFragment) label=AllMovieReportFragment class=com.example.ui.report.all.AllMovieReportFragment

     

    - ํ”„๋ž˜๊ทธ๋จผํŠธ ์ด๋™

    ์˜ํ™”๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ์˜ํ™”์˜ ์ƒ์„ธ ์ •๋ณด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ”„๋ž˜๊ทธ๋จผํŠธ๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๋ฉด ๋˜๋Š”๋ฐ

    ์—ฌ๊ธฐ์„œ navigateToMovieDetail์€ nav_graph.xml ํŒŒ์ผ์—์„œ action์— ์ •์˜ํ•ด์ค€ id๊ฐ’์ด๋‹ค.

    ๋„˜๊ฒจ์ค˜์•ผํ•  arguement๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•จ๊ป˜ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

    // findNavController() : ์ด ํ”„๋ž˜๊ทธ๋จผํŠธ ๋˜๋Š” ํ™œ๋™๊ณผ ์—ฐ๊ด€๋œ NavController๋ฅผ ์ฐพ๋Š”๋‹ค.
    // NavController : ์•ฑ ๋‚ด ํƒ์ƒ‰์„ ๊ด€๋ฆฌํ•˜๊ณ  ํƒ์ƒ‰ ๊ทธ๋ž˜ํ”„์— ์ •์˜๋œ ํ”„๋ž˜๊ทธ๋จผํŠธ, ์•กํ‹ฐ๋น„ํ‹ฐ ์‚ฌ์ด๋ฅผ ์ด๋™ํ•˜๋Š”๋ฐ ๋„์›€์„ ์ค€๋‹ค.
    
        private val navController by lazy { findNavController() }    
        
        private fun navigateToMovieDetail(movieId: Int) {
            val action = AllMovieReportFragmentDirections.navigateToMovieDetail(movieId)
            navController.navigate(action)
        }

     

    MovieDetailFragment์—์„œ ๋ฐ›์€ movieId๋ฅผ ์ด์šฉํ•˜๋ ค๊ณ  ํ•œ๋‹ค๋ฉด args ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•ด์ฃผ๊ณ  args.movieId์™€ ๊ฐ™์ด ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

    val args: MovieDetailFragmentArgs by navArgs()
    
    //MovieDetailFragmentArgs : Safe Args๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ƒ์„ฑ๋˜๋Š” ํด๋ž˜์Šค์ด๋‹ค. 
    // MovieDetailFragment์— ์•ˆ์ „ํ•˜๊ฒŒ ์œ ํ˜•๋ณ„๋กœ ์ „๋‹ฌ๋œ ์ธ์ˆ˜๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ์ˆ˜๋™์œผ๋กœ ๋ฒˆ๋“ค์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š์•„๋„ ์ธ์ˆ˜๋ฅผ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ๋”ฐ.
            val args: MovieDetailFragmentArgs by navArgs()
            lifecycleScope.launch {
                    viewModel.searchMovieDetail(args.movieId)
            }

    safe args ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์ข‹์•˜๋˜ ์ 

    ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ฐ„ ์ด๋™์„ ๋”์šฑ ๊ฐ„ํŽธํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด Navigation์„ ์ ์šฉํ•˜๋‹ค๋ณด๋‹ˆ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ๋˜ํ•œ ์ง€์›ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋˜์—ˆ๊ณ  , ๋‹ค์†Œ ๋ฒˆ๊ฑฐ๋กญ๋˜ ์ธ์ž ์ „๋‹ฌ ๋ฐฉ์‹๋˜ํ•œ safe args๋ฅผ ์ ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•ด๋ณด์•˜๋‹ค.

     

    ๊ธฐ์กด bundle ๋ฐฉ์‹์—์„œ๋Š” ํ‚ค๋ฅผ ์ง์ ‘ ์—ฌ๋Ÿฌ ๋ฒˆ ์ž‘์„ฑํ•ด์ค˜์•ผํ•˜๋Š”๋ฐ ์ด ๋•Œ ์ž˜๋ชป ์ž‘์„ฑํ•  ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์ œ๋Œ€๋กœ ์˜ค์ง€ ์•Š๋Š”๋‹ค.

    ๋‚˜๋„ ๋ช‡ ๋ฒˆ ํ‚ค ์ฒ ์ž๋ฅผ ์ž˜๋ชป ์ž‘์„ฑํ•˜์—ฌ ๋ฐ์ดํ„ฐ๊ฐ€ ์˜ค์ง€ ์•Š์•˜๋˜ ์ ์ด ๋ช‡ ๋ฒˆ์žˆ์—ˆ๊ณ , ๊ดœํžˆ ๋‹ค๋ฅธ ๋ถ€๋ถ„์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š” ์ค„ ์•Œ๊ณ  ์‹œ๊ฐ„์„ ํ—ˆ๋น„ํ–ˆ๋˜ ๋•Œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

    // ๋ฒˆ๋“ค ๋ฐฉ์‹
    val movieID = arguments?.getInt("MOVIE_ID")  // Needs to use the correct key
    
    // safe agrs ๋ฐฉ์‹
        private val args: ReportThumbnailFragmentArgs by navArgs()
       args.movieName

     

    ๊ทธ๋Ÿฌ๋‚˜ safe args ๋ฐฉ์‹์—์„œ๋Š” nav_graph.xml ์ƒ์—์„œ argument๋ฅผ ์ •์˜ํ•ด์ฃผ๊ณ  ๊ฐœ๋ฐœ ์ค‘์—๋Š” ๋ฌธ์ž์—ด ํ˜•ํƒœ์˜ ํ‚ค๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ์ง€ ์•Š์•„๋„ ๋œ๋‹ค. 

    ์œ ํ˜•์ด๋‚˜ ํ‚ค ์œ ํ˜•์ด  nav_graph.xml ์—์„œ ์ž‘์„ฑํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€์ง€๋„ ์•Œ์•„์„œ ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ํŽธํ–ˆ๋‹ค ใ…Žใ…Ž 

    ๋ฌด์—‡๋ณด๋‹ค  nav_graph.xml ํŒŒ์ผ 1๊ฐœ๋งŒ ๊ฒ€ํ† ํ•˜๋ฉด ๋œ๋‹ค๋Š” ์ ์ด ๊ฐ€์žฅ ํŽธํ–ˆ๋‹ค.

     

    Navigation๋„ ๊ฐ„ํŽธํ•˜๊ฒŒ ํ™”๋ฉด์„ ์ด๋™ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ์–ด์„œ ํŽธํ–ˆ๋‹ค

    ๊ธฐ์กด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ณ  Navigation์„ ์‚ฌ์šฉํ•˜๋‹ˆ ๋”์šฑ ์ฐจ์ด๊ฐ€ ๋Š๊ปด์กŒ๋‹ค. 

     

    ๊ทธ๋Ÿผ Navigation ์ ์šฉ ๊ณผ์ • ๋„์

    728x90