본문 바로가기

[Android]/App UI 따라 만들기

[배달의민족2] 클론코딩 - 1. 스플래시 화면

안녕하세요! 허접 샴푸입니다.

 

오랜만에 다시 시작하고자 합니다.

처음부터 끝까지 한번 제대로 만들어보도록 하겠습니다.

 

이번 편은 먼저 스플래시 화면과 권한 체크를 하는 화면입니다.

 

[만들고자 하는 화면]

인트로 화면 -> 권한 동의 여부 다이얼로그 -> 권한 체크(허용 / 거절)

1. 인트로 화면

2. 권한 동의 여부 다이얼로그

3. 권한 체크 팝업

 

[필요 사전 지식]

1. 안드로이드

2. 코틀린

3. Android Jetpack

4. 코루틴

* 일단 사전 지식이 없더라도 따라 만들면서 부족한 점은, 인터넷 찾아보면서, 학습하시면 됩니다.

 

[목표]

1. Fragment 및 ResultListener

2. Jetpack Navigation

3. Permission check

 

[시작]

1) Gradle 설정

 

 

- build.gradle (Module: app)에 위와 같이 ViewModel 및 Jetpack Navigation 종속 항목을 추가합니다.

- 이 프로젝트에서는 ViewModel과 Jetpack Navigation을 사용해서 화면을 탐색하고자 합니다.

 

2) 구조

패키지 스트럭쳐

- 안드로이드 앱 권장 아키텍처를 사용하기 위해 위와 같이 패키지 스트럭처를 가져갔습니다.

- 클린 아키텍처에 중점을 두어 프로젝트를 제작해 나가겠습니다.

- 간단하게 말씀드리자면, 권장 아키텍처 및 클린 아키텍처에서는 코드를 총 3개의 레이어인 data, domain, presentation 레이어로 나눌 수 있습니다. 

- data 레이어: 비즈니스 로직을 담당하는 레이어(네트워크 통신, 로컬 데이터베이스 통신 등)

- domain 레이어: presentation 레이어와 data 레이어를 잇는 매개체 역할을 하는 레이어

- presentation 레이어: 말 그대로 화면에 보여주는 역할과 관련된 코드를 지닌 레이어입니다.

* 일단 레이어 분리부터, 이것저것 이해가 잘 안 가실 테지만 차근차근 배우면서 설명드리도록 하겠습니다.

 

Presentation 레이어

- 일단 이번에 만들 것은, 위와 같습니다. 

- Presentation 패키지 하위에 ui 패키지가 있고, ui 패키지에는 각 화면을 담당하는 Fragment 클래스가 있습니다.

- 그리고 utils 패키지는 ui에 필요한 파일들을 작성하는 곳입니다.

- 마지막으로 MainActivity가 있으며, 저희는 Single Activity 규칙을 사용합니다.

 

3) 네비게이션 설정

 

 

- MainActivity는 단순히 FragmentContainerView를 가지고 있고, 저희는 모든 화면을 Fragment로 작성하여 FragmentContainerView가 모든 네비게이션을 담당하도록 설정하였습니다.

- FragmentContainerView는 네비게이션 그래프가 필요하며 아래와 같습니다.

 

 

 

- 네비게이션 그래프는 nav_graph.xml 파일이며, 시작 화면은 무엇이고, 각 화면에서 어느 화면으로 탐색을 할 수 있는지 규칙을 정하는 navigation 파일입니다.

- introSplashFragment가 startDestination(앱을 시작하였을 때 가장 먼저 보이는 화면)이 되며, 해당 화면에서는 checkPermissionsDialogFragment와 introTermAgreementFragment로 탐색(화면 전환)을 할 수 있게 설정하였습니다.

- popupTo와 popUpToInclusive: introSplashFragment에서 introTermAgreementFragment로 화면 전환을 하고 나면, introTermAgreementFragment에서 백버튼을 눌러 뒤로 가기를 할 경우, nav_graph로 pop을 하도록 설정하였습니다.

- popupToInclusive 옵션에 true를 설정하여 nav_graph 또한 네비게이션 백스택에서 제거되도록 설정하였습니다. 그래서 introTermAgreementFragment에서 뒤로가기를 누르면 네비게이션 백스택에 남아있는 화면이 없으므로 앱은 꺼지게 됩니다.

- 만약 "false"로 설정하였다면, nav_graph는 startDestination인 introTermAgreementFragment를 다시 화면에 표시하고 앱이 진행될 것입니다.

- 네비에기션 파일은 res > navigation 디렉터리에 위치해야 합니다.

 

4) Presentation Layer 코딩

<IntroSplashFragment.kt>

 

 

1. delay 옵션

- 1000ms = 1초

- 1초를 딜레이 한 뒤 함수들을 호출하도록 하였습니다.

 

2. 권한 체크

- 사용자가 READ_EXTERNAL_STORAGE 권한을 허용했는지 체크를 합니다.

- 권한이 있을 경우 navigateToIntroTermAgreementScreen()를 호출하여 그다음 화면으로 이동하도록 하였습니다.

- 권한이 없을 경우 navigateToCheckPermissionDialogScreen()를 호출하여 권한 동의 여부 다이얼로그 화면으로 이동하도록 하였습니다.

 

- findNavController()는 MainActivity에서 관리되는 Navigation Controller 객체를 반환받으며, 해당 객체의 navigate 함수를 이용하여 저희가 네비게이션 그래프인 nav_graph.xml에 선언한 action id를 인자로 넘겨주어 화면을 탐색할 수 있습니다.

 

- setFragmentResultListener: 다른 Fragment의 결과 값을 받을 수 있는 리스너입니다.

- Request Key를 사용하여 결괏값을 준 화면과 받는 화면 간의 계약을 체결할 수 있습니다. 만약 Request Key가 다르다면 결과 값을 받을 수 없습니다.

- 권한 체크 동의 여부 다이얼로그 화면인 checkPermissionDialogFragment에서 동의를 의미하는 "확인" 버튼을 클릭했을 때 해당 화면에서 결과 값을 현재 introSplashFragment에게 리턴합니다. 즉, 동의를 했다는 결과를 받은 introSplashFragment 화면에서는 navigateToIntroTermAgreementScreen()을 호출하여 그다음 화면으로 이동하게 됩니다.

 

<CheckPermissionsDialogFragment.kt>

* 먼저 권한 체크에 앞서, 아래 코드를 AndroidManifest.xml에 추가해주셔야 합니다.

 

 

- 권한 체크 여부를 묻는 다이얼로그 화면입니다.

 

- "확인" 버튼인 btnConfirm를 클릭하였을 때 reqeustPermissionLauncher는 neededPermission 권한이 있는지 확인을 합니다.

 

- requestPermissionLauncher는 권한이 있는지 없는지 결과 값을 받게 됩니다. isGranted 변수를 사용하여 권한을 허용했는지 거절했는지 판단할 수 있습니다.

- 단 배달의 민족 어플을 보시면, 권한에 동의하지 않아도 앱이 진행되기에, 위에서 if else 조건문을 사용하진 않았습니다. 

- 권한에 동의하지 않아도 앱이 진행될 수 있도록 조건문 없이 setFragmentResultAndPopBackStack() 함수를 호출하도록 하였습니다.

 

- findNavController().popBackStack() 호출하여 현재 화면을 네비게

이션 백스택에서 제거합니다. 즉, 지금 보여주고 있는 화면을 더 이상 보여주지 말라는 뜻입니다.

- 그다음 setFragmentResult(REQUEST_KEY_PERMISSION_CHECK_READ_EXTERNAL_STORAGE, bundleOf()) 를 이용하여, introSplashFragment에게 "확인" 버튼을 눌렀다는 것을 알려줍니다.

- bundleOf()를 이용하여 더 많은 값을 넘겨줄 수 있지만, 저희 앱에서는 딱히 넘겨줄 값이 없기에 빈 번들 객체만을 넘겨주게 하였습니다.

 

 

* 중요한 점

(IntroSplashFragment.kt)

(CheckPermissionsDialogFragment.kt)

- 위 두 코드를 보시면 REQUEST_KEY_PERMISSION_CHECK_READ_EXTERNAL_STORAGE를 key 값으로 이용하여 Fragment 간의 통신 계약을 체결하였습니다.

- 즉, key가 같아야 원하는 Fragment로부터 결괏값을 받을 수 있습니다.

 

 

[마무리]

- Jetpack Navigation에 대해 간단히 알아보았습니다.

- FragmentResultListener에 대해 간단히 알아보았습니다.

- 권한 체크에 대해서 간단히 알아보았습니다.

 

[다음 목표]

- 마케팅 동의 여부 화면을 만들고, ViewModel을 사용하여 Ui 상태를 보존하는 방법 및 왜 ViewModel을 사용해야 하는지 등에 대해서 알아보도록 하겠습니다.

 

 

 

 

[Github] 코드를 다운로드하여서 실행해보시기 바랍니다.

https://github.com/DJDrama/BaeminPractice2/tree/1_splashscreen

 

GitHub - DJDrama/BaeminPractice2

Contribute to DJDrama/BaeminPractice2 development by creating an account on GitHub.

github.com