[SwiftUI] 스유에서 뷰를 그리고(draw) 업데이트하는 데이터 흐름 관리 - @propertyWrappers 1
@propertyWrapper..? propertyWrapper는 Swift 5.1+ 에 소개된, 이름 그대로 property를 감싸는 역할을 하는 어트리뷰트다. 어떤 상태를 가지는 property를 정의하고 값을 반영하는 과정을 추상화 할 수 있게 해준다. 제네릭을 이용해서 다양한 타입을 가질 수 있는 멤버 변수도 동일한 로직을 타게 할 수 있다. 말이 너무 어려운데, propertyWrapper로 많이 관리되는 UserDefaults가 역시 나에게도 이해가 쉬웠다. UserDefaults는 String으로 된 키와 저장할 값을 기반으로 값(상태)을 저장하거나 불러올 수 있다. 저장은 새로운 값으로 업데이트가 될 수 있다는 뜻으로도 쓰일 수 있다. (덮어 쓸 수 있다는 말) UserDefaults에 값을 저장, 업데이트,불러오는 등의 로직은 아주 단순하지만 사용할 때마다 그 구문을 반복해야 한다. 그래서 보통 UserDefaults를 관리하는 객체를 만들어서 활용하는데, 이때 propertyWrapper 개념을 접목하면 조금 더 활용도가 높아진다. propertyWrapper 어트리뷰트가 붙은 객체는 내부적으로 wrappedValue라고 하는 연산 프로퍼티를 구현해주어야 한다. 이 wrappedValue 로직을 통해 멤버에 대해 동일한 로직을 반영해줄 수 있다. 생성자를 통해서 기본값도 넣어줄 수 있다. 정의한 propertyWrapper 객체는 어트리뷰트처럼 활용할 수 있다. @로 호출한 propertyWrapper의 생성자에 필요한 초기 값을 넣어준 형태로 멤버에 반영해주면, 따로 해당 멤버의 값(상태)을 불러오거나 업데이트 하는 로직을 신경쓰지 않아도 된다. SwiftUI에서도 다양한 propertyWrapper들이 활용되고 있는데, 특히 뷰를 그리고, 뷰를 업데이트하는 데이터(상태 값)를 효율적으로 관리할 수 있게 도와주는 로직이 구현되어 있다. @State 이름에서 알 수 있듯이, View 객체의 데이터 상태를 관리하는 멤버 앞에 붙는다. View struct 안에서 활용되기에 당연히 View 객체 안에서 초기화 되어야 한다. 특정 View 객체 안에서 사용되고, 초기화되기 때문에 외부에서 주입을 받아 상태가 관리되지 않아야 한다. (사실 주입은 받을 수 있겠지만, 내부에서 그 주입받은 상태가 관리되지 않기 때문에 private 사용이 권장된다.) 뷰 객체 내부에서 활용되는 자식 뷰 객체에게 그 상태값을 바인딩 프로퍼티와 함께 전달해줄 수 있다. 가장 좋은 점은, @State로 관리되는 상태값에 따라 뷰가 fresh-render되고 뷰가 살아있는 동안 그 상태값이 유지된다는 것이다. class의 인스턴스와 같은 참조형을 상태값으로 활용할 수는 있지만, 해당 인스턴스의 값을 변경한다고 하더라도 상태가 변경되었다고 인지되지는 않는다. (@Published, @ObservableObject와 같은 wrapper가 붙으면 말이 달라지긴 하지만) State 의 구현부를 타고 가보면 DynamicProperty 라는 프로토콜을 채택하고 있는 것을 볼 수 있다. DynamicProperty 프로토콜은 내부적으로 update라고 하는 메서드를 가지고 있고, 이 메서드를 통해서 변경된 상태를 인지하고 뷰를 새롭게 업데이트 해주게 된다.