2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

[Swift-Study] 심화 문법종합반 2주차 1일차 정리 - 프로퍼티 옵저버

2024. 3. 12. 16:41ㆍIOS/Swift-Study
SMALL

이번엔 프로퍼티를 관찰하여 특정 프로퍼티의 값 변경 전후에

추가 동작을 수행할 수 있는 프로퍼티 옵저버에 대해 학습해 보려고 합니다.

'didSet''willSet'

프로퍼티 옵저버란?

변수에 프로퍼티 옵저버를 정의해주어서

프로퍼티 값의 변경 사항을 모니터링하고,

미리 구현한 코드로 대응할 수 있는 것을 말합니다.

 

쉽게 말해서 프로퍼티를 관찰(observe)하면서

변경 사항이 발생할 때 실행되는 것을 말합니다.

 

'willSet'보다는 'didSet'이 많이 사용됩니다

willSet과 didSet을 둘 다 작성했을 경우 'willSet'이 먼저 실행됩니다.

추가할 수있는 경우

저장 프로퍼티(stored property)

연산 프로퍼티(computed property)

 

'didSet'

'didSet'은 새 값이 저장된 직후에 호출됩니다.

이전 프로퍼티의 값이 oldValue 로 제공됩니다.

 

'willSet'

'willSet'은 값이 저장되기 직전에 호출됩니다.

새로운 프로퍼티의 값이 newValue 로 제공됩니다.

// 'willSet'과 'didSet' 예시
var myProperty: Int = 20{
   didSet(oldVal){
      //myProperty의 값이 변경된 직후에 호출, oldVal은 변경 전 myProperty의 값
      /* 
       예를 들어 myProperty의 값을 외부에서 넣어주면 oldVal은 변경 전 myProperty의 값
       위의 경우 20이 들어 있을 것입니다. 
      */
   }
   willSet(newVal){
      //myProperty의 값이 변경되기 직전에 호출, newVal은 변경 될 새로운 값
   }
}

var name: String = "Unknown" { 
/*
 name 프로퍼티는 String인데 "Unknown" 이라는 값이 이미 들어가 있죠?
 여기에는 willSet과 didSet 이라는 프로퍼티 옵저버가 들어가 있는데
 willSet에는 현재 이름 바뀔이름을 print 해주고 있으며 didSet도 현재이름, 바뀌기 전 이름
 이렇게 print를 해주고 있습니다. willSet의 경우 newValue 라는 값을 이미 지니고 있는데
 들어가는 값이 newValue라는 키워드를 써주면 자동으로 세팅이 됩니다.
 
 didSet의 경우는 oldValue가 적용이 됩니다. 사용 예시로는 기존 name에는 Unknown이 들어가 있었는데
 name에 Peter라는 값을 다시 할당 해줍니다. willSet이 먼저 실행이 될 것이고, Peter라는 값이 
 세팅되기 바로 직전에 실행이 되는 것을 이야기 합니다. 그렇기 때문에 현재 이름 = Unknown이 되는것이고,
 바뀔 이름 = Peter가 됩니다. didSet의 경우는 현재 이름 = Peter, 바뀌기 전 이름 = Unknown이 됩니다.
*/
    willSet {
        print("현재 이름 = \(name), 바뀔 이름 = \(newValue)")
    }
    didSet {
        print("현재 이름 = \(name), 바뀌기 전 이름 = \(oldValue)")
    }
}
 
name = "Peter" // (옵저버)관찰당함
// willSet이 먼저 실행됨
// 현재 이름 = Unknown, 바뀔 이름 = Peter
// 현재 이름 = Peter, 바뀌기 전 이름 = Unknown
// 심화 예시
class UserAccount { // 유저의 계정이라는 클래스를 선언
    var username: String
    var password: String
    /*
     username, password 라는 String타입의 저장 프로퍼티가 있으며, 'loginAttempts' 라는
     프로퍼티가 있습니다. 여기엔 기본값으로 0이 들어가 있습니다. 이는 로그인 시도 횟수를 나타냅니다.
    */
    var loginAttempts: Int = 0 { // 
        didSet { // Int = 0 이 3이상이면 횟수실패 문구를 출력 후 lockAccount 메서드를 호출
            if loginAttempts >= 3 { 
                print("로그인 시도가 3회 이상 실패하였습니다. 계정이 잠겼습니다.")
                lockAccount() // isLocked라는 변수를 보면 이해할 수 있습니다.
            }
        }
    }
    
    var isLocked: Bool = false { // isLocked 프로퍼티의 기본값은 false입니다.
        didSet { // 위 값이 할당이 되면 isLocked 상태라면 계정 잠금 문구를 아니라면 해제문구를 출력하는 프로퍼티 입니다.
            if isLocked {
                print("계정이 잠겼습니다.")
            } else {
                print("계정이 잠금 해제되었습니다.")
            }
        }
    }
    
    // 초기화자 선언문
    init(username: String, password: String) { // init에는 username과 password를 넣어주고 있습니다.
        self.username = username
        self.password = password
    }
    
    func login(with enteredPassword: String) { // login이라는 메서드에는 enteredPassword 라는 파라미터로 들어가 있음을 볼 수 있는데 
        if enteredPassword == password { // enteredPassword가 password와 일치하는지 확인해줍니다.
            print("로그인 성공!")
            loginAttempts = 0 // 로그인 성공 시 로그인 시도 횟수 초기화
            // 이후 loginAttempts를 0으로 초기화 시켜주는것입니다. 이유는 몇번 실패 했다 하더라도 성공을 하게 되면 그 카운터를 다시 제거 해주어야 하니까요.
        } else { // 아니라면 실패 문구를 실행
            print("잘못된 비밀번호입니다.")
            loginAttempts += 1 // 로그인 실패 시 로그인 시도 횟수 증가
            // 여기서 3회가 된다면 위쪽에 loginAttempts >= 3 요부분이 실행되는 로직이 구현 되어 있습니다.             
        }
    }
    
    private func lockAccount() {
        isLocked = true
    }
    
    func unlockAccount() { // 잠긴 상태를 다시 풀어주는 역할을 해줍니다.
        isLocked = false
    }
}

// 사용자 계정 생성
let user = UserAccount(username: "user123", password: "password123")

// 로그인 시도
user.login(with: "wrongpassword") // user를 가지고 login 시도할 것인데 login을 했을 때 wrongpassword 이 패스 워드를 가지고 로그인을 할 것인데 이때 위에서 정의한 password123 패스워드와 비교를 할 것입니다.
// 출력:
// 잘못된 비밀번호입니다.

user.login(with: "wrongpassword") 
// 출력:
// 잘못된 비밀번호입니다.

user.login(with: "wrongpassword") 
// 출력:
// 잘못된 비밀번호입니다.
// 로그인 시도가 3회 이상 실패하였습니다. 계정이 잠겼습니다.
// 계정이 잠겼습니다.

/* 
 loginAttempts += 1 이부분이 3이 되는 순간 위에서 선언한 didSet 부분이 호출이 됩니다. 
 didSet 부분은 지속적으로 사용이 되고 있었는데 이유는 loginAttempts는 원래 0이 었는데 
 loginAttempts += 1 부분에서 걸려서 1이 들어가는 순간 didSet부분이 실행이 되는 것이죠
 if loginAttempts >= 3 { 이부분에서 조건문을 넣어 두었기 때문에 loginAttempts가 1일때에는
 로그인실패가 실행이안된 것이며 3이된 시점이 된 순간 lockAccount()가 발동합니다. 계정이 잠기게 되는것이죠
 계정을 잠궈버리는 프로퍼티는 isLocked 부분입니다. 이를 true로 바꿔주면서 이계정을 잠구게 되는 것입니다.
 그렇기에 계정을 비상시에 풀수 있는 unlockAccount를 만든 것입니다.
*/

// 계정 잠금 해제
user.unlockAccount() // 계정이 잠금 해제되었습니다.
/*
 unlockAccount 이부분의 경우는 isLocked부분에 false를 넣었기 때문에 
 var isLocked: Bool = false 이 부분이 출력이 된 것입니다.
 즉, unlockAccount가 들어갔기 때문에 else 부문이 실행이 되어 계정이 잠금해제된 것 입니다.
*/
728x90