SwiftUI 프레임워크를 활용하여 앱을 개발할 때 상태 관리를 위한 @StateObject와 @ObservedObject를 정확하게 구분하여 사용하는 것은 매우 중요한 기초 지식이라 할 수 있더라고요. 두 속성 모두 ObservableObject 프로토콜을 준수하는 클래스를 관찰하기 위해 사용되는 도구이지만, 내부적으로 뷰의 생명주기 및 렌더링 루프에 어떻게 반응하느냐에 따라 동작 원리가 상당히 다르거든요. 단순히 둘의 기능이 유사해 보인다는 이유로 혼용하게 되면, 특정 상황에서 데이터가 예기치 않게 초기화되거나 뷰가 정상적으로 업데이트되지 않는 등의 기술적인 결함이 발생할 수 있어 명확한 구분법을 익혀두는 것이 좋더라고요.

@StateObject는 해당 객체의 소유권이 현재 뷰에 있음을 명시적으로 선언하는 역할을 수행하더라고요. SwiftUI의 뷰는 구조체(struct) 타입으로 정의되어 있기 때문에 상태가 바뀔 때마다 뷰가 빈번하게 재구성되는데, 이때 @StateObject로 선언된 객체는 뷰가 다시 그려지더라도 메모리상에서 인스턴스가 유지되도록 보장해주거든요. 즉, 뷰의 생명주기 내에서 데이터의 연속성이 보장되어야 하는 초기화 단계에서 사용해야 하더라고요. 이 방식을 사용하면 뷰가 재렌더링될 때마다 객체가 새로 생성되는 것을 방지할 수 있어 데이터의 안정성을 확보하는 데 큰 도움을 주더라고요.

반면에 @ObservedObject는 외부 소스나 상위 뷰로부터 전달받은 객체를 단순히 관찰하는 용도로 사용될 때 적합하더라고요. 이 속성은 뷰가 재구성되어도 전달받은 인스턴스를 그대로 사용하므로, 객체를 뷰 내부에서 직접 초기화하면 재구성 시마다 새 인스턴스가 만들어져 데이터가 초기화되는 문제가 생길 수 있거든요. 주로 부모 뷰나 다른 컴포넌트로부터 데이터가 주입될 때 사용하더라고요. 만약 부모 뷰에서 생성한 객체를 자식 뷰에서 @StateObject로 선언하게 되면, 자식 뷰는 처음 생성된 인스턴스를 계속 유지하며 부모가 새로운 객체를 전달하더라도 그 업데이트를 무시하게 되거든요. 이는 데이터 흐름이 꼬이고 예상치 못한 동작을 유발하는 원인이 되기도 하더라고요.

import SwiftUI

class UserProfile: ObservableObject {
    @Published var name: String
    init(name: String) {
        self.name = name
    }
}

struct ParentView: View {
    // 소유권이 있는 위치에서는 @StateObject를 사용하더라고요.
    @StateObject var user = UserProfile(name: "Swift_User")

    var body: some View {
        ChildView(user: user)
    }
}

struct ChildView: View {
    // 외부에서 주입받은 객체는 @ObservedObject로 관찰하더라고요.
    @ObservedObject var user: UserProfile

    var body: some View {
        Text(user.name)
    }
}

결론적으로 실무에서 코드를 작성할 때는 객체의 생성 위치와 소유 관계를 명확히 파악하는 것이 중요하더라고요. 객체를 처음 생성하고 소유권을 가지는 가장 상위의 뷰에서는 @StateObject를 선택하고, 그 아래의 뷰로 데이터가 전달되는 경로에서는 @ObservedObject를 사용하여 데이터의 일관성을 유지하는 방식이 권장되는 패턴이더라고요. 이러한 구분은 코드의 가독성을 높일 뿐만 아니라, 뷰의 재구성 과정에서 발생할 수 있는 다양한 런타임 오류를 방지하는 데 매우 효과적인 방법이더라고요.

+ Recent posts