티스토리 뷰

공부

App-architecture - Networking

Zedd0202 2018. 9. 17. 12:53
반응형

안녕하세요 :) Zedd입니다.

그냥 공부하는 걸 기록하려고 적습니닷.



Networking



controller-owned networking : MVC에서 모델 레이어를 제거하고, ViewController가 네트워크 요청을 처리.

model-owned networking : MVC에서 모델계층을 유지하고, 그 아래에 네트워킹 레이어를 추가하여 네트워크 요청을 처리.


- 모델을 통해 데이터를 공유하는 접근방식(= model-owned networking)과 비교할 때, controller-owned networking은 서로 독립적으로 작동하기 때문에, ViewController간에 데이터를 공유하기 어렵게 만듬.

- model-owned networking에서는 이러한 VIewController가 모델의 "변경 사항"을 관찰(observe)한다.

(manual propagation(수동전파..?)는 전체 앱에서 데이터 일관성을 유지하는 것을 어렵게 만듬.)


- 주로 살펴볼 것은 네트워킹이 ViewController 또는 모델 레이어에 의해 소유되는지 여부를 보면된다. 



자, 대부분의 앱은 네트워킹은 필수인데요, 어떤 아키텍쳐가 사용되는지 관계없이, 앱에 네트워킹 기능을 추가할 때, 많은 어려움이 있습니다.


1. 네트워킹은 실패할 수 있다!

: 네트워크에서 데이터를 가져오려는 "시도"는 여러가지 이유로 실패할 수 있어요. 이러한 실패 case를 어떻게 처리할 지 계획을 세워야하며, 추가 UI작성까지 이어질 수 있겠죠.


2. 네트워크를 통해 지속적으로 데이터를 관찰하는 것은, 데이터를 로컬에서 관찰하는 것보다 훨씬 어려우며, 이로인해 대신 수동으로 새로고침 주기를 트리거하는 경우가 많습니다. 


3. 여러 클라이언트가 네트워크를 통해 데이터를 업데이트 할 가능성이 생기므로 잠재적인 충돌이 발생 할 수 있습니다. 네트워크의 데이터는 앱과 독립적으로 변경될 수 있으며, 리소스에 대한 참조가 별도의 알림없이 무효화 될 수 있습니다. 



controller-owned networking

controller-owned networking을 사용하는 ViewController는, 당연히 controller-owned networking니까 ViewController안에 네트워크 요청 코드가 들어있겠죠? 그리고 여기서 요청까지 하니까 이 요청의 response로 올 데이터까지도 ViewController가 소유하게 될 것입니다. 즉?????!!@?!? "이 데이터까지 ViewController가 소유한다 == 앱에 모델 레이어가 없다"라는 것을 의미하게 됩니다. 각 ViewController는 자신만의 데이터를 관리하게 됩니다. 


솔직히 controller-owned networking은 앱에 네트워킹을 추가하는 쉬운 "임시" 방편이며, 빠른 결과를 우리에게 안겨줄 수 있죠. 그리고 코드베이스에서 널리 사용되는 패턴입니다. 거의 모든 개발자는 이와같은( = controller-owned networking) 코드를 한번쯤은 작성한적이 있죠.




Fetching Initial Data

자..controller-owned networking가 어떤식으로 구성되는지 알아봅시다.

먼저 네트워크의 첫번째 작업은 일단 데이터를 가져오는 작업이죠?

controller-owned networking는 ViewController가 네트워크 요청을 하고, 데이터가 오면 View를 구성하는 방식입니다.

controller-owned networking앱에서는  각 ViewController가 이전에 가져오는 데이터의 메모리 내에 캐시만 유지하므로, 앱이 시작될 때 반드시 다시 가져와야 합니다. 이건 당연하죠?!


Making Changes

기존의 MVC와 다르게, controller-owned networking앱에서는 모델 레이어가 없습니다. 앱의 모든 데이터가 "일시적"이죠. == 앱이 종료될때 데이터는 유지되지 않는다. 데이터는 전적으로 서버에 의존하고 있죠. 


만약에 Tableview에 데이터를 띄웠다고 생각해볼게요. 물론 서버에서 가져왔어요. 근데 내가 여기서 어떤 row를 지웠어요!

그러면 서버에서도 지워져야겠죠!? 또 네트워킹을 해야겠죠!?!?!?!?

즉, 이제 데이터를 "변경"하는 것은 네트워크를 통한 비동기 작업이라고 할 수 있어요.


그럼 TableView Row한 줄을 지우는데 어떤 과정들이 필요한지 알아봅시다.




자, 이렇게 지운다고 생각해볼게요. 위 작업은 TableView Datasource 메소드에서 할 수 있죠. 

이제 서버의 데이터도 변경되어야 하기 때문에 이제 네트워킹을 하러 가봅시다.

네트워킹을 통해 서버에 있는 데이터도 지워졌다!! 라는 것, 즉 서버에서 데이터 삭제에 성공했다는 code를 보냈을 때

ViewController가 소유한 데이터에서 해당 row를 지우고, ‘tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)’


이렇게 row를 삭제하는 모든 과정이 끝나게 됩니다.

이 작업에는 비동기적인 특성 이외에도, 기존 MVC버전과의 가장 큰 차이점은, 사용자가 특정 row를 삭제하면 이에 대한 응답으로 TableView를 "적극적으로"업데이트 한다는 것입니다. 

기존 MVC에서는 Store에 특정 항목을 제거하도록 지시하고, TableView는 저장소의 변경 notification에 응답하여 자체적으로 업데이트 했었습니다. controller-owned networking에서는 그러한 매커니즘이 없습니다. 


discussion

처음에 대부분의 앱에서 controller-owned networking 접근법을 사용하는 것이 일반적으로 권장되지 않는다고 했었어요. 그러나 controller-owned networking이 가능한 특정 상황과 그렇지 않은 상황을 이해하기 위해 접근방식의 절충점을 보다 자세하게 조사하는 것이 좋습니다.


이 방법(controller-owned networking)에서 네트워크에서 가져온 데이터를 ViewController가 소유하기 때문에, 이 접근 방법에서는 모델 레이어가 아예 없습니다. 는 ViewController가 소유한 데이터가 본질적으로 VIew-State임을 의미합니다. 로컬에서 관리되는 View-state는 앱의 다른 부분이 종속되지 않는 한 제대로 작동합니다. VIewController간에 View-state를 공유해야하는 즉시, 이 상태를 소유하며 이를 변경하고 관찰 할 수 있는 VIewController 레이어 외부에 객체를 만듭니다.


이러한 추론을 ViewController의 네트워크에서, 로컬로 캐시된 데이터의 경우에 적용하면 다음과 같은 결론을 얻을 수 있죠.


ViewController가 네트워크에서 로드한 데이터 중 하나라도!!! 앱의 다른 부분과 공유되어야 하는 경우, 우리는 반드시 모델 레이어를 도입해야 합니다.

데이터가 오직!!!! 로컬에서만 사용되는 경우, 모델레이어 없이 앱을 만 들 수 있습니다. == controller-owned networking을 사용하여 앱을 만들 수 있습니다.


하지만, 고려해야 할 몇가지 다른 문제가 있죠.


1. controller-owned networking이 지금 실행 가능한 접근 방식일 수도 있지만, 나중에 이것이 변경될 수 있을지를 고려해야합니다. 종종 앱에서 데이터를 공유해야하는 필요성은 기능과 복잡성이 커질때 발생합니다. model-owned networking접근법은 단순한 앱에서도 마찬가지로 동작하기 때문에, 처음부터 보다 미래가 보장되는 접근방식을 사용할 수 있죠.


2. controller-owned networking은 네트워크 요청, 결과 처리, 네트워크 실패 처리 등 ViewController에게 새로운 책임을 안겨줍니다. ViewController는 보통 충분한 책임이 있기때문에 코드를 ViewController에 두지않고 어떤 종류의 네트워크 서비스로 분류하는 것이 좋습니다. 간단한 함수나 서버와의 상호작용을 처리하는 웹 서비스 클래스로 구현될 수 있죠. 이 네트워크 서비스는 우리가 모델 레이어라고 부르는 것이 아닙니다. 순수하게 요청하는 작업에 대한 wrapper이며, 결과데이터를 소유하지 않습니다. 


다음으로 model-owned networking을 살펴볼까요!!




Model-Owned Networking

controller-owned networking버전의 앱과는 달리, Model-Owned Networking버전은 기존 코드를 변경하지 않고, 원래의 MVC코드기반을 사용합니다. 네트워킹 지원을 추가하기 위해 Store와 직접적으로(directly) 이야기하는 웹 서비스 컴포넌트를 추가합니다. 네트워크에서 가져온 데이터는 모델 레이어가 소유하므로, Model-Owned 라고 합니다.


ViewContoller의 유일한 추가 책임은 데이터 로드를 시작하는 것입니다.( ex: pull to refresh 또는 새로운 화면으로 이동할 때) 그러나 ViewController는 controller-owned 방식에서 수행한 것 처럼, 네트워크 요청으로 받은 데아터를 처리하지 않습니다. 대신 네트워크에서 가져온 데이터는 Store에 들어가게 됩니다. ViewController는 모델의 변경을 관찰하여 이 변경사항에 대한 알림을 받습니다.


이 접근접의 큰 장점은, 데이터를 공유하고 변경사항을 앱 전체에 전달하는 것이 쉽다는 것입니다. 모든 ViewController는 저장소(store)에서 데이터를 가져와, 변경 알림(change notifications)을 구독합니다. (모델에 변경이 생기면 알림을 받겠다는 뜻) 이는 앱의 여러 부분이 동일한 데이터를 독립적으로 표시하거나 변경하더라도 데이터 일관성을 보장하게 됩니다. 


물론 model-owned networking코드베이스에는 복잡성이 더해집니다. 하지만 이는 이 디자인 패턴때문이라고는 할 수 없습니다. 



Fetching Initial Data

일단 controller-owned networking에서 했듯이, ViewDidLoad에서 초기데이터를 가져옵니다. (위에서 말했다시피, model-owned networking에서 VIewController의 유일한 책임은 데이터 로드를 시작하는 것이랬죠?)


기존 MVC버전에서와 마찬가지로 Store는 저장(save)한 후에, notification을 보냅니다. 이 notification을 받은 viewController는 TableView를 업데이트 하게 되죠.



Making Changes

model-owned networking버전에서, 데이털르 변경하는 작업은 기존 MVC버전과 동일합니다. 예를들어, TableView의 data source 메소드를 통해 row를 제거하고, 변경내용을 저장하고, 모델의 변경 notification에 대한 응답으로 (viewController) 는 TableView를 업데이트 합니다. 


이는 model-owned networking이 마치 로컬 데이터인 것 처럼 store의 데이터와 상호작용하고, 모델 레이어 내에서 네트워크가 발생하는방식을 보여줍니다.

(여기서는) model-owned networking구현에서는 MVC버전의 모델 레이어를 가능한 한 변경하지 않고, store와 별도로 웹 서비스를 추가하기로 결정했다고 함.


이 웹서비스는 로컬 store의 변경사항을 수신하기 위해서 ViewController와 동일한 모델 변경 notification을 관찰(observes)합니다.


notification에서 변경사항을 웹서비스 클래스에 커밋하는데 필요한 모든정보(ex: 업데이트 또는 삭제)는 물론, 변경된 항목의 현재 속성(attributes)도 추출합니다. 이 모든 정보는 PendingItem구조체에 저장됩니다. 보류중인 다른 변경사항이 계속 실행중이거나, 커밋되기를 기다리고 있을 수도 있고, 현재 연결이 없을 수도 있기때문에 이 구조체를 queue에 추가합니다. 마지막으로 processChanges를 호출하여 queue의 처리를 시작합니다. 


processChanges구현은 queue에서 첫번째 항목을 가져와서 서버에 커밋하려고 시도하고 응답을 처리합니다. 응답이 오류인 경우에는 오류를 처리하고, 성공인 케이스에는 queue에서 item을 제거하고, 변경 notification을 보내 다른 컴포넌트가 item의 새로운 커밋 상태에 대응 할 수 있도록 합니다. 



discussion

아키텍쳐적인(?) 관점에서 볼 때, 위에 제시된 두가지 접근 방식(controller-owned networking, model-owned networking)의 주요 차이점은 데이터 소유권입니다. controller-owned networking 에서 데이터는 ViewController에 의해 로컬로 소유되지만, model-owned networking에서는 모델 레이어의 일부 엔티티(여기서는 Store)가 데이터를 소유합니다. (네트워크 "요청"을 했던건 웹 서비스 클래스, "소유"한건 Store!


ViewController에 있는 네트워킹 코드는 대게 anti-pattern으로 설명됩니다. (anti-pattern : "소프트웨어 공학에서, 안티-패턴이란 흔히 사용되기도 하는 패턴이지만 실무적으로는 비효율적인 그리고/또는 생산적이지 못한 패턴을 말한다.") 그러나 네트워크의 데이터가 로컬 view-state로만 사용되며, 다른 컴포넌트와 커뮤니케이션 할 필요가 없는 경우, 원칙적으로 문제가 되지 않습니다. 네트워크 코드의 대부분이 helper functions으로 적절히 분해되면, 이 접근법은 ViewController를 그렇게 많이 무겁게 만들지 않습니다. 


자, 이렇게 되면 중요한 질문은 이겁니다. 

데이터를 공유해야되냐!?!?!?!?!?!


데이터를 공유해야하는 즉시, model-owned networking은 자연스러운 접근방식입니다. controller 레이어 외부의 엔티티가 데이터를 소유하고 있으므로, 데이터 불일치가 발생하지 않고, 앱에서 쉽게 공유 할 수 있습니다. 이는 view-state와 유사합니다. 

view-state가 하나의 view 또는 하나의 viewController에만 관련이 있다면 로컬에 저장해도 잘 동작합니다. 그러나 다른 컴포넌트가 동일한 state에 의존하는 즉시, 우리는 그것을 관찰 할 수 있는 공유 엔티티로 이동해야합니다. 



이 장에서 설멸한 두가지 네트워킹 방법은 MVC에만 적용되는 것이 아니라 MVVM과 같은 패턴에도 적용됩니다. 차이점은 controller-owned networking은 MVVM에서 view-model-owned networking이 되고, 구현은 매우 유사합니다. 


그러나 controller-owned networking와 model-owned networking의 구분은 MVC+View-State, TEA 또는 MAVB(ModelAdapter-ViewBinder)같은 패턴에는 적용되지 않습니다. 예를들어, MVC+View-state의 전제는 view-state를 모델의 일부로 명시적으로 표현하는 것입니다. controller-owned networking에서의 데이터는 view-state의 일부이기 때문에 즉시 모델 레이어에 push됩니다. 이러한 맥락에서 (MVC+View-State에서는) controller-owned networking과 model-owned networking의 구분은 의미가 없습니다. 

TEA 및 MAVB에는 controller-owned networking방식을 구현 할 수 있는 ViewController가 없습니다. 따라서 네트워킹 코드는 자연스럽게 모델 레이어로 이동하여 view가 바인딩된 상태(MAVB) 또는 가상 view(virtual views)가 앱의 상태를 업데이트 합니다. 


출처 : objc.io App-architecture 

반응형