SwiftUI 입문 가이드: Cross-Platform 개발자를 위한 iOS 앱 여정 시작하기
React Native, Flutter 개발자도 쉽게 시작할 수 있는 SwiftUI 입문 가이드! 선언형 UI, 상태 관리, 핵심 컴포넌트, 그리고 실전 To-Do 앱 예제까지, iOS 모바일 앱 개발의 첫걸음을 떼어보세요.
SwiftUI 입문 가이드: Cross-Platform 개발자를 위한 iOS 앱 여정 시작하기
최근 모바일 앱 개발 생태계는 React Native, Flutter와 같은 Cross-Platform 프레임워크의 약진과 함께 빠르게 변화하고 있습니다. 하지만 iOS 생태계의 핵심인 Swift와 SwiftUI는 여전히 강력한 매력을 지니고 있으며, 특히 Apple 생태계에 최적화된 앱을 만들고자 하는 개발자들에게 필수적인 선택지입니다. 이 글은 React Native나 Flutter 개발 경험이 있는 분들이 SwiftUI에 쉽게 입문하고, 효율적으로 iOS 앱 개발을 시작할 수 있도록 돕는 실용적인 가이드가 될 것입니다.
SwiftUI란 무엇인가요? 선언형 UI의 매력
SwiftUI는 Apple이 2019년 WWDC에서 발표한 최신 UI 프레임워크로, 모든 Apple 플랫폼(iOS, iPadOS, macOS, watchOS, tvOS)에서 선언형(Declarative) 방식으로 사용자 인터페이스를 구축할 수 있도록 설계되었습니다. 기존의 UIKit이 명령형(Imperative) 방식으로 UI를 구성했던 것과는 달리, SwiftUI는 "데이터가 이 상태일 때 UI는 이렇게 보여야 한다"고 선언하는 방식으로 작동합니다.
이러한 선언형 패러다임은 React나 Flutter와 같은 현대적인 UI 프레임워크에 익숙한 개발자들에게 매우 친숙하게 다가올 것입니다. 예를 들어, React의 JSX나 Flutter의 Widget Tree처럼, SwiftUI는 Swift 코드 자체로 UI 계층 구조를 직관적으로 표현합니다. 이는 코드의 가독성을 높이고, UI 상태 관리를 훨씬 단순하게 만듭니다.
// SwiftUI의 선언형 UI 예시
import SwiftUI
struct ContentView: View {
var body: some View {
VStack { // Vertical Stack
Text("Hello, SwiftUI!")
.font(.largeTitle)
.padding()
Button("Tap Me") {
print("Button tapped!")
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
}
위 코드에서 VStack, Text, Button은 모두 View 프로토콜을 준수하는 UI 컴포넌트들이며, .font, .padding, .background 등은 이들 View의 모양과 동작을 수정하는 Modifier입니다. 이처럼 SwiftUI는 적은 코드로도 강력하고 유연한 UI를 구축할 수 있게 해줍니다.
SwiftUI의 핵심 개념 살펴보기: View와 State
SwiftUI 앱 개발의 두 가지 핵심 기둥은 View와 State입니다.
View
SwiftUI에서 View는 화면에 보이는 모든 UI 요소를 나타내는 프로토콜입니다. 버튼, 텍스트, 이미지, 심지어 레이아웃 컨테이너(VStack, HStack 등)까지 모두 View입니다. 모든 View는 body라는 연산 프로퍼티를 가지며, 이 body는 해당 View가 어떻게 구성될지를 정의하는 다른 View들을 반환합니다. 이는 React의 render 함수나 Flutter의 build 함수와 유사하다고 볼 수 있습니다.
// View 프로토콜을 준수하는 구조체
import SwiftUI
struct MyCustomView: View {
var body: some View {
// 이 뷰가 화면에 어떻게 그려질지 정의합니다.
Text("커스텀 뷰입니다.")
.font(.headline)
.foregroundColor(.green)
}
}
State
State는 SwiftUI 앱의 동적인 데이터를 관리하는 핵심 메커니즘입니다. UI는 State에 따라 자동으로 업데이트됩니다. @State 프로퍼티 래퍼를 사용하면, SwiftUI는 해당 프로퍼티의 변경을 감지하고, 이 State를 사용하는 모든 View를 자동으로 다시 렌더링(re-render)하여 UI를 갱신합니다. 이는 React의 useState 훅이나 Flutter의 setState와 유사한 개념입니다.
import SwiftUI
struct CounterView: View {
@State private var count: Int = 0 // @State로 count 변수 선언
var body: some View {
VStack {
Text("현재 값: \(count)")
.font(.title)
Button("증가") {
count += 1 // count 값이 변경되면 Text 뷰가 자동으로 업데이트됩니다.
}
.padding()
.background(Color.purple)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
@State는 주로 단일 뷰 내에서만 사용되는 간단한 상태를 관리하는 데 적합합니다. 더 복잡하거나 여러 뷰에서 공유되는 상태 관리는 @Binding, @ObservedObject, @StateObject, @EnvironmentObject 등을 활용하게 됩니다.
기본 UI 컴포넌트와 레이아웃: Stack과 Modifier
SwiftUI는 다양한 빌트인 View와 강력한 레이아웃 시스템을 제공합니다.
기본 UI 컴포넌트 (Views)
-
Text: 텍스트를 표시합니다. -
Image: 이미지를 표시합니다. -
Button: 사용자 인터랙션을 위한 버튼입니다. -
TextField: 사용자 입력을 받습니다. -
Toggle: 스위치 형태의 온/오프 토글입니다. -
Slider: 슬라이더를 통해 값을 선택합니다. -
List: 스크롤 가능한 행 목록을 표시합니다. (Flutter의ListView.builder와 유사) -
NavigationView/NavigationStack: 화면 전환 및 내비게이션 바를 제공합니다.
레이아웃 컨테이너 (Stacks)
SwiftUI의 레이아웃은 주로 스택(Stack)을 기반으로 합니다.
-
VStack: 자식 뷰들을 세로로 정렬합니다. (Flutter의Column위젯과 유사) -
HStack: 자식 뷰들을 가로로 정렬합니다. (Flutter의Row위젯과 유사) -
ZStack: 자식 뷰들을 깊이 방향(Z축)으로 겹쳐서 정렬합니다. (Flutter의Stack위젯과 유사)
import SwiftUI
struct LayoutExampleView: View {
var body: some View {
VStack { // 전체를 세로로 정렬
Text("상단 제목")
.font(.headline)
HStack { // 가로로 정렬
Image(systemName: "star.fill")
.foregroundColor(.yellow)
Text("별점")
Spacer() // 남은 공간을 채워 왼쪽으로 정렬
Text("5.0")
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(5)
ZStack { // 겹쳐서 정렬
Rectangle()
.fill(Color.orange)
.frame(width: 100, height: 100)
Text("겹친 텍스트")
.foregroundColor(.white)
}
}
.padding()
}
}
Modifier
Modifier는 View의 모양, 동작, 레이아웃 등을 변경하는 메서드입니다. 체이닝(chaining) 방식으로 여러 Modifier를 연속해서 적용할 수 있어 매우 유연하고 가독성 높은 코드를 작성할 수 있습니다. 예: .font(), .padding(), .background(), .foregroundColor(), .frame(), .cornerRadius(), .shadow() 등. Modifier의 순서는 중요합니다. 예를 들어, padding을 적용한 후 background를 적용하면 padding 영역까지 background가 채워지지만, 순서를 바꾸면 background가 먼저 적용되고 그 안에 padding이 생깁니다.
데이터 흐름 관리: State, Binding, ObservableObject
SwiftUI에서 데이터는 단방향으로 흐르는 경향이 있으며, 다양한 프로퍼티 래퍼(Property Wrapper)를 통해 데이터의 소유권과 접근 방식을 명확히 합니다.
@State
앞서 설명했듯이, @State는 뷰 내부에서만 사용되는 간단한 값 타입(Int, String, Bool 등)의 상태를 관리합니다. 뷰가 생성될 때 초기화되며, 뷰의 생명 주기와 함께합니다.
@Binding
@Binding은 부모 뷰의 @State 변수를 자식 뷰에서 읽고 쓸 수 있도록 연결해줍니다. State의 소유권은 부모에게 있지만, 자식은 Binding을 통해 해당 State에 접근하여 변경할 수 있습니다. 이는 React의 props drilling을 피하기 위한 콜백 함수 전달이나, Flutter의 ValueNotifier를 통해 상태를 공유하는 것과 유사합니다.
import SwiftUI
struct ParentView: View {
@State private var parentValue: String = "Hello"
var body: some View {
VStack {
Text("Parent Value: \(parentValue)")
ChildView(childValue: $parentValue) // $를 사용하여 Binding 전달
}
}
}
struct ChildView: View {
@Binding var childValue: String // @Binding으로 받음
var body: some View {
TextField("입력하세요", text: $childValue)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
}
@ObservedObject와 ObservableObject
더 복잡하거나 여러 뷰에서 공유해야 하는 참조 타입(클래스)의 상태를 관리할 때 사용됩니다.
-
ObservableObject: 프로토콜을 준수하는 클래스는@Published프로퍼티 래퍼를 사용하여 변경될 때마다 구독자에게 알릴 수 있습니다. -
@ObservedObject:ObservableObject를 준수하는 클래스의 인스턴스를 뷰에서 구독할 때 사용됩니다. 해당 객체의@Published프로퍼티가 변경되면,ObservedObject를 사용하는 뷰는 자동으로 업데이트됩니다. 이는 MVVM(Model-View-ViewModel) 패턴에서 ViewModel을 구현하는 데 유용합니다.
import SwiftUI
// 1. ObservableObject를 준수하는 클래스 정의
class UserSettings: ObservableObject {관련 게시글
Kotlin Multiplatform으로 Cross-Platform 모바일 개발 가속화하기
Kotlin Multiplatform (KMP)을 활용한 크로스플랫폼 모바일 앱 개발 전략을 심층적으로 다룹니다. iOS와 Android에서 코드 재사용을 극대화하고, 네이티브 UI와 성능을 유지하는 방법을 알아보세요.
Kotlin Multiplatform (KMP): 크로스플랫폼 모바일 개발 심층 가이드
Kotlin Multiplatform (KMP)을 활용한 크로스플랫폼 모바일 앱 개발 가이드입니다. React Native, Flutter와 비교하며 KMP의 장점, 아키텍처, 실전 팁 및 코드 예제를 상세히 다룹니다.
SwiftUI Introduction: Building Modern iOS and Cross-Platform Apps
SwiftUI의 기본부터 심화 개념, 그리고 React Native, Flutter와 같은 크로스 플랫폼 프레임워크와의 비교를 통해 현대 모바일 앱 개발에 SwiftUI를 활용하는 방법을 상세히 다룹니다.