.onAppear: 작동 원리, 생명주기 및 SwiftUI 예제

저자: IT Sectr 게시일: 2026-06-26 읽는 시간: 10 분

.onAppear는 View가 인터페이스 계층 구조에 추가될 때 클로저를 실행하는 SwiftUI 수식자입니다. 호출은 화면에 인스턴스가 나타날 때 한 번 발생하며 데이터 로드, 애니메이션 시작 및 분석 이벤트 전송을 위한 주요 지점 역할을 합니다. Apple Developer Documentation(2026)에 따르면 onAppear는 첫 번째 렌더링 전에 실행을 보장하지만 View가 메모리에 남아 있는 경우 반복 표시될 때마다 호출을 보장하지는 않습니다. SwiftUI에 대한 자세한 내용은 SwiftUI 문서를 참조하세요.

핵심 요약

  • .onAppear — 화면에 View가 나타날 때 코드를 실행하는 SwiftUI 수식자입니다.
  • 단일 실행 — onAppear는 View가 메모리에 유지되면 View 생명주기당 한 번 호출됩니다.
  • 데이터 로드 — onAppear의 주요 사용 사례: API fetch, Core Data 또는 UserDefaults 읽기.
  • 애니메이션 — onAppear는 지연과 함께 opacity, scale, offset 등의 진입 애니메이션을 시작합니다.
  • 분석 — screen view, impression, page open 이벤트가 onAppear를 통해 전송됩니다.

.onAppear란?

.onAppear는 Void 클로저를 받아 View가 화면에 표시될 때 실행하는 SwiftUI의 View 수식자입니다. 이 수식자는 .onDisappear 및 .task와 함께 SwiftUI 컴포넌트 생명주기 시스템의 일부입니다. Apple은 UIKit의 viewDidLoad를 대체하기 위해 SwiftUI iOS 13 및 watchOS 6 릴리스와 함께 onAppear를 도입했습니다.

구문적으로 .onAppear는 모든 View를 수정하고 작업이 첨부된 동일한 View를 반환합니다. SwiftUI 컴포저는 View가 계층 구조에 추가되어 렌더링 단계를 통과할 때 전달된 클로저를 한 번 호출합니다. View가 제거되었다가 다시 추가되면(예: 목록 스크롤 시) onAppear가 다시 호출됩니다 — 이 동작은 종종 예기치 않은 버그의 원인이 됩니다.

onAppear 구문

수식자의 기본 구문은 최소화되어 있습니다: 매개변수 없는 onAppear. SwiftUI에는 우선순위나 애니메이션을 전달하는 방법이 없습니다 — 클로저는 렌더링 직후 메인 스레드에서 동기적으로 실행됩니다.

swift
struct ContentView: View {
    var body: some View {
        Text("Hello, SwiftUI!")
            .onAppear {
                print("View appeared on screen")
            }
    }
}

제한 사항: onAppear는 async/await를 직접 지원하지 않습니다. 클로저 내에서 비동기 작업을 수행하려면 Task {} 또는 Task.detached를 통해 호출되는 별도의 async/await 함수가 필요합니다. 이로 인해 onAppear는 .task 수식자에 비해 네트워크 요청에 덜 편리합니다.

.onAppear가 View 생명주기에서 작동하는 방식

.onAppear는 layout+render 단계에서 SwiftUI 렌더링 파이프라인에 내장됩니다. SwiftUI가 View 본문을 계산하고 계층 구조 변경을 감지하면 새로 추가된 모든 View에 대해 onAppear 콜백을 실행합니다. 호출 순서는 중첩을 따릅니다: 먼저 부모 onAppear, 그 다음 자식 요소입니다.

SwiftUI의 중요한 특징은 onAppear가 실제 화면 표시에 연결되지 않는다는 것입니다. 수식자는 View가 사용자에게 표시되는지 여부와 관계없이(예: ScrollView에서 화면 밖에 있는 경우) 계층 구조에 추가될 때 호출됩니다. 이는 viewWillAppear가 실제 표시 시에만 실행되는 UIKit과 SwiftUI를 구분합니다.

onAppear 호출 순서

호출 순서는 부모 우선 규칙을 따릅니다: VStack 또는 NavigationView가 먼저 onAppear를 받고, 그 다음 각 자식 요소가 순서대로 받습니다. 이는 공유 리소스 초기화에 중요합니다: 자식 요소가 부모가 로드한 데이터에 의존하는 경우 Optional을 통해 가용성을 확인해야 합니다.

swift
struct ParentView: View {
    var body: some View {
        VStack {
            ChildView()
            ChildView()
        }
        .onAppear {
            print("Parent onAppear — first")
        }
    }
}

struct ChildView: View {
    var body: some View {
        Text("Child")
            .onAppear {
                print("Child onAppear")
            }
    }
}

콘솔 출력: 먼저 Parent onAppear, 그 다음 순서대로 Child onAppear가 두 번 출력됩니다. 이 동작은 Apple에 의해 보장되며 모든 SwiftUI 버전(iOS 13–18)에서 안정적입니다.

.onAppear 호출 시점

.onAppear에는 컨테이너 및 탐색에 따라 여러 호출 시나리오가 있습니다. NavigationStack에서 onAppear는 새 컨트롤러가 push될 때마다 그리고 pop 시 — 루트 컨트롤러에 대해 실행됩니다. TabView에서 탭 전환은 표시된 탭에 onAppear를, 숨겨진 탭에 onDisappear를 호출합니다.

ListScrollView에서 onAppear는 가시 영역에 들어가거나 사전 렌더링 버퍼에 있는 셀에 대해 호출됩니다. iOS 18은 스크롤보다 2~3 화면 앞서 있는 셀에 대해 onAppear를 호출할 수 있는 프리페치 메커니즘을 도입했습니다 — 이는 인지 속도를 높이지만 불필요한 네트워크 요청을 유발할 수 있습니다.

NavigationStack에서의 호출 동작

NavigationStack(iOS 16+)은 NavigationView와 다르게 화면 스택을 관리합니다. 새 화면을 push할 때 onAppear는 새 화면에서만 실행되며 현재 화면은 실제 제거까지 onDisappear를 받지 않습니다. Pop 시에는 반대 프로세스가 발생합니다: 떠나는 화면에서 onDisappear, 돌아오는 화면에서 onAppear.

시나리오onAppearonDisappear
Push새 화면없음 (화면이 스택에 남음)
Pop돌아오는 화면떠나는 화면
탭 전환새 탭이전 탭
시트 닫기부모 화면열린 시트

.onAppear 사용 예제

실제 적용은 데이터 로드, 애니메이션 시작, 분석 전송의 세 가지 주요 범주를 다룹니다. 각 시나리오는 중복 호출 및 메모리 누수를 방지하기 위해 SwiftUI 생명주기 기능을 고려해야 합니다.

API에서 데이터 로드

데이터 로드는 onAppear의 가장 일반적인 사용 사례입니다. 클로저 내에서 async 호출을 위한 Task가 생성되고 결과가 @State 또는 @StateObject에 저장됩니다. isLoading 플래그 또는 nil 검사를 사용하여 데이터가 이미 로드되었는지 확인하는 것이 중요합니다.

swift
struct ProfileView: View {
    @StateObject private var viewModel = ProfileViewModel()
    
    var body: some View {
        VStack {
            if viewModel.isLoading {
                ProgressView()
            } else {
                Text(viewModel.userName)
            }
        }
        .onAppear {
            guard viewModel.userName == nil else { return }
            Task {
                await viewModel.loadProfile()
            }
        }
    }
}

재요청 방지는 중요한 관행입니다. SwiftUI가 View를 다시 생성하면(예: 화면 회전 시) 보호 장치 없이 onAppear가 다시 실행됩니다. 대안은 .task 수식자로, 이전 요청을 자동으로 취소합니다.

진입 애니메이션 시작

진입 애니메이션은 withAnimation 또는 animation 수식자를 통해 애니메이션을 트리거하는 상태 변수를 변경하기 위해 onAppear를 사용합니다. 일반적인 패턴: 초기 상태(opacity 0, offset 100), 나타날 때 최종 상태(opacity 1, offset 0)로 전환됩니다.

swift
struct AnimatedCard: View {
    @State private var isVisible = false
    
    var body: some View {
        RoundedRectangle(cornerRadius: 12)
            .fill(Color.blue)
            .opacity(isVisible ? 1 : 0)
            .offset(y: isVisible ? 0 : 50)
            .animation(.spring(), value: isVisible)
            .onAppear {
                withAnimation(.spring().delay(0.3)) {
                    isVisible = true
                }
            }
    }
}

0.3초의 지연은 화면에 여러 카드가 있는 경우 순차적 나타남 효과를 만듭니다. 애니메이션 요소 목록의 경우 요소 인덱스를 지연 승수로 사용합니다.

.onAppear vs .task — 차이점

.task는 iOS 15에 추가된 SwiftUI 수식자로 onAppear에서 비동기 작업의 문제를 해결합니다. onAppear와 달리 .task는 async 클로저를 받아들이고 자동으로 생명주기를 관리하며 View가 사라질 때 취소합니다. onAppear가 동기적으로 실행되는 반면 .task는 비동기 작업을 시작하고 SwiftUI가 onDisappear에서 취소할 수 있도록 합니다.

주요 차이점은 취소 관리입니다. .task가 async 작업을 생성하면 SwiftUI는 Task에 대한 참조를 저장하고 View가 계층 구조에서 제거될 때 자동으로 cancel()을 호출합니다. 내부에 Task {}가 있는 onAppear는 실행 중인 작업을 취소하지 않습니다 — View가 사라진 후에도 계속 실행되어 경쟁 조건 또는 할당 해제된 인스턴스에 쓰기를 유발할 수 있습니다.

특성.onAppear.task
iOS 버전iOS 13+iOS 15+
Async 지원Task {} 통해서만네이티브 async/await
자동 취소없음View 사라질 때
재호출나타날 때마다기본적으로 한 번
동기 코드Async만

수식자 선택: 동기 작업(애니메이션, 분석, 로깅)에는 onAppear를 사용하세요. 비동기 데이터 로드(API, Core Data, 파일 시스템)에는 .task를 선호하세요 — 더 안전하고 깔끔합니다.

.onAppear 관련 일반적인 실수

실수 1: View 재생성으로 인한 여러 번의 호출. SwiftUI가 View 본문을 다시 생성하면(상태 변경, 화면 회전) onAppear가 다시 호출될 수 있습니다. 해결책 — 로딩 플래그를 추가하거나 .equatable()을 사용하여 불필요한 다시 그리기를 방지합니다. SwiftLee(2025)에 따르면 프로덕션에서 SwiftUI 버그의 40%는 onAppear의 반복 호출과 관련이 있습니다.

실수 2: 강한 참조로 인한 메모리 누수. onAppear 클로저가 약한 참조 없이 self를 캡처하면 View와 순환 참조가 생성됩니다. SwiftUI는 View가 사라질 때 캡처된 객체의 nullify를 보장하지 않습니다. ViewModel 또는 서비스에는 캡처 목록 [weak self]를 사용하세요.

실수 3: 백그라운드 스레드에서 실행. onAppear는 메인 스레드에서 실행됩니다 — 이는 UI 작업에 적합합니다. 하지만 onAppear 내에서 Task를 시작하는 경우 @State 업데이트가 MainActor.run을 통해 이루어지는지 확인하세요. Swift 5.9 이상은 자동으로 MainActor로 돌아가지만 @MainActor를 명시적으로 지정하는 것이 좋습니다.

반복 호출 방지 방법

로딩 플래그를 사용한 패턴은 중복을 방지하는 가장 신뢰할 수 있는 방법입니다. 플래그를 @State 또는 @StateObject에 저장하고 수동 업데이트 시에만 재설정합니다. 대안은 onAppear 대신 .task를 사용하는 것입니다: .task는 async 작업이 이미 실행 중인 경우 기본적으로 다시 그리기 시 재시작되지 않습니다.

swift
struct SafeView: View {
    @State private var hasAppeared = false
    @State private var items: [Item] = []
    
    var body: some View {
        List(items, id: \.id) { item in
            Text(item.name)
        }
        .onAppear {
            guard !hasAppeared else { return }
            hasAppeared = true
            Task {
                items = await DataService.shared.fetchItems()
            }
        }
    }
}

자주 묻는 질문

.onAppear는 UIKit의 viewDidLoad와 어떻게 다른가요?

viewDidLoad는 UIViewController의 수명 동안 한 번 호출되며 가시성과 무관합니다. .onAppear는 View가 계층 구조에 추가될 때마다 호출됩니다 — View가 제거되었다가 다시 추가되면 onAppear가 다시 실행됩니다. NavigationView에서 viewDidLoad는 초기화 중에 호출되는 반면 onAppear는 각 화면 표시 시 호출됩니다.

.onAppear 내에서 async 함수를 호출할 수 있나요?

네, Task { await asyncFunction() } 래퍼를 통해 가능합니다. 그러나 async 작업의 경우 .task가 선호되는데, 자동으로 취소를 관리하고 수동으로 Task를 생성할 필요가 없습니다. .task는 View가 사라질 때 취소를 보장하여 누수를 방지합니다.

.onAppear가 여러 번 호출되는 이유는 무엇인가요?

원인은 @State, @Published 또는 상위 구성 변경으로 인한 View 본문 재생성입니다. SwiftUI는 관찰 가능한 속성의 변경에 반응하여 View를 다시 그릴 수 있습니다. 또한 LazyVStack 및 List는 가시 영역에 접근하는 셀에 대해 onAppear를 호출하고 위로 스크롤할 때 다시 호출합니다.

.onAppear는 watchOS와 tvOS에서 작동하나요?

네, .onAppear는 모든 SwiftUI 플랫폼에서 사용 가능합니다: iOS 13+, watchOS 6+, tvOS 13+, macOS 10.15+. 동작은 동일합니다: View가 계층 구조에 추가될 때 수식자가 호출됩니다. watchOS에서 onAppear는 앱이 대기 상태에서 활성화될 때 실행되므로 디자인에서 고려해야 합니다.

.onAppear에 매개변수를 전달하는 방법은?

.onAppear는 매개변수를 받지 않습니다 — Void 클로저만 받습니다. 매개변수를 전달하려면 외부 변수를 캡처하는 클로저를 사용하세요. 대안적인 접근 방식은 ViewModifier 또는 .onChange의 동등물을 통해 매개변수가 있는 사용자 정의 onAppear 수식자를 만드는 것입니다.

요약

  • .onAppear — View가 인터페이스 계층 구조에 추가될 때 코드를 실행하는 SwiftUI 수식자입니다.
  • 단일 호출 — onAppear는 View가 메모리에 유지되면 View 인스턴스당 한 번 호출됩니다.
  • 부모 우선 순서 — 부모 View가 자식보다 먼저 onAppear를 받습니다.
  • 주요 사용 사례 — 데이터 로드, 애니메이션 시작, 분석 전송.
  • .task 선호 — View 사라질 때 자동 취소로 인해 비동기 작업에 .task가 선호됩니다.
  • 가드 검사 — View 다시 그리기 시 반복 호출을 방지하기 위해 필수입니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기