모바일 앱 화면 Life Cycle (1)
AOS의 Life Cycle, iOS의 Life Cycle
하나의 모바일 앱 안에는 수많은 화면들이 포함되어 있다. 각각의 화면은 사용자의 입력을 앱 내부로 전달하거나, 앱의 상태를 사용자에게 출력 하며, 사용자가 앱과 상호작용할 수 있도록 한다.
이러한 화면이 얼마나 잘 동작하는 가는 앱에 대한 사용자 경험을 좌우한다. 화면 뒤 쪽에서 서비스가 아무리 매끄럽게 돌아간 들, 화면이 버벅거리거나 깜빡거리는 모습이 계속 반복되면 사용자는 앱에 대한 좋은 인상을 가질 수 없다. 그만큼 앱 개발자는 완성도 높은 화면을 개발하는 데에 많은 시간과 노력을 기울이게 된다.
화면의 완성도를 높이는 것은, 화면의 Life Cycle을 제대로 이해하는 것에서부터 출발한다. 하나의 화면이 생성되어, 사용자에게 보여지고, 할당된 기능을 수행한 후, 종료되기까지의 과정을 ‘Life Cycle’ 이라고 한다. Life Cycle의 각 과정에 대한 순서와, 해당 과정에서 수행되는 일들을 명확히 알고 나면, 화면이 언제 어떻게 행동하는 지를 알 수 있다.
이 글은 두 편으로 나뉜 시리즈 글로, 1) Android OS(이하 AOS)와 iOS의 화면 Life Cycle에 대하여 각각 알아보고, 2) 두 운영체제의 화면 Life Cycle을 비교함으로써, 모바일 앱 화면의 Life Cycle에 대한 이해를 돕고자 한다.
그럼 그 첫 번째 순서로, AOS와 iOS의 화면 Life Cycle에 대하여 먼저 알아보자. 이미 알고 있는 내용이라면, 그림만 훑어본 후 두 운영체제를 비교하는 다음 편으로 이동하는 것을 추천한다.
AOS의 화면, Activity Life Cycle
Activity
안드로이드 앱을 구성하는 필수 요소는 Activity, Service, Broadcast Receiver, Content Provider, 이렇게 4 가지이다. 그중 Activity는 안드로이드 앱의 화면을 담당하여 사용자와 상호작용을 책임지는 가장 중요한 요소라고 할 수 있다.
Activity Life Cycle
Activity의 Life Cycle은 총 7개의 단계로 나누어져 있으며, 각각의 단계는 콜백 함수로 작동한다.
1. onCreate(…)
onCreate(…)는 Activity 생성을 위한 콜백이며, Life Cycle 동안 한 번만 실행이 된다. setContentView()를 호출하여 XML 파일로부터 화면을 구성하고, 화면에 사용할 데이터를 초기화한다.
2. onStart()
onStart()는 화면 구성이 완료된 이후에 호출된다. onStart()는 사용할 리스너를 등록하거나, 서버에 통신을 요청하는 등, 사용자와 상호 작용을 할 준비하기에 적합한 위치이다.
Android Devleopers Documentation을 포함하여, 많은 자료에서 onStart()가 호출되면 Activity가 사용자에게 표시된다고 설명하고 있으나, 이 글을 작성하며 테스트해 본 결과로는 onResume()이 완료된 후에 Activity가 표시되는 것으로 확인하였다.
이 글은 사용자의 관점에 집중하기 위하여, onResume() 이후에 화면이 표시되는 것을 기준으로 작성하였기 때문에, 관련한 부분에서 다른 자료들과 맞지 않는 부분이 있을 수 있다.
3. onResume()
onResume()은 Activity가 사용자와 상호작용을 할 수 있도록 활성화되기 직전에 호출된다. onResume()은 데이터를 화면에 적용하기에 알맞은 위치이다. 필요한 애니메이션을 실행하거나 화면을 갱신하여 적절한 데이터가 화면에 표시되도록 한다.
onResume()에서 너무 많은 일이나 작업시간이 긴 일을 수행할 경우 수행 중인 일이 완료될 때까지 화면이 비활성화 된 채로 표시 되므로, onStart()와 일을 배분하여 최대한 짧은 수행 시간을 갖도록 코드를 작성해야 한다.
4. onPause()
onPause()는 Acitivity가 활동을 멈추고 상호작용이 불가능한 상태이다. 이때 저장되지 않은 데이터를 저장소에 저장하거나 애니메이션 중인 작업을 중지하면 된다. onPause()의 작업이 길어지면 다음 Acitivity까지 넘어가는 시간이 많이 소요되므로 최대한 짧은 작업만을 수행하도록 해야 한다.
5. onStop()
onStop()은 Acitivty가 더 이상 화면에 표시되지 않고 실행이 중단된 후에 호출된다. onStop()은 onPause()에서 작업 시간상 수행하지 못했던(CPU를 많이 사용하는 등의) 작업을 수행하기에 적절한 위치이다.
6. onDestroy()
onDestroy()는 Acitivity가 소멸되기 직전에 호출된다. onDestroy()에서는 사용자가 finish()를 사용하여 Acitivity를 닫았는지 여부를 isFinishing()으로 확인할 수 있다.
7. onRestart()
Activity가 중지(onStop())된 이후, 다시 시작되는 경우에 onStart()에 앞서 호출된다. 중지되었던 화면이 다시 사용자에게 표시될 때에만 특별히 수행해야 할 일이 있다면, onRestart()에 코드를 작성하는 것이 적합하다.
iOS의 화면, ViewController Life Cycle
ViewController
iOS에서 각 화면은 ViewController라는 단위로 구동된다. ViewController는 사용자에게 화면을 표시하고, 사용자의 입력에 따라 화면의 상태를 변화 시키며 사용자와 상호작용한다.
ViewController Life Cycle
ViewController의 Life Cycle은 총 8개의 단계로 나누어져 있다.
1. init(…)
init(…)은 ViewController를 생성하기 위하여 호출되며, 내부적으로 loadView()를 호출하여 Root View를 생성한다. init(…)에 파라미터들을 추가하여 ViewController에서 사용하는 변수의 initialize를 수행할 수 있다.
2. loadView()
loadView()의 가장 중요한 점은, 기본적으로 개발자가 이 함수를 직접 호출하지 않아야 한다는 것이다. loadVIew()는 ViewController와 연결된 디자인 파일(xib, storyboard)로부터 view를 생성하여 ViewController의 root view로 사용할 수 있도록 한다.
loadView()는 ViewController의 init(…)에서 간접 호출되기 위해서 존재한다. 개발자가 loadView()를 직접 불러서 사용한다는 것은 해당 ViewController의 모든 UI를 코드로 하나하나 구현하겠다는 의미이기 때문에, 특별한 경우가 아니라면 사용할 일은 없다.
3. viewDidLoad()
viewDidLoad()는 ViewController에 view 연결이 완료되었을 때 호출되는 콜백 함수이다. viewDidLoad()는 ViewController가 생성될 때 한 번만 호출되므로, view에서 필요한 데이터 등을 initialize 하기 적절한 위치이다.
4. viewWillAppear()
viewWillAppear()는 ViewController가 사용자에게 보여지기 전에 호출되는 함수이다. viewWillAppear()는 view가 사용자에게 보여질 때의 상태를 custom 하기에 적합한 위치이다. 예를 들어, stautus bar를 hidden 하거나, orientation을 전환하는 등의 설정이 이에 해당한다.
viewWillAppear()는 view가 사용자에게 보여질 때마다 호출되므로, 데이터의 initialize보다는 view의 appearance에 집중하여 코드를 작성하는 것이 좋다.
5. viewDidAppear()
viewDidAppear()는 ViewContoller가(담당하는 view가) 사용자에게 완전히 보여진 후에 호출되는 함수이다. 다만, viewDidAppear()는 이미 유저에게 view가 보여진 이후에 호출되는 함수이기 때문에, view와 직접적으로 관계되지 않은 데이터들을 처리하는데 적합한 위치이다.
6. viewWillDisappear()
viewWIllDisappear()는 view가 사용자에게서 숨겨지기 직전에 호출되는 함수이다. viewWillDisappear()는 수행 중인 user interaction을 정리하고 결과를 저장하는 일을 수행하기에 적합한 위치이다.
해당 ViewController에 들어올 때(viewWillAppear에서), status bar 등 앱 전체에 영향을 미치는 설정을 했다면, viewWillDisappear()에서 다시 원상태로 복귀시켜주어야 다른 ViewController에 혼동이 생기지 않는다.
7. viewDidDisappear()
viewDidDisappear()는 화면이 사용자에게서 보이지 않게 된 이후에 호출되는 함수이다. viewController가 숨겨진 이후에, 앞선 viewWillAppear()에서 못다 한 마무리 작업이 필요한 경우에 사용한다.
8. deinit()
deinit()은 ViewController가 삭제(de-allocation)될 때 호출 되는 함수이다. 일반적으로 iOS 앱에서 ViewController의 deinit()은 거의 사용할 일이 없다. 앞선 viewDidDisapear() 등을 통해, view가 사용자로부터 보여지지 않게 되는 때를 알 수 있기 때문이다.
사용자가 사용하지 않는 view들은 언제 사라져도 무방하도록 미리 정리해두는 편이 좋다. deinit()을 직접 컨트롤 하기 보다는, deinit()이전의 콜백함수들에서 데이터를 미리 정리해두어 viewController의 de-allocation은 OS level에서 언제든지 가능하도록 방치하는 것이 보다 일반적이다.
다음 편으로 이어집니다.
글 작성에 참고한 자료
● 정재곤, Do it! 안드로이드 앱 프로그래밍, 이지스퍼블리싱(2020)
● 안드로이드 액티비티(Activity) 생명주기 총정리, programmingfbf7290, https://programmingfbf7290.tistory.com/entry/안드로이드-액티비티Activity-생명주기-총정리
● Android: Service란 무엇인가 그리고 생명주기는?, 내가 보려고 만든 블로그, https://ndpaulkim.tistory.com/92
● Android Developers Documentation, https://developer.android.com/docs
● Apple Developer Documentation, https://developer.apple.com/document