본문 바로가기
iOS/Swift

Swift) Delegate

by Jiseong 2022. 2. 13.

Delegate?

진짜 Delegate 이거.. 너무 복잡하게 생각했던건가 생각도 드는데..

이해가 조금 되서 쓰는 것도 있지만, 이해하고 정리하려 포스팅하는 것도 없지 않아 있다.

그냥 우선 쉽게 접근하기 위해 Delegate 뜻이 뭔지부터 알아보자

뭐 더 많은 뜻이 있겠지만, 우리가 알아야하는 Delegate의 뜻은 "위임하다" 이다. (영국식으로 ㅋㅋ)

 

우리는 위임을 알아야한다.

 

위임은 내가 할 일은 다른 사람에게 넘기는 것, 대신 시키는 것 이라는 의미를 가진다.

 

위임하면 사장-직원 같은 수직적인 관계가 생각나는데, 위임은 그저 내가 할 일을 누군가에게 시키는 것이기 때문에 수평적인 관계에서도 가능하다.

 

들어가기 전 먼저 알아야 할 핵심은 수직/수평적 관계를 따지는 것이 아닌 내가 할 일을 을 다른 누군가에게 시키는 것 임을 알자.

근데 누구한테 위임을 하고 자시고 이걸 왜 쓰는건데?가 궁금할 것이다.

왜 쓰는것인가?

객체지향 세계에선 역할과 책임이라는 중요한 문구가 있다.

 

예를 들어 SOLID에서 단일책임원칙이라던지, 한 함수의 기능, 책임은 하나여야 된다 던지..

 

Delegate는 책임을 넘겨주기 위해 사용된다.

 

실생활에서 위임은 어떻게 쓰이고 있을까?

  • 내가 하는 일이 너무 많아서, 즉 지는 책임이 너무 많아서 이를 다른 사람에게 넘겨주는 경우
  • 피치못할 사정이 생기거나, 일 하기 싫을 때 남한테 넘겨주는 경우

대통령 권한 대행, 배송 대행지 이런 것도 위임으로 볼 수 있다.

 

"아 이건 못할 것 같아 너가 해줘", "아 바빠/귀찮아 너가 해줘" 이렇게 자신의 책임을 남에게 넘기는 것 같이 실생활에서 빈번하게 위임은 일어나고 있다.

 

프로그래밍 세계에선 자신의 책임을 넘김으로써 위임한 자, 위임받은 자의 책임과 역할을 분명하게 할 수 있고, 그로 인해 가독성, 로직의 흐름을 따라가기 쉬워지는 결과를 얻을 수 있다.

 

음.. 객체를 단순화시키고 추상에 의존함으로써 의존성도 떨어뜨릴 수 있는.. 이건 밑에서 설명하겠다.

Delegate

위에선 수평/수직적 관계 상관없다고 그랬는데 떠오르는 예가 이것뿐이 없어서 사장과 직원과의 관계로 예를 들어 설명해보겠다.

 

(주)지성이라는 회사의 사장인 지성이 직원에게 "리스트 던져줄테니깐 개발해봐" 라고 일을 시켰다고 생각해보자.

 

아 물론 회사의 사장인 지성도 개발할 수 있다는 가정이 있다.

 

먼저 사장 객체를 만들어보자.

class CEO {
    var developmentList: [String: Bool]

    var employee: Employee
    
    init(developmentList: [String: Bool], employee: Employee) {
        self.developmentList = developmentList
        self.employee = employee
        self.employee.delegator = self
    }
    
    func notifyDevelopeList() -> [String] {
        return developmentList.keys.map { $0 }
    }
}

이니셜라이저를 보면 직원(Employee)의 위임자는 self (CEO 자기 자신)으로 초기화되고 있다. -> CEO가 직원에게 위임했다.

 

CEO는 개발 리스트를 작성하고 작성된 리스트를 직원에게 던져주는 역할을 하고 있다.

 

그럼 직원은 그 개발 리스트를 받아서 개발을 하고 CEO에게 개발 다 됐어요 하고 알려야겠지?

class Employee {
    var delegator: CEO?
    
    func develope() {
        let developmentStatus = delegator?.notifyDevelopeList()
        
        delegator?.developmentList.updateValue(true, forKey: (developmentStatus?.first)!)
    }
}

강제 언래핑은.... 이해해주세요.. 직원이 시킨대로 개발 착착 다 하면 때려치고 회사 차릴까봐.. 하나만 완료하게 하려다 그만..

let employee1 = Employee()

let ceo = CEO(developmentList: ["지성플레이어":false, "지플릭스":false, "지성e북":false], employee: employee1)

employee1.develope()

ceo.developmentList // ["지플릭스": true, "지성플레이어": false, "지성e북": false]

CEO에게 일을 위임받은 employee1이 개발을 하니, ceo의 개발리스트가 업데이트 된 모습을 볼 수 있다.

어 근데 이거.. 이러면 Delegate를 왜 쓰는거여? 그냥 직접 접근해서 값 바꿔주는 거잖아? 생각이 들어야 한다.

 

맞다. 위 코드는 위임을 한 것은 맞지만 서로 의존성이 강하다.

 

사장이 위임할 사람으로 지정한 직원이 없으면 회사 쫄딱 망하는거고, 직원 또한 저 사장이 아니면 굶어 죽는 것이다.

그래서 Swift에서의 Delegate는 필수적으로 Protocol을 사용한다.

 

의존성을 줄이기 위해 서로에게 의존하는 것이 아닌 Protocol이라는 추상에 의존을 하게끔하여 서로를 모르는 사이로 만들어 버리는 것이다.

 

사장은 개발할 수 있는 직원이 필요한거지 특정 직원이 필요한 것이 아니다.

 

더불어 직원은 일 할 수 있는 회사(사장)이 필요한거지 꼭 저 회사(사장)에서 일 할 필요가 없다.

 

그럼 우린 "개발할 수 있는" 과 "개발 시킬 수 있는" 으로 추상화 해볼 수 있다.

protocol Developable {
    func develope()
}

protocol DevelopementOrderable {
    var developmentList: [String: Bool] { get set }
    
    func notifyDevelopeList() -> [String]
}

이제 사장이 달라지든, 직원이 달라지든 해당 프로토콜만 채택해준다면 누구든 일을 할 수 있고, 일을 시킬 수 있는 책임과 역할을 얻을 수 있다.

class CEO: DevelopementOrderable {
    var developmentList: [String: Bool]

    var employee: Developable?
    
    init(developmentList: [String: Bool]) {
        self.developmentList = developmentList
    }
    
    func notifyDevelopeList() -> [String] {
        return developmentList.keys.map { $0 }
    }
}

class TeamLeader: DevelopementOrderable {
    var developmentList: [String: Bool]

    var subordinate: Developable?
    
    init(developmentList: [String: Bool]) {
        self.developmentList = developmentList
    }
    
    func notifyDevelopeList() -> [String] {
        return developmentList.keys.map { $0 }
    }
}

DevelopementOrderable을 채택하니 꼭 사장이 아닌 팀장도 부하직원에게 일을 시킬 수 있게 됐다.

class Employee: Developable {
    var delegator: DevelopementOrderable?
    
    func develope() {
        let developmentStatus = delegator?.notifyDevelopeList()
        
        delegator?.developmentList.updateValue(true, forKey: (developmentStatus?.first)!)
    }
}

class CodingGod: Developable {
    var delegator: DevelopementOrderable?
    
    func develope() {
        let developmentStatus = delegator?.notifyDevelopeList()
        
        developmentStatus?.forEach {
            delegator?.developmentList.updateValue(true, forKey: $0)
        }
    }
}

이제 특정 사람(클래스)가 아닌 Developable을 채택한 사람이라면 직원, 부하직원이 될 수 있다.

이전에 의존성이 강했던 코드와 달리 사장과 직원은 서로 의존하지 않고, 추상에 의존하므로 서로를 모르는(의존하지 않는)사이가 되었다.

 

해당 프로토콜만 채택하고 있다면 위임에도 제한이 없을 것이다.

let employee1 = Employee()
let genius = CodingGod()

let ceo = CEO(developmentList: ["지성플레이어":false, "지플릭스":false, "지성e북":false])
let leader = TeamLeader(developmentList: ["성지플레이어":false, "성플릭스":false, "성지e북":false])

employee1.delegator = ceo
employee1.develope()
print(ceo.developmentList) // ["지성플레이어": true, "지플릭스": false, "지성e북": false]

employee1.delegator = leader
employee1.develope()
print(leader.developmentList) // ["성지e북": true, "성플릭스": false, "성지플레이어": false]

genius.delegator = leader
genius.develope()
print(leader.developmentList) // ["성지e북": true, "성지플레이어": true, "성플릭스": true]

위의 프로토콜을 채택만 해준다면 어떤 클래스든 위임받고 할 수 있다.




이미 정의된 Delegate 프로토콜도 많은데, 그것들도 다 똑같다.

 

진짜 간단하게 생각해보면 된다.

 

밑에 예에서 "나"를 TextField로 생각해보자

 

TextField: "아 내가 할일이 많아.. 그래서 이 일 좀 누구한테 넘겨야겠어"

TextField: "야, X야 너가 이것 나 대신 좀 해라"

X: 아.. 네..

 

그럼 X측 관점으로 보면 쟤(TextField)의 일을 내가 대신 하는 것이다.

간단한 코드로 보면 (그냥 대충 짜본 것이다. 컴파일 오류날 것임)

class X {
	TextField.delegate = self // TextField가 일 위임하는 애는 나임, 쟤가 나한테 일 시킴
}

이렇게 되는 것이다.

 

근데 TextField의 관점으로 보면?

쟤(X)가 나(TextField)의 일을 대신 하는 거니

class TextField {
	var delegate = X
}

이렇게 된다.

 

근데 딱봐도 밑에건 드럽게 헷갈리지 않나?

그래서 개인적으로 생각한 것은 위임받는 자의 관점으로 생각하면 훨씬 간단해질 것 같다.

 

아ㅏㅏㅏ 내일 테이블뷰로 더 알아보겠다.

 

설명 진짜 못한 것 같아서 우울하다.

'iOS > Swift' 카테고리의 다른 글

Swift) Codable, Decode, Encode  (0) 2022.02.13
Swift) TableView  (0) 2022.02.13
SOLID  (0) 2022.02.13
Swift) App LifeCycle  (0) 2022.02.13
Swift) weak, unowned  (0) 2022.02.13

댓글