SwiftUI의 @AppStorage — 개념, UserDefaults 및 설정 저장

저자: IT Sectr 게시일: 2026-06-25 읽는 시간: 7 분

SwiftUI의 @AppStorage는 UserDefaults와 함께 작동하는 property wrapper로, 값을 UI와 자동으로 동기화합니다. @AppStorage로 선언된 프로퍼티가 변경되면 새 값이 즉시 UserDefaults에 저장되고, UserDefaults가 외부(위젯 또는 확장 기능)에서 변경되면 View가 자동으로 다시 그려집니다. Apple Developer Documentation (2025)에 따르면 @AppStorage는 String, Int, Double, Bool, Data, URL 및 선택적 버전을 지원하여 수동 관찰 코드 없이 사용자 설정의 반응형 저장을 제공합니다.

핵심 사항

  • @AppStorage — SwiftUI에서 UserDefaults와 반응형으로 작업하기 위한 property wrapper
  • 자동 저장 — 변경 시마다 값이 UserDefaults에 기록됩니다
  • 자동 UI 업데이트 — 모든 소스에서 UserDefaults가 변경되면 View가 다시 그려집니다
  • 지원되는 유형: String, Int, Double, Bool, Data, URL 및 선택적 버전
  • 기본값은 선언 시 설정되며 첫 실행 시 사용됩니다

SwiftUI에서 @AppStorage란?

@AppStorage는 Apple이 iOS 14에서 도입한 property wrapper로, View 프로퍼티를 UserDefaults의 키에 바인딩합니다. 프로퍼티를 읽을 때 SwiftUI는 지정된 키로 UserDefaults에서 값을 로드합니다. 쓸 때는 새 값을 저장하고 View에 다시 그려야 함을 알립니다.

@AppStorage 이전에는 개발자가 onAppear에서 수동으로 UserDefaults를 읽고, UserDefaults.didChangeNotification을 구독하고, 변경 시 @State를 업데이트해야 했습니다. @AppStorage는 전체 사이클을 자동화합니다. 한 줄의 선언으로 15-20줄의 보일러플레이트 코드를 대체합니다. 또한 @AppStorage는 양방향 동기화를 제공합니다. 다른 프로세스(App Extension 또는 Widget)에서 UserDefaults 값이 변경되어도 View는 계속 업데이트를 받습니다.

아키텍처적으로 @AppStorage는 DynamicProperty로 구현되어 SwiftUI가 종속성을 추적하고 관찰된 값이 변경될 때 View를 다시 그릴 수 있게 합니다. 이는 사용자 설정(인터페이스 언어, 기능 활성화/비활성화, 마지막 선택 탭, 사용자 이름)을 저장하는 데 이상적입니다.

@AppStorage vs UserDefaults: 비교

@AppStorage는 내부적으로 UserDefaults를 사용하지만 저장소 작업 방식은 근본적으로 다릅니다. UserDefaults는 읽기, 쓰기 및 변경 알림의 수동 관리가 필요한 저수준 API입니다. @AppStorage는 즉시 사용 가능한 반응형 동작을 제공하는 SwiftUI 추상화입니다.

UserDefaults는 일회성 작업(앱 시작 시 설정 로드, 분석 데이터 쓰기, 토큰 캐싱)에 적합합니다. @AppStorage는 UI를 반응형으로 업데이트해야 하는 설정(테마 전환, 언어 선택, 인터페이스 상태 저장)을 위한 것입니다. View 내에서 UserDefaults를 직접 사용하는 것은 안티패턴입니다. 추가 구독 없이는 View가 변경 사항을 알 수 없기 때문입니다.

매개변수@AppStorageUserDefaults
반응성자동알림 구독 필요
보일러플레이트프로퍼티당 1줄프로퍼티당 15-20줄
유형String, Int, Double, Bool, Data, URL모든 유형 + 아카이브 객체
사용자 정의 유형RawRepresentable 통해NSKeyedArchiver 통해
App Extension자동 동기화수동 구독

반응형 UI가 있는 간단한 설정의 경우 @AppStorage가 선호되는 선택입니다. 복잡한 데이터(배열, 딕셔너리, 사용자 정의 객체)의 경우 UserDefaults를 @State 및 수동 변경 구독과 조합하여 사용하거나 구조화된 저장을 위해 SwiftData / Core Data로 전환하세요.

지원되는 데이터 유형

@AppStorage는 UserDefaults가 직접 직렬화할 수 있는 표준 유형을 지원합니다: String, Int, Double, Bool, Data, URL. 각 유형마다 선택적 버전(String?, Int?, Double?, Bool?, Data?, URL?)이 있어 "설정되지 않음"과 "빈 값"을 구분할 수 있습니다.

RawRepresentable 프로토콜을 준수하는 사용자 정의 유형을 저장하는 경우 @AppStorage도 자동으로 작동합니다. enum의 rawValue가 String 또는 Int 유형이면 직접 사용할 수 있습니다: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI는 rawValue를 통해 값을 자동으로 직렬화/역직렬화합니다.

swift
enum AppTheme: String {
    case system, light, dark
}

struct SettingsView: View {
    @AppStorage("username") var username: String = "Guest"
    @AppStorage("launchCount") var launchCount: Int = 0
    @AppStorage("isDarkMode") var isDarkMode: Bool = false
    @AppStorage("appTheme") var theme: AppTheme = .system
    @AppStorage("lastOpened") var lastOpened: Date? = nil

    var body: some View {
        Form {
            TextField("Username", text: $username)
            Toggle("Dark mode", isOn: $isDarkMode)
            Text("\(launchCount)회 실행됨")
        }
    }
}

예제에서는 다양한 @AppStorage 유형을 사용합니다: 기본값 "Guest"의 String, 실행 카운터의 Int, 다크 테마의 Bool, String 유형의 rawValue를 가진 AppTheme enum, 마지막 열기 시간의 선택적 Date?. 각 프로퍼티는 첫 번째 인수로 지정된 UserDefaults 키에 바인딩됩니다. 첫 실행 시 키가 저장소에 없으면 기본값이 사용됩니다.

저장소 변경 관찰

@AppStorage의 주요 장점 중 하나는 모든 소스에서 UserDefaults 변경을 자동으로 관찰한다는 것입니다. App Extension 또는 Widget이 값을 변경하면 부모 앱의 @AppStorage가 알림을 받고 View를 다시 그립니다. 이는 @AppStorage가 UserDefaults.didChangeNotification에 자동으로 설정하는 KVO(Key-Value Observing) 메커니즘을 통해 달성됩니다.

실제로 이는 사용자가 Widget에서 설정을 변경하면(예: 다크 테마 활성화) 앱이 즉시 변경 사항을 감지함을 의미합니다. 메인 앱과 Share Extension, Watch App, Today Widget 간에도 동일한 동기화가 작동합니다. 개발자는 프로세스 간 데이터 교환 코드를 작성할 필요가 없습니다 — @AppStorage가 자동으로 처리합니다.

swift
struct ThemeSettingView: View {
    @AppStorage("isDarkMode") var isDarkMode: Bool = false

    var body: some View {
        VStack {
            Toggle("Dark Mode", isOn: $isDarkMode)
                .onChange(of: isDarkMode) { oldValue, newValue in
                    print("다크 모드가 \(newValue)(으)로 변경됨")
                }
        }
    }
}

Toggle은 @AppStorage를 통해 $isDarkMode에 바인딩됩니다. 전환 시 값이 "isDarkMode" 키로 UserDefaults에 자동 저장됩니다. .onChange 수정자를 사용하면 변경 시 부수 효과를 수행할 수 있습니다(예: 분석 데이터 보내기 또는 다른 화면의 UI 업데이트). Widget이 동일한 키를 변경하면 @AppStorage도 onChange를 트리거하여 상태 일관성을 보장합니다.

@AppStorage 코드 예제

모든 구성을 저장하기 위해 @AppStorage를 사용하는 완전한 앱 설정 화면을 살펴보겠습니다. 양식에는 다양한 유형의 설정 섹션(텍스트 필드, 토글, 카운터)이 포함되어 있으며 모든 값이 자동으로 UserDefaults에 저장됩니다.

swift
struct AppSettingsView: View {
    @AppStorage("displayName") var displayName = ""
    @AppStorage("notificationsEnabled") var notificationsEnabled = true
    @AppStorage("maxResults") var maxResults = 25
    @AppStorage("selectedTab") var selectedTab = "home"

    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("프로필")) {
                    TextField("Display name", text: $displayName)
                }

                Section(header: Text("환경설정")) {
                    Toggle("Enable notifications",
                           isOn: $notificationsEnabled)
                    Stepper("Max results: \(maxResults)",
                           value: $maxResults,
                           in: 10...100,
                           step: 5)
                }

                Section {
                    Button("설정 재설정") {
                        UserDefaults.standard.removePersistentDomain(
                            forName: Bundle.main.bundleIdentifier!)
                    }
                    .tint(.red)
                }
            }
            .navigationTitle("Settings")
        }
    }
}

양식에는 다양한 유형의 네 가지 @AppStorage 프로퍼티가 포함됩니다: 이름용 String, 알림용 Bool, 결과 수용 Int, 선택된 탭용 String. 모든 컨트롤은 Binding($displayName, $notificationsEnabled 등)을 통해 프로퍼티에 바인딩됩니다. "Reset settings" 버튼은 앱 도메인을 제거하여 모든 UserDefaults를 지우며, 이후 @AppStorage는 자동으로 기본값으로 돌아갑니다.

App Group과 @AppStorage 동기화

swift
struct SharedSettingsView: View {
    let sharedDefaults = UserDefaults(suiteName: "group.com.example.app")

    @AppStorage("widgetTheme", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetTheme: String = "시스템"

    @AppStorage("widgetColor", store: UserDefaults(suiteName: "group.com.example.app")!)
    var widgetColor: String = "파란색"

    var body: some View {
        Form {
            Picker("Widget theme", selection: $widgetTheme) {
                Text("시스템").tag("system")
                Text("라이트").tag("라이트")
                Text("다크").tag("다크")
            }
            Picker("Accent color", selection: $widgetColor) {
                Text("파란색").tag("blue")
                Text("초록색").tag("초록색")
                Text("빨간색").tag("빨간색")
            }
        }
    }
}

App Group(앱과 확장 기능 간 공유 저장소)의 경우 @AppStorage는 store 매개변수를 허용합니다: UserDefaults(suiteName:). 값은 메인 앱, Widget, Watch App 및 동일한 그룹의 다른 확장 기능에서 액세스할 수 있는 공유 컨테이너에 저장됩니다. Widget은 이러한 설정을 읽을 수 있으며, 앱에서 설정이 변경되면 UserDefaults 관찰 메커니즘을 통해 Widget이 자동으로 업데이트됩니다.

자주 묻는 질문

@AppStorage와 @State의 차이점은 무엇인가요?

@State는 값만 메모리에 저장하고 앱을 다시 시작하면 재설정됩니다. @AppStorage는 값을 UserDefaults에 저장하고 다음 실행 시 복원합니다. 임시 화면 데이터에는 @State를, 재시작 후에도 유지되어야 하는 설정에는 @AppStorage를 사용하세요.

Enum과 함께 @AppStorage를 사용할 수 있나요?

네, Enum이 String 또는 Int 유형의 rawValue로 RawRepresentable 프로토콜을 구현하는 경우 가능합니다. 예: @AppStorage("theme") var theme: AppTheme = .system. SwiftUI는 rawValue를 통해 enum을 자동으로 직렬화하고 로드 시 복원합니다.

모든 @AppStorage 값을 지우려면 어떻게 해야 하나요?

표준 저장소의 경우 UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)를 호출하거나 특정 키의 경우 removeObject(forKey:)를 호출하세요. 지운 후 모든 @AppStorage 프로퍼티는 선언 시 지정된 기본값으로 돌아갑니다.

@AppStorage가 App Extensions에서 작동하나요?

네, 앱과 확장 기능 간 동기화를 위해 App Group을 사용하세요: @AppStorage("key", store: UserDefaults(suiteName: "group.com.example.app")!). Widget, Share Extension 및 Watch App이 동일한 UserDefaults를 읽고 쓸 수 있으며 변경 사항이 자동으로 추적됩니다.

@AppStorage에 얼마나 많은 데이터를 저장할 수 있나요?

@AppStorage는 UserDefaults를 사용하며, 이는 소량의 데이터(설정, 토큰, 카운터)용으로 설계되었습니다. 권장 한도는 앱당 최대 100KB입니다. 구조화된 데이터나 대용량 데이터(객체 배열, 미디어 파일)의 경우 SwiftData, Core Data 또는 파일 시스템을 사용하세요.

요약

  • @AppStorage — UserDefaults에서 반응형 설정 저장을 위한 property wrapper
  • 자동 저장 및 모든 소스에서 값 변경 시 UI 자동 업데이트
  • 지원: String, Int, Double, Bool, Data, URL 및 RawRepresentable enum
  • 기본값은 선언 시 설정되며 첫 실행 시 복원됨
  • App Group으로 앱과 확장 기능 간 @AppStorage 동기화 가능
  • UserDefaults는 소량의 데이터(최대 100KB)에만 적합
  • @AppStorage는 사용자 설정에, @State는 임시 화면 상태에 사용

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

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

프로젝트 논의

더 읽어보기