버깅 스트레스 끝! PropertyWrapper로 Swift 코드 오류 추적

작성일 :

버깅 스트레스 끝! PropertyWrapper로 Swift 코드 오류 추적

Swift에서 프로그래머들은 종종 코드 오류를 추적할 때 큰 어려움을 겪습니다. 이러한 문제를 해결하기 위해 'PropertyWrapper'라는 기능이 도입되었습니다. PropertyWrapper를 통해 어떻게 코드 오류를 효과적으로 추적하고 디버깅할 수 있는지에 대해 자세히 알아보겠습니다.

PropertyWrapper란?

PropertyWrapper는 Swift 5.1에 도입된 기능으로, 프로퍼티의 접근자 메서드를 감싸는 래퍼를 생성할 수 있습니다. 이를 통해 코드의 중복을 줄이고, 특정 프로퍼티에 대한 추가 기능을 손쉽게 제공할 수 있습니다. 예를 들어, 값을 저장하기 전에 유효성 검사를 하거나, 변화를 감지해 로그를 기록하는 등의 작업이 가능합니다.

PropertyWrapper 사용법

PropertyWrapper를 사용하기 위해서는 @propertyWrapper 키워드를 사용해 래퍼 타입을 정의해야 합니다. 예를 들어, 값의 유효성을 검사하는 단순한 래퍼를 만들어 보겠습니다.

swift
@propertyWrapper
struct Validated<Value> {
    private var value: Value?
    private let validation: (Value) -> Bool

    init(wrappedValue initialValue: Value?, _ validation: @escaping (Value) -> Bool) {
        self.validation = validation
        if let initialValue = initialValue, validation(initialValue) {
            self.value = initialValue
        } else {
            self.value = nil
        }
    }

    var wrappedValue: Value? {
        get { return value }
        set {
            if let newValue = newValue, validation(newValue) {
                value = newValue
            } else {
                print("값이 유효하지 않습니다.")
                value = nil
            }
        }
    }
}

위 예시에서 Validated 래퍼는 값이 유효한지 검사하고, 유효하지 않은 경우 경고 메시지를 출력합니다. 이를 통해 값을 설정할 때 자동으로 유효성 검사가 이루어지도록 할 수 있습니다.

예제: 유효성 검사 래퍼 적용

위에서 만든 Validated 래퍼를 실제 코드에 적용해보겠습니다. 간단한 사용자 정보를 다루는 구조체에 적용해 보겠습니다.

swift
struct User {
    @Validated({ $0.count >= 5 })
    var username: String?

    @Validated({ $0 > 0 && $0 <= 100 })
    var age: Int?
}

이제 User 구조체의 usernameage 프로퍼티에 유효성 검사를 자동으로 적용할 수 있게 되었습니다. 예를 들면, 사용자가 너무 짧은 이름을 입력하거나, 나이를 음수로 입력하는 실수를 방지할 수 있습니다.

swift
var user = User()
user.username = "abc"  // 출력: 값이 유효하지 않습니다.
user.username = "swift_user"  // 정상적으로 설정됨

user.age = -1  // 출력: 값이 유효하지 않습니다.
user.age = 25  // 정상적으로 설정됨

이처럼 PropertyWrapper를 이용하면 특정 프로퍼티에 일관된 검사를 적용하고, 오류를 추적하기가 쉬워집니다.

디버깅을 위한 PropertyWrapper

디버깅을 더 용이하게 하기 위해 Custom Logging PropertyWrapper를 만들어 보겠습니다. 이 래퍼는 값이 변경될 때마다 로그를 기록하여 나중에 디버깅할 때 유용하게 사용할 수 있습니다.

Custom Logging PropertyWrapper 구현

swift
@propertyWrapper
struct Logged<Value> {
    private var value: Value
    private let name: String

    init(wrappedValue initialValue: Value, name: String) {
        self.value = initialValue
        self.name = name
        print("
        print("
  (\(self.name) - 초기값 설정: \(self.value))")
    }

    var wrappedValue: Value {
        get { return value }
        set {
            print("
            print("
  (\(self.name) - 값 변경: \(value) -> \(newValue))")
            value = newValue
        }
    }
}

Logged 래퍼는 초기값 설정 및 값 변경 시마다 로그를 출력합니다. 이를 활용하면 어떤 값이 언제 변경되었는지 추적할 수 있어 디버깅에 큰 도움이 됩니다.

예제: 디버깅을 위한 래퍼 적용

다음은 사용자 정보 구조체에 Logged 래퍼를 적용한 예입니다.

swift
struct UserWithLogging {
    @Logged(name: "Username")
    var username: String

    @Logged(name: "Age")
    var age: Int
}

var user = UserWithLogging(username: "initial_user", age: 20)
user.username = "new_user"  // 출력: (Username - 값 변경: initial_user -> new_user)
user.age = 25  // 출력: (Age - 값 변경: 20 -> 25)

이처럼 Logged 래퍼를 사용하면 프로퍼티 값 변경 시마다 자동으로 로그를 기록하고, 이를 통해 오류를 빠르게 추적할 수 있습니다.

결론

Swift의 PropertyWrapper는 코드 오류를 추적하고 디버깅하기 위한 강력한 도구입니다. 이를 활용하면 프로퍼티의 유효성 검사, 값 변경 로그 기록 등 다양한 기능을 손쉽게 구현할 수 있습니다. 위에서 설명한 방법을 통해 더 효율적이고 유지보수하기 쉬운 코드를 작성할 수 있기를 바랍니다.