안녕하세요! [생각하는 개발자]입니다.
우리는 지난 시간에 화면을 구성하는 큰 지도(Navigation, TabBar)와 이를 넘나드는 4가지 액션(Push/Pop, Present/Dismiss)을 체계적으로 정리했습니다. 이제 원하는 타이밍에 화면을 열고 닫을 수 있게 되었죠.
하지만 진짜 프로 개발자가 되려면 한 단계 더 깊은 질문을 던져야 합니다.
"화면이 전환될 때, 우리가 원하는 서버 데이터는 어느 타이밍에 불러와야 가장 매끄럽게 보일까?"
이 질문의 답을 찾기 위해, 뷰 컨트롤러가 메모리에 태어나 눈에 보이고, 결국 사라질 때까지의 모든 과정을 다루는 생명주기(Lifecycle)의 실무 활용 바이블을 준비했습니다.

5.2.1 캔버스 위에 재료 얹기 (생명주기 메서드 오버라이딩)
생명주기를 완벽히 지배하려면 UIViewController 클래스가 제공하는 내부 이벤트 메서드들을 오버라이드(Override)하여 순서를 제어해야 합니다.
코드로 직접 로그를 찍어보며 흐름을 추적할 수 있도록 기본 뼈대 코드를 배치해 봅니다.
import UIKit
class LifeCycleViewController: UIViewController {
// 1. 메모리에 뷰가 로드되었을 때 (최초 1회)
override func viewDidLoad() {
super.viewDidLoad()
print("1. viewDidLoad: 메모리에 로드됨 (초기화의 시작)")
}
// 2. 화면이 나타나기 직전 (화면 전환마다 반복)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("2. viewWillAppear: 화면이 나타날 준비 중")
}
// 3. 화면이 완전히 나타난 직후
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("3. viewDidAppear: 화면에 완전히 보임 (유저 인터랙션 가능)")
}
// 4. 화면이 사라지기 직전
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("4. viewWillDisappear: 화면이 곧 사라짐")
}
// 5. 화면이 완전히 사라진 직후
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
print("5. viewDidDisappear: 화면이 완전히 사라짐 (메모리 정리 시점)")
}
}
5.2.2 [단계별 실습] 실무 상황별 라이프사이클 배치 공식
이론적으로 순서를 아는 것도 중요하지만, 실무에서 "어떤 코드를 어디에 써야 하는가"를 명확히 하는 것이 훨씬 중요합니다. 대표적인 3가지 상황을 매칭해 봅시다.
1단계: 딱 한 번만 실행할 무거운 초기화 ➔ viewDidLoad
- 무엇을 하나요?: 아웃렛 변수(@IBOutlet) 연결, UI 컴포넌트의 테두리 깎기, 폰트 적용 등 디자인 세팅, 그리고 컴퓨터 리소스를 한 번만 써도 되는 API 데이터 초기 요청입니다.
- 이유: 이 메서드는 뷰 컨트롤러가 메모리에 적재될 때 오직 한 번만 실행됩니다. 화면이 가려졌다가 다시 돌아와도 호출되지 않으므로 중복 호출을 막을 수 있는 가장 안전한 장소입니다.
2단계: 화면이 보일 때마다 실시간 동기화 ➔ viewWillAppear
- 무엇을 하나요?: 마이페이지의 유저 프로필 이미지 변경 사항 반영, 알림함의 새로운 메시지 읽음 처리 표시 등입니다.
- 이유: 만약 설정 화면(B)에서 회원 정보를 수정하고 뒤로 가기(Pop)를 눌러 메인 화면(A)으로 돌아왔을 때, 메인 화면의 viewDidLoad는 다시 호출되지 않습니다. 따라서 화면이 다시 유저 앞에 나타날 때마다 최신 정보를 갱신하는 코드는 무조건 viewWillAppear에 적어야 합니다.
3단계: 화면 애니메이션 및 포커싱 ➔ viewDidAppear
- 무엇을 하나요?: 팝업창이 서서히 커지는 애니메이션 효과 시작하기, 로그인 화면이 열리자마자 비밀번호 텍스트 필드에 포커스(becomeFirstResponder())를 주어 키보드를 자동으로 올리기 등입니다.
- 이유: 화면이 미처 다 그려지기도 전에 키보드부터 불쑥 튀어나오면 유저가 레이아웃 꼬임을 겪거나 시각적으로 불편함을 느끼게 됩니다. 유저 눈에 도화지가 완전히 안착한 직후에 부드러운 인터랙션을 주는 것이 정석입니다.
👨💻 안드로이드 개발자와의 비교 조각
안드로이드 개발을 해보신 분들은 액티비티(Activity)나 프래그먼트(Fragment)의 생명주기와 1:1로 매핑되는 정합성에 무릎을 탁 치실 겁니다.
- Layout을 인플레이트하고 객체를 생성하는 안드로이드의 onCreate()는 iOS의 viewDidLoad와 완벽히 상응합니다.
- 화면이 눈에 보이기 시작하는 onStart()와 onResume()의 전환 과정은 iOS의 viewWillAppear()와 viewDidAppear()의 거울상과 같습니다.
- 화면이 가려지며 리소스를 멈춰두는 onPause() -> onStop() 흐름 역시 iOS의 viewWillDisappear() -> viewDidDisappear()와 정확하게 매칭됩니다.
이처럼 모바일 디바이스라는 제한된 RAM 메모리와 배터리 환경에서 **"유저가 안 보는 화면의 리소스를 최대한 아껴두겠다"**는 하드웨어 제어 철학은 구글과 애플 모두 일치합니다.
🎯 오늘의 요약
- viewDidLoad는 화면이 메모리에 등록될 때 단 한 번 호출되므로 초기 설정 및 1회성 데이터 통신에 적합하다.
- 다른 화면에서 돌아왔을 때 화면을 최신 상태로 갱신하려면 반드시 viewWillAppear를 활용한다.
- 키보드를 올리거나 애니메이션을 가동하는 등 비주얼 요소의 시작점은 viewDidAppear가 안전하다.
생명주기를 온전히 이해하고 다루는 시점부터, 화면 뒤에서 조용히 흘러가는 리소스의 낭비를 막고 매끄러운 UX를 만드는 진짜 개발자로 거듭나게 됩니다.
다음 장 [iOS 5-3]에서는 이렇게 움직이는 화면들 사이에서, 앞뒤로 안전하게 실시간 데이터를 핑퐁하며 주고받는 3가지 데이터 전달 패턴(Property, Delegate, NotificationCenter)을 실제 동작 코드로 아주 쉽게 격파해 보겠습니다. 생각의 깊이를 높이며 다음 조각으로 넘어가 봅시다!