델리게이트도 괜찮지만, 더 나은 방법이 있다.

당신은 iOS앱을 만들고 있는 개발자라 가정하자. 당신은 당신이 할 수 있는 최선의 견고한(SOLID) 아키텍처로 앱의 구조를 만들어 놓았다. 앱은 모델, 네트워크 레이어, UI 레이어 또는 그것을 돕는 것들로서 구성된다. 이 레이어들 사이에 데이터를 주고 받을때 책에서는 델리게이션을 이용해라고 알려준다. 실제로 iOS 개발에서 일반적으로 사용되는 유용한 패턴이기도 하다.

델리게이션은 간단히 말하자면, 어떤것의 변화로부터 알림받기 원할때 그 어떤것에 알림받기 원하는 대상을 등록해놓는 방식의 패턴이다. 이렇게하면 그 어떤것으로부터 반응(react)할 수 있다. 예를들면 ViewController가 네트워크 서비스에게 말을 걸어서 (ViewController를 네트워크 서비스 델리게이트로 등록해서) 어떤 요청이 완료될때 자신에게 알려달라고 한다. 이때 ViewController를 네트워크 서비스의 델리게이트로 만들어 가능하게 된다. 네트워크 서비스는 요청이 완료되었을 때 델리게이션 메소드를 호출 할 것이다.
델리게이트 참조를 보내는 것은 괜찮은 방법이고 기능적으로도 아무 문제가 없다. 그러나 Swift에서는 더 나은 방법이 있고, 왜 이 방법이 더 나은지 설명해 보겠다.


델리게이션을 위해 콜백을 사용
콜백은 델리게이트 패턴과 비슷한 기능을 가진다. 어떤 일이 발생할때 다른 오브젝트가 알게 해주고, 데이터를 전달하는 기능이다.

델리게이트 패턴과 다른점은, 응답받고 싶은 객체 자체를 넘겨주는 것 대신에 함수만을 넘겨준다. 함수는 Swift에서 클래스의 첫번째 요소이다. 따라서 함수를 프로퍼티로 가지고 있을수도 있다.
MyClass는 이제 myFunction 이라는 프로퍼티를 가지는데, 어딘가에서 호출할 수도 있고, 누구나 값을 바꿀 수도 있다(Swift에서 정의된 규칙에의해 프로퍼티는 디폴트로 internal이 된다). 이것이 델리게이션 대신 콜백을 사용하는 기본 아이디어이다. 아래 예제는 위 예제에서 델리게이트 대신 콜백으로 대체한 것이다:
콜백을 사용하는 다른 상황은 데이터가 바뀔때 알림을 받고 싶을 경우이다. 프로퍼티 옵저버에서 콜백을 호출함으로서 가능하다:
콜백에대한 간단한 노트 : 델리게이트에서는 리테인(retain) 사이클을 멈추기위해 weak 프로퍼티로 만들어야하는 것처럼, 여러분도 클로저 안에서는 self를 weak 변수로 해두어야한다.

그래서 왜 콜백이 더 나은가?
1. 분리됨(Decoupling)
델리게이트는 코드를 분리하는 경향이 있다. 프로토콜로 구현하는 한 NetworkService에게 누가 그것의 델리게이트인지 알 필요가 없다. 그러나 델리게이트가 프로토콜 구현을 가지고 있고, @objc 프로토콜 대신 Swift를 사용한다면, 델리게이트는 프로톸콜에서 모든 메소드 구현을 가진다.(따라서 옵셔널 프로토콜 일치도 필요없다)

다르게 말해보면 콜백을 사용할 때, NetworkService는 메소드를 호출하기위한 델리게이트를 가지고 있을 필요가 없고 누군가가 이 메소드를 구현할 것이라는 것을 알고 있으면 된다. 메소드가 호출되는 시점만 관리하면 되고, 이 메소드가 어떻게 구현되있을지는 알 필요가 없는 것이다.

2. 다중 델리게이션
요청이 끝나고 ViewController에 알림을 주고 싶은데, 그때 로그를 남기는 클래스나 통계를 남기는 클래스를 넣고 싶을 수 있다.

델리게이트로 구현하면, 델리게이트의 배열을 가지고 있어야 할 것이다. 아니면 세개의 서로 다른 프로토콜을 가지는 델리게이트 프로퍼티를 가질 것이다!

그러나 콜백으로 구현한다면 함수의 배열을 선언하고(Swift의 이런 점이 좋다!) 뭔가 처리가 끝날때 각각 호출하면 된다. 따라서 리테인 사이클의 위험을 감수하거나 어마어마한 양의 코드로 작성되거나 하는 수많은 오브젝트와 프로토콜이 필요없어진다.

3. 일을 분리하기에 더 명확하다.
내가 생각하는 델리게이트와 콜백의 차이는 이렇다. 델리게이트는 NetworkService가 델리케이트에게 "이봐, 나 갱신됐어!"라고 말하는 반면 콜백은 델리게이트가 NetworkService를 응시하고 바라보고 있는 느낌이다.

실제로 작은 차이 같지만, 후자의 방법으로 생각하면 NetworkService가 자신의 기능을 하지 못하고 화면 표시의 기능으로 변질되는 그런 것을 방지해주는 패턴으로서는 크게 도움이 될 것이다!

4. 테스트하기 쉬움!
유닛테스트와 함께 구현하면 항상 코드베이스가 두배로 늘어난다고 느낄것이다. 그 이유는 앱의 모든 델리게이트를 포함하여 매 프로토콜마다 목(mock)을 만들어야하기 때문이다.

콜백으로 구현하면 어떠한 델리게이트에 목을 할 필요 없을 뿐만 아니라 각 테스트에서 원하는 어떠한 콜백이든 사용할 수 있게 해준다.

한 테스트에서 콜백이 제대로 호출되는지 호출는지 테스트해보고, 다른 테스트에서 그것이 호출될 때 옳바른 결과를 내는지 테스트 할 수 있다. 그리고 어떠한것도 someFuncDidGetCalled 불리언(boolean)이나 비슷한 프로퍼티를 가진 복잡하게 목(mocked)된 델리게이트를 필요로 하지 않는다.

나는 개인적으로 콜백이 코드와 테스트를 명확하게 만들어주고, 더 Swift스럽게 데이터를 주고 받는 방법이라 생각한다. 여러분이 오늘 뭔가 새로운 것을 배웠기를 바란다! 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

트랙백  0 , 댓글  0개가 달렸습니다.
secret