SwiftUI: 개념, 주요 개념 및 View Protocol

저자: IT Sectr 게시일: 2026-04-30 읽는 시간: 8 분

SwiftUI는 Apple 생태계의 모든 플랫폼에서 사용자 인터페이스를 구축하기 위한 선언형 프레임워크입니다. 단계를 명령형으로 설명하는 대신, 개발자는 인터페이스가 어떻게 보여야 하는지 선언하고 SwiftUI가 렌더링과 업데이트를 관리합니다. Apple Developer Documentation (2025)에 따르면 SwiftUI는 iOS 15+, iPadOS 15+, macOS 12+, watchOS 8+ 및 tvOS 15+를 지원하며 모든 인터페이스 구성 요소의 기본 빌딩 블록으로 View Protocol을 사용합니다.

핵심 요약

  • SwiftUI는 Apple의 선언형 프레임워크로, 개발자가 인터페이스를 설명하면 업데이트가 자동으로 수행됩니다.
  • View Protocolbody 속성은 SwiftUI UI 구성 요소의 기초이며, 뷰 구성을 통해 화면 설명을 반환합니다.
  • Property Wrappers — @State, @Binding, @ObservedObject, @StateObject — 상태를 관리하고 데이터 변경 시 다시 그리기를 트리거합니다.
  • NavigationStack (iOS 16+)은 타입 안전한 라우트와 선언형 전환을 갖춘 최신 네비게이션 API입니다.
  • Modifier는 클래스 상속 없이 뷰의 모양과 동작을 사용자 정의하기 위한 호출 체인입니다.

SwiftUI란 무엇인가?

SwiftUI는 2019년 Apple이 새로운 프로젝트에서 UIKit을 대체하기 위해 도입한 선언형 프레임워크입니다. UIView 인스턴스를 수동으로 생성하고 계층 구조에 추가하는 대신, 개발자는 View 프로토콜을 준수하는 구조체를 통해 인터페이스를 설명합니다. SwiftUI는 현재 상태와 새 상태 간의 차이를 자동으로 계산하고 자체 렌더링 엔진을 사용하여 변경된 부분만 다시 그립니다.

프레임워크는 값 의미 체계(클래스가 아닌 구조체)를 사용하여 Swift로 작성되어 UI 구성 요소를 가볍고 스레드 안전하게 만듭니다. Objective-C 런타임으로 인해 UIViewController가 200+ 바이트가 될 수 있는 UIKit과 달리, SwiftUI View는 단지 몇 바이트 크기의 단순한 구조체입니다. 이는 메모리가 제한된 watchOS에서 특히 중요합니다.

SwiftUI 크로스 플랫폼

동일한 View 설명이 iPhone, iPad, Mac, Apple Watch, Apple TV 및 Apple Vision Pro에서 작동합니다. SwiftUI는 플랫폼에 따라 인터페이스를 조정합니다: iOS에서는 터치 제스처, macOS에서는 키보드 조합, watchOS에서는 Digital Crown 스크롤. 이는 여러 Apple 플랫폼에서 앱을 출시하는 회사의 개발 시간을 단축하지만 각 플랫폼별 요소에 대한 추가 구성이 필요합니다.

View Protocol 및 뷰 본문

SwiftUI에서 각 화면은 View 프로토콜을 구현하는 구조체로, 유일한 요구 사항은 some View 유형의 계산된 속성 body입니다. some 키워드(불투명 유형)는 구체적인 뷰 유형을 숨겨 SwiftUI가 렌더링을 최적화할 수 있게 합니다. body 내에서 개발자는 ViewBuilder를 사용하여 미리 만들어진 구성 요소(Text, Image, Button, List)를 결합하여 여러 뷰를 하나로 조립합니다.

swift
struct GreetingView: View {
    let name: String

    var var body: some View {
        VStack {
            Text("안녕하세요, \(name)!")
                .font(.title)
                .foregroundColor(.blue)
            Image(systemName: "hand.wave")
                .imageScale(.large)
        }
        .padding()
    }
}

예제에서 VStack(수직 스택)은 Text와 Image를 포함합니다. name 값은 구조체 초기화자를 통해 전달됩니다. 이것이 SwiftUI에서 외부 DI 컨테이너 없이 DI(의존성 주입)가 작동하는 방식입니다. 각 수정자는 원본을 변경하지 않고 변경 사항이 적용된 새 뷰를 반환합니다. 이는 값 유형의 불변성 덕분에 가능합니다.

ViewBuilder 및 조건문

ViewBuilder@resultBuilder로 주석이 달린 결과 빌더로, 최대 10개의 뷰를 하나로 조립합니다. body 내에서 추가 래퍼 없이 if/else, switch 및 ForEach를 사용할 수 있습니다. ForEach는 Identifiable 요소와 함께 작동하며, 삽입/삭제 시 올바른 애니메이션을 위해 각 뷰에 고유 ID가 할당됩니다.

상태 관리: @State, @Binding, @ObservedObject

SwiftUI에서 상태는 화면에 표시되는 내용을 결정합니다. 상태가 변경되면 SwiftUI는 의존하는 뷰의 body를 다시 만들고 diff 알고리즘을 사용하여 결과를 이전 결과와 비교합니다. 상태 저장을 위해 속성 래퍼가 사용되며, 각각 로컬 상태, 자식 뷰와의 연결 또는 외부 데이터 모델이라는 특정 작업을 해결합니다.

swift
struct CounterView: View {
    @State private var count = 0

    var var body: some View {
        VStack {
            Text("카운터: \(count)")
            Button("증가") {
                count += 1
            }
        }
    }
}

class UserViewModel: ObservableObject {
    @Published var name = ""
    @Published var age = 0
}

@State는 View 구조체 내에 로컬 단순 값(Int, String, Bool)을 저장합니다. SwiftUI는 구조체에서 메모리를 별도 저장소로 이동하므로 View가 값 유형이더라도 @State 속성을 변경할 수 있습니다. @ObservableObject@Published 속성이 있는 클래스를 위한 것으로, 해당 변경 사항이 SwiftUI에 다시 그리기 필요성을 자동으로 알립니다.

@Binding 및 부모-자식 연결

@Binding은 부모 뷰에 있는 데이터 소스와의 양방향 연결을 만듭니다. 부모는 $variable(투영 값)을 전달하고 자식은 바인딩을 통해 값을 읽고 씁니다. 이를 통해 부모에 상태를 유지하면서 텍스트 입력 또는 토글을 별도 구성 요소로 이동할 수 있습니다. @Binding이 없으면 각 변경마다 새 값을 위로 전달하는 콜백 클로저가 필요합니다.

iOS 16 이전에는 SwiftUI의 네비게이션이 NavigationView를 기반으로 구축되었습니다 — iPad에서 복잡한 동작(분할 보기, 이중 열)을 가진 레거시 API입니다. iOS 16부터 Apple은 NavigationStack을 권장합니다 — 타입 안전한 라우트를 갖춘 간소화된 대안입니다. 개발자가 가능한 라우트의 열거형을 정의하면 NavigationStack이 딥 링크 및 루트로 돌아가기를 지원하여 화면 스택을 자동으로 관리합니다.

swift
enum Route: Hashable {
    case detail(id: Int)
    case settings
}

struct ContentView: View {
    var var body: some View {
        NavigationStack {
            List {
                NavigationLink("세부 화면",
                               value: Route.detail(id: 42))
                NavigationLink("설정",
                               value: Route.settings)
            }
            .navigationDestination(for: Route.self) { route in
                switch route {
                case .detail(let id): DetailView(id: id)
                case .settings: SettingsView()
                }
            }
        }
    }
}

Hashable을 준수하는 라우트는 매개변수 전달에 모든 데이터 유형을 사용할 수 있습니다. navigationDestination(for:destination:)은 라우트 유형을 대상 뷰와 연결합니다. UIKit 네비게이션에 비해 장점은 새 라우트를 추가할 때 다시 그리기가 필요하지 않다는 것입니다 — 열거형에 케이스를 추가하고 switch에 핸들러를 추가하기만 하면 됩니다. 딥 링크는 NavigationStack의 processDeepLink를 통해 처리됩니다.

프로그래매틱 네비게이션

프로그래매틱 네비게이션(로그인, 타이머 또는 서버 응답 후)을 위해 NavigationLink 초기화자와 함께 @State가 사용됩니다: NavigationLink(isActive: $isActive). isActive = true로 설정하면 사용자 터치 없이 전환이 발생합니다. 다른 방법은 NavigationStack에서 $path 배열을 바인딩하는 것입니다: $path.append(Route.detail(id: 1)).

View Modifier — 모양 사용자 정의

Modifier는 뷰의 수정된 복사본을 반환하는 메서드입니다. 기존 뷰를 변경하여 속성을 구성하는 UIKit과 달리, SwiftUI는 변경 사항이 적용된 새 값을 만듭니다. 수정자 체이닝은 순차적 변환(글꼴 → 패딩 → 색상 → 그림자 → 제스처)을 통해 최종 인터페이스를 구축합니다.

Apple은 200개 이상의 내장 수정자를 제공합니다. 가장 일반적인 것: .font(), .foregroundColor(), .padding(), .background(), .cornerRadius(), .shadow(), .opacity(), .offset(). 수정자의 순서가 중요합니다: .padding() 전에 .background()를 사용하면 패딩이 포함된 영역이 채워지고, 이후에 사용하면 내부 영역만 채워집니다. 사용자 정의 수정자는 ViewModifier 프로토콜을 통해 생성됩니다.

조건부 수정자 및 애니메이션

수정자는 삼항 연산자를 통해 조건부로 적용할 수 있습니다: .foregroundColor(isError ? .red : .primary). 애니메이션을 위해 .animation(.easeInOut, value: state)가 사용됩니다 — 애니메이션 수정자는 특정 상태 속성에 바인딩됩니다. 이 속성이 변경되면 SwiftUI는 이전 값과 새 값 사이의 전환을 애니메이션화합니다. 애니메이션은 opacity, offset, scale, rotation, 크기 및 색상과 함께 작동하며 각 속성에는 해당하는 AnimatableParameter가 있습니다.

사용자 정의 애니메이션의 경우 .transition(나타나기/사라지기) 및 .matchedGeometryEffect(두 컨테이너 간 요소의 부드러운 전환)을 사용할 수 있습니다. 후자는 목록의 히어로 애니메이션에 사용됩니다: 목록 셀의 아이콘이 세부 화면에서 큰 이미지로 부드럽게 변환됩니다.

SwiftUI vs UIKit: 접근 방식 비교

SwiftUI와 UIKit 사이의 선택은 iOS 개발자의 첫 번째 딜레마 중 하나입니다. 두 프레임워크 모두 Apple에서 지원하지만 인터페이스 구축 문제를 근본적으로 다른 방식으로 해결합니다: SwiftUI는 선언형으로, UIKit은 명령형으로. 그 차이는 상태 관리, 네비게이션, 성능 및 호환성에서 나타납니다.

측면SwiftUIUIKit
접근 방식선언형: 무엇을 표시할지명령형: 어떻게 구축할지
상태Property Wrappers, 자동 다시 그리기수동: reloadData, setNeedsLayout
UI 코드간결함, 수정자 체인장황함, NSCoder/Storyboard/제약 조건
성능iOS 17+에서 높음, diff 알고리즘iOS 12–16에서 최고, 직접 제어
최소 버전iOS 15+ (완전 지원)iOS 2+ (모든 버전)

최소 iOS 17 버전의 새 프로젝트의 경우 Apple은 SwiftUI를 기본 프레임워크로 권장합니다. UIKit은 렌더링에 대한 세밀한 제어(사용자 정의 UICollectionViewLayout, 복잡한 CAAnimation 장면) 또는 iOS 12–14 지원이 필요한 인터페이스에 계속 필요합니다. 많은 프로젝트가 하이브리드 접근 방식을 사용합니다: UIHostingController를 통해 SwiftUI를 UIKit 앱에 임베드하고, UIViewRepresentable을 사용하여 SwiftUI 계층 내에서 UIKit 구성 요소를 사용할 수 있습니다.

자주 묻는 질문

같은 프로젝트에서 SwiftUI와 UIKit을 함께 사용할 수 있나요?

네, UIHostingController(SwiftUI in UIKit)와 UIViewRepresentable(UIKit in SwiftUI)을 통해 가능합니다. 이는 마이그레이션 중에 인기 있는 하이브리드 접근 방식입니다.

어떤 iOS 버전부터 SwiftUI 프로젝트를 시작해야 하나요?

iOS 17은 완전한 기능을 제공합니다: NavigationStack, Observation framework, Swift Charts. iOS 15는 프로덕션을 위한 최소 기준입니다.

SwiftUI가 가끔 인터페이스를 업데이트하지 않는 이유는 무엇인가요?

가장 일반적인 원인은 백그라운드 스레드에서 @Published 속성을 변경하는 것입니다. ObservableObject는 main actor에서 변경 사항을 전송해야 합니다: @MainActor class ViewModel.

SwiftUI에서 버튼 누름 디바운스를 처리하는 방법은?

Combine을 통해 .debounce를 사용하세요: Button.publisher(for: .tap) .debounce(for: .seconds(0.3), scheduler: RunLoop.main).

SwiftUI는 사용자 정의 제스처를 지원하나요?

네, Gesture 수정자를 통해 지원합니다: DragGesture, LongPressGesture, MagnificationGesture, RotationGesture. .simultaneousGesture()와 .sequenced()를 사용하여 결합하세요.

요약

  • SwiftUI는 Apple의 선언형 프레임워크로, 인터페이스가 상태 관리를 위한 속성 래퍼가 있는 View 구조체의 구성으로 설명됩니다.
  • View Protocol과 계산된 속성 body는 모든 뷰의 단일 진입점입니다. ViewBuilder는 추가 컨테이너 없이 최대 10개의 뷰를 하나로 조립합니다.
  • @State, @Binding@ObservedObject는 로컬 상태, 부모-자식 연결 및 외부 모델의 모든 데이터 관리 시나리오를 다룹니다.
  • NavigationStack은 타입 안전한 열거형 라우트로 NavigationView를 대체하고 딥 링크 및 프로그래매틱 네비게이션 지원을 추가했습니다.
  • Modifier는 상속 없이 호출 체인을 통해 뷰의 모양을 사용자 정의할 수 있는 핵심 SwiftUI 패턴입니다.
  • SwiftUI와 UIKit은 UIHostingController와 UIViewRepresentable을 통해 공존하며 점진적인 프로젝트 마이그레이션을 가능하게 합니다.
  • iOS 17+의 경우 Apple은 SwiftUI를 기본 프레임워크로 권장합니다. UIKit은 복잡한 사용자 정의 인터페이스 및 이전 버전 지원을 위해 남아 있습니다.

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

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

프로젝트 논의

더 읽어보기