SwiftUI는 2019년 iOS 13과 함께 공개된 Apple의 선언형 UI 프레임워크다. 화면을 "어떻게 바꿀지"의 절차가 아니라 "지금 상태에서 어떤 모습이어야 하는지"를 기술하면, 프레임워크가 상태 변화와 화면 갱신을 연결한다. 문제는 이 편의가 공짜가 아니라는 점이다. 어떤 API를 쓸 수 있는지는 최소 배포 버전이 결정하고, 어떤 뷰가 갱신되는지는 프로퍼티 래퍼 선택이 결정한다. 여기서는 공식 문서에 명시된 사양과 제약을 기준으로 시작 단계에서 반드시 확인해야 할 것들을 정리한다.

선언형 UI의 기본 구조

View 프로토콜과 body

SwiftUI의 모든 화면 요소는 View 프로토콜을 채택한 구조체다. 클래스가 아니라 값 타입이며, 필수 요구사항은 연관 타입 Body를 반환하는 body 계산 프로퍼티 하나뿐이다. body에는 @ViewBuilder가 적용되어 있어 여러 뷰를 나열하면 자동으로 하나의 합성 타입으로 묶인다. 이 때문에 뷰의 실제 타입은 중첩이 깊어질수록 길어지고, 타입 검사 부담도 함께 커진다. 하나의 body에 수십 줄을 몰아넣으면 컴파일 시간이 급격히 늘어나거나 타입 추론 실패 메시지를 만나게 되는데, 이는 버그가 아니라 구조상 예상되는 결과다. 공식 문서가 권하는 대응은 단순하다. 뷰를 잘게 나누고, 반복되는 묶음은 별도 타입으로 추출한다.

앱 진입점과 Scene

iOS 14부터 App 프로토콜과 @main 속성으로 앱 진입점 전체를 SwiftUI로 작성할 수 있다. WindowGroup 같은 Scene이 최상위 컨테이너 역할을 하고, 그 안에 루트 뷰를 넣는다. iOS 13만 지원하는 프로젝트라면 이 방식을 쓸 수 없고 UIHostingController를 UIKit 앱 델리게이트에 얹는 형태가 된다. 즉 같은 SwiftUI라도 최소 지원 버전에 따라 앱의 골격 자체가 달라진다.

레이아웃 협상 규칙

SwiftUI의 레이아웃은 부모가 크기를 제안하고, 자식이 그 제안 안에서 자신의 크기를 스스로 정하고, 부모가 그 결과를 배치하는 3단계로 동작한다. 자식이 부모의 제안을 반드시 따르지 않는다는 점이 핵심이다. frame 수정자를 걸었는데 의도대로 되지 않는 대부분의 상황은 이 협상 순서를 오해한 결과다. 오토레이아웃처럼 제약을 선언하고 해결기를 신뢰하는 모델이 아니라는 점을 초반에 잡고 가야 한다.

상태 관리 — 프로퍼티 래퍼 선택 기준

소유와 참조의 구분

상태 관련 실수는 대부분 "이 값을 누가 소유하는가"를 잘못 정한 데서 나온다. 값을 직접 소유하는 뷰는 @State, 상위가 소유한 값을 읽고 쓰기만 하는 뷰는 @Binding을 쓴다. 참조 타입도 같은 원칙이 적용되는데, iOS 14에서 추가된 @StateObject는 뷰 갱신 사이에도 인스턴스 수명을 유지하지만 @ObservedObject는 그렇지 않다. 객체를 생성하는 위치에 @ObservedObject를 쓰면 상위 뷰가 다시 평가될 때마다 객체가 새로 만들어져 상태가 초기화된다. 화면이 이유 없이 리셋되는 증상의 전형적인 원인이다.

환경을 통한 전달

중간 뷰들을 거치지 않고 값을 내려보내야 할 때 @Environment@EnvironmentObject를 쓴다. 다만 @EnvironmentObject는 주입되지 않은 상태로 접근하면 런타임에 중단된다. 컴파일 타임 보장이 없다는 것이 명시된 제약이므로, 프리뷰나 테스트용 뷰를 만들 때 주입 누락을 반드시 확인해야 한다.

Observation 도입 이후

iOS 17부터는 Observation 프레임워크의 @Observable 매크로를 쓸 수 있고, 이 경우 ObservableObject·@Published·@StateObject 조합 대신 일반 프로퍼티 접근만으로 추적이 이루어진다. 실제로 접근한 프로퍼티만 추적하므로 불필요한 갱신이 줄어든다. 다만 iOS 16 이하를 지원해야 한다면 사용할 수 없다. 상태 관리 방식 선택은 취향이 아니라 배포 타깃이 먼저 결정한다.

시작 전 확인해야 할 제약

최소 배포 버전이 곧 API 범위

SwiftUI 학습에서 가장 자주 부딪히는 벽은 문법이 아니라 가용성이다. 튜토리얼 코드가 그대로 컴파일되지 않는다면 대개 그 API의 최소 버전이 프로젝트 설정보다 높기 때문이다. 주요 API의 도입 시점은 다음과 같다.

기능최소 iOS 버전비고
기본 뷰(List, TabView, NavigationView)iOS 13NavigationView는 이후 대체됨
App 프로토콜 기반 라이프사이클, @StateObjectiOS 14iOS 13은 UIHostingController 필요
.task 수정자, .searchableiOS 15async/await 연동
NavigationStack, NavigationSplitView, Layout 프로토콜, Swift ChartsiOS 16내비게이션 모델 전면 변경
@Observable(Observation), SwiftDataiOS 17상태·영속성 방식 변경

내비게이션은 특히 주의해야 한다. iOS 15 이하를 지원해야 하면 NavigationStack을 쓸 수 없어 구형 API와 신형 API를 if #available로 갈라 관리해야 하고, 두 경로의 동작 차이를 따로 검증해야 한다. 지원 범위를 한 단계 낮추는 결정이 코드량과 테스트 범위를 함께 늘린다는 뜻이다.

개발 환경 요건

Apple 개발의 전제 조건은 도구 쪽에도 있다. Xcode는 macOS에서만 동작하고, 프리뷰와 시뮬레이터도 여기에 묶여 있다. 사용 가능한 SDK와 시뮬레이터 런타임은 설치된 Xcode 버전이 정하므로, 최신 OS의 새 API를 쓰려면 Xcode부터 올려야 한다. 실기기 실행과 App Store 배포에는 Apple Developer Program 관련 요건이 따르며, 무료 개인 팀으로도 기기 실행은 가능하지만 유효 기간과 앱 수에 제한이 있다는 점이 공식 문서에 명시되어 있다. 요금과 정책은 변동될 수 있으므로 등록 전 개발자 사이트에서 직접 확인하는 편이 안전하다.

UIKit 상호운용

SwiftUI가 모든 것을 대체하지는 않는다. 세밀한 텍스트 편집 제어, 일부 카메라·미디어 인터페이스, 성숙한 서드파티 UI 라이브러리는 여전히 UIKit 기반인 경우가 많다. 이를 위해 UIViewRepresentableUIViewControllerRepresentable로 UIKit 컴포넌트를 감싸고, 반대 방향으로는 UIHostingController로 SwiftUI 뷰를 UIKit 화면에 넣는다. 양방향 통로가 공식 API로 제공된다는 사실은, 기존 iOS 앱을 한 번에 전환하지 않고 화면 단위로 점진 도입할 수 있다는 뜻이기도 하다.

SwiftUI, UIKit, 크로스플랫폼 비교

사양 비교

항목SwiftUIUIKit크로스플랫폼 계열
대상 플랫폼iOS·iPadOS·macOS·watchOS·tvOS·visionOSiOS·iPadOS 중심(Mac Catalyst)Apple 플랫폼 + Android 등
언어SwiftSwift·Objective-CDart·JavaScript·Kotlin 등
UI 작성 방식선언형, 상태 기반명령형, 뷰 계층 직접 조작대체로 선언형
최소 지원 OSiOS 13 이상(기능별 상향)사실상 제한 없음프레임워크 정책에 따름
새 OS 기능 대응OS SDK와 동시 제공OS SDK와 동시 제공브리지·플러그인 지원 대기
개발 환경macOS + Xcode 필수macOS + Xcode 필수배포 단계에서 macOS + Xcode 필요

선택 기준

초반에 자주 걸리는 지점

어떤 경우에 적합한가

SwiftUI로 시작하기 좋은 조건

다른 선택지를 함께 검토해야 하는 조건

정리하면 SwiftUI 학습의 출발점은 문법이 아니라 두 가지 확정이다. 지원할 최소 iOS 버전과, 각 상태를 누가 소유하는지. 이 둘이 정해지면 나머지 API 선택은 대체로 따라온다. 버전별 가용성과 동작 세부는 계속 갱신되므로, 판단 근거는 Apple 개발자 문서의 해당 심볼 페이지에 표시된 가용성 정보에서 확인하는 것이 정확하다.