🔥 속성 옵저버

450자
6분

속성 옵저버(Property Observers)는 속성 값의 변화를 관찰하고 대응하는 기능을 제공해요. 속성의 값이 설정될 때마다 호출되는데, 새로 설정된 값이 현재 값과 같더라도 호출된답니다.

속성 옵저버는 다음과 같은 곳에 추가할 수 있어요:

  • 직접 정의한 저장 속성
  • 상속받은 저장 속성
  • 상속받은 계산 속성

상속받은 속성의 경우, 서브클래스에서 해당 속성을 재정의(overriding)하여 속성 옵저버를 추가할 수 있습니다. 하지만 직접 정의한 계산 속성의 경우에는 옵저버(Observer)를 만들기보다는 속성의 setter를 사용하여 값의 변화를 관찰하고 대응하는 것이 좋습니다. 속성 재정의에 대한 자세한 내용은 Overriding 부분을 참고해 주세요.

속성에는 다음 두 가지 옵저버를 정의할 수 있답니다:

  • willSet: 값이 저장되기 직전에 호출됩니다.
  • didSet: 새로운 값이 저장된 직후에 호출됩니다.

willSet 옵저버를 구현하면, 새로운 속성 값이 상수 매개변수로 전달됩니다. willSet 구현부에서 이 매개변수의 이름을 지정할 수도 있어요. 만약 매개변수 이름과 괄호를 작성하지 않으면, 기본 매개변수 이름인 newValue로 사용할 수 있습니다.

마찬가지로, didSet 옵저버를 구현하면, 이전 속성 값을 담고 있는 상수 매개변수가 전달됩니다. 매개변수에 이름을 지정하거나 기본 매개변수 이름인 oldValue를 사용할 수 있죠. 만약 didSet 옵저버 내부에서 속성에 값을 할당하면, 방금 설정된 값을 새로 할당한 값으로 대체합니다.

그럼 willSetdidSet이 실제로 어떻게 동작하는지 예제를 통해 살펴볼까요? 아래 예제는 StepCounter라는 새로운 클래스를 정의하고 있어요. 이 클래스는 사람이 걸음을 걸을 때 총 걸음 수를 추적합니다. 이 클래스는 만보계나 다른 걸음 수 측정기의 입력 데이터와 함께 사용되어 사람의 일상 속 운동량을 기록할 수 있겠죠.

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to \(newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
 
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
 
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
 
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
swift

위 예제에서 StepCounter 클래스는 Int 타입의 totalSteps 속성을 선언하고 있습니다. 이 속성은 willSetdidSet 옵저버를 가진 저장 속성이에요.

totalSteps에 새로운 값이 할당될 때마다 willSetdidSet 옵저버가 호출됩니다. 이는 새로운 값이 현재 값과 같은 경우에도 마찬가지랍니다.

이 예제의 willSet 옵저버는 newTotalSteps라는 사용자 정의 매개변수 이름을 사용하여 설정될 새로운 값을 전달받아요. 여기서는 단순히 설정될 값을 출력하고 있습니다.

didSet 옵저버는 totalSteps의 값이 업데이트된 후에 호출됩니다. 새로운 totalSteps 값과 이전 값을 비교하여, 총 걸음 수가 증가했다면 새로 추가된 걸음 수를 나타내는 메시지를 출력해요. didSet 옵저버는 이전 값에 대한 사용자 정의 매개변수 이름을 제공하지 않고, 대신 기본 이름인 oldValue를 사용하고 있습니다.

속성 옵저버를 사용하면 속성 값의 변화에 따른 추가 작업을 수행할 수 있어 매우 유용합니다. 값의 변화를 추적하고, 필요한 로직을 수행하거나, 다른 객체에 알림을 보내는 등 다양한 용도로 활용할 수 있겠죠?

lecture image

위 다이어그램은 속성 옵저버의 동작 순서를 나타내고 있어요. 속성 값이 변경되면 먼저 willSet 옵저버가 호출되고, 그 후에 속성에 새로운 값이 실제로 설정됩니다. 마지막으로 didSet 옵저버가 호출되는 거죠.