프로퍼티(Property)

Little_Machine_Human_·2025년 2월 10일

Swift 프로퍼티와 프로퍼티 래퍼 완벽 가이드

목차

  1. 기본 프로퍼티
  2. SwiftUI 프로퍼티 래퍼
  3. 실전 활용 예제
  4. 프로퍼티 선택 가이드

기본 프로퍼티

1. 저장 프로퍼티 (Stored Properties)

가장 기본적인 형태의 프로퍼티로, 값을 직접 저장하는 인스턴스 프로퍼티입니다.

class Person {
    var name: String        // 변수 저장 프로퍼티
    let birthDate: Date     // 상수 저장 프로퍼티
    
    init(name: String, birthDate: Date) {
        self.name = name
        self.birthDate = birthDate
    }
}

2. 연산 프로퍼티 (Computed Properties)

값을 저장하지 않고 계산된 값을 반환하는 프로퍼티입니다.

struct Circle {
    var radius: Double
    
    var area: Double {
        get {
            return .pi * radius * radius
        }
        set {
            radius = sqrt(newValue / .pi)
        }
    }
}

3. 타입 프로퍼티 (Type Properties)

인스턴스가 아닌 타입 자체에 속하는 프로퍼티입니다.

struct Math {
    static let pi = 3.141592653589793
    static var computedProperty: Int {
        return 42
    }
}

4. 지연 저장 프로퍼티 (Lazy Stored Properties)

필요한 시점에 초기화되는 프로퍼티입니다.

class ExpensiveDataManager {
    lazy var data: [String] = {
        // 복잡한 초기화 로직
        return ["Heavy", "Data", "Loading"]
    }()
}

5. 프로퍼티 옵저버 (Property Observers)

프로퍼티 값의 변화를 관찰하고 반응하는 기능을 제공합니다.

class StepCounter {
    var steps: Int = 0 {
        willSet {
            print("Will set steps to \(newValue)")
        }
        didSet {
            print("Steps changed from \(oldValue) to \(steps)")
        }
    }
}

SwiftUI 프로퍼티 래퍼

1. @State

View의 로컬 상태를 관리하는 프로퍼티 래퍼입니다.

struct ContentView: View {
    @State private var isPlaying = false
    
    var body: some View {
        Button(action: { isPlaying.toggle() }) {
            Text(isPlaying ? "Pause" : "Play")
        }
    }
}

2. @Binding

다른 View의 상태와 양방향 바인딩을 제공합니다.

struct ChildView: View {
    @Binding var text: String
    
    var body: some View {
        TextField("Enter text", text: $text)
    }
}

struct ParentView: View {
    @State private var text = ""
    
    var body: some View {
        ChildView(text: $text)
    }
}

3. @Observable (iOS 17+)

클래스의 상태 변화를 감지하고 UI를 업데이트합니다.

@Observable class UserProfile {
    var name = "Kim"
    var age = 25
    
    func updateAge() {
        age += 1
    }
}

4. @Environment

시스템 환경 값에 접근합니다.

struct ThemedText: View {
    @Environment(\.colorScheme) var colorScheme
    
    var body: some View {
        Text("Current theme: \(colorScheme == .dark ? "Dark" : "Light")")
            .foregroundColor(colorScheme == .dark ? .white : .black)
    }
}

5. @AppStorage

UserDefaults와 연동되어 데이터를 영구 저장합니다.

struct SettingsView: View {
    @AppStorage("isDarkMode") private var isDarkMode = false
    
    var body: some View {
        Toggle("Dark Mode", isOn: $isDarkMode)
    }
}

6. @SceneStorage

현재 scene의 상태를 저장합니다.

struct DocumentView: View {
    @SceneStorage("selectedTab") private var selectedTab = 0
    
    var body: some View {
        TabView(selection: $selectedTab) {
            Text("Tab 1").tag(0)
            Text("Tab 2").tag(1)
        }
    }
}

7. @FetchRequest

Core Data 쿼리 결과를 관리합니다.

struct TodoListView: View {
    @FetchRequest(
        sortDescriptors: [SortDescriptor(\Todo.date, order: .reverse)],
        animation: .default)
    private var todos: FetchedResults<Todo>
    
    var body: some View {
        List(todos) { todo in
            Text(todo.title ?? "Untitled")
        }
    }
}

8. @StateObject

ObservableObject의 수명주기를 관리합니다.

class DataModel: ObservableObject {
    @Published var value = 0
}

struct DataView: View {
    @StateObject private var model = DataModel()
    
    var body: some View {
        Text("Value: \(model.value)")
    }
}

실전 활용 예제

사용자 프로필 관리 예제

@Observable class UserProfile {
    var name: String
    var email: String
    var preferences: Preferences
    
    struct Preferences {
        var isDarkMode: Bool
        var notificationsEnabled: Bool
    }
    
    init(name: String, email: String, preferences: Preferences) {
        self.name = name
        self.email = email
        self.preferences = preferences
    }
}

struct ProfileView: View {
    @State private var profile: UserProfile
    @AppStorage("lastLoginDate") private var lastLoginDate: Date?
    
    var body: some View {
        Form {
            Section("Personal Info") {
                TextField("Name", text: $profile.name)
                TextField("Email", text: $profile.email)
            }
            
            Section("Preferences") {
                Toggle("Dark Mode", isOn: $profile.preferences.isDarkMode)
                Toggle("Notifications", isOn: $profile.preferences.notificationsEnabled)
            }
            
            if let date = lastLoginDate {
                Section("Last Login") {
                    Text(date, style: .date)
                }
            }
        }
    }
}

프로퍼티 선택 가이드

언제 어떤 프로퍼티를 사용해야 할까요?

  1. @State 사용 시기

    • View 내부에서만 사용되는 간단한 데이터
    • 해당 View의 생명주기와 함께하는 데이터
    • 예: 토글 상태, 텍스트 필드 입력값
  2. @Binding 사용 시기

    • 부모 View의 상태를 자식 View에서 수정해야 할 때
    • 양방향 데이터 바인딩이 필요할 때
    • 예: 커스텀 컴포넌트의 상태 관리
  3. @Observable 사용 시기

    • 복잡한 데이터 모델이 필요할 때
    • 여러 View에서 공유되는 데이터
    • 예: 사용자 프로필, 앱 설정
  4. @AppStorage 사용 시기

    • 앱 재시작 후에도 유지되어야 하는 데이터
    • 간단한 사용자 설정
    • 예: 테마 설정, 사용자 기본 설정
  5. @Environment 사용 시기

    • 시스템 설정에 접근해야 할 때
    • 전역적으로 공유되는 환경 값이 필요할 때
    • 예: 다크 모드 감지, 디바이스 방향

주의사항과 베스트 프랙티스

  1. 성능 최적화

    • @State는 가능한 private으로 선언
    • 불필요한 View 업데이트 방지를 위해 적절한 범위 설정
    • 큰 데이터는 참조 타입으로 관리
  2. 메모리 관리

    • @StateObject는 View의 생명주기 동안 한 번만 생성
    • lazy var를 활용하여 필요할 때만 초기화
    • 큰 데이터는 필요한 시점에 로드
  3. 코드 구조화

    • 관련 프로퍼티들을 논리적으로 그룹화
    • 명확한 네이밍 컨벤션 사용
    • 문서화 주석 활용

결론

Swift의 프로퍼티 시스템은 강력하고 유연합니다. 기본 프로퍼티부터 SwiftUI의 다양한 프로퍼티 래퍼까지, 각각의 용도에 맞게 적절히 선택하여 사용하면 효율적인 앱 개발이 가능합니다. 특히 SwiftUI에서는 프로퍼티 래퍼를 통해 상태 관리를 더욱 쉽고 직관적으로 할 수 있습니다.

이 가이드를 통해 다양한 프로퍼티의 특성과 활용법을 이해하고, 실제 프로젝트에서 적절히 활용하시기 바랍니다.

profile
while(true){ 가족(); 건강(); 자기개발(); }

0개의 댓글