Skip to content

Architecture Patterns

HoonHa edited this page Nov 23, 2021 · 1 revision

Architecture Patterns

만들고자 하는 앱에 가장 적절한 아키텍처에 대한 고민

고려사항

  • 유지보수 용이
  • 테스트 용이
  • 서비스의 특성, 규모 고려

MVVM-C 선택한 이유

  • View 와 ViewModel 완전히 독립적, 객체간 역할이 명확해짐

  • 코드의 재사용성

  • 간결해진 ViewController

  • 유지보수 용이, Unit Test 용이

  • Coordinator 뷰컨트롤러 계층을 관리하는 객체를 두어 ViewController의 생성,이동 역할을 분리


의존성 주입을 한곳에서 처리하는 Composition Root 활용

Coordinator 의존성 주입 대한 고민
  • ViewModel에 필요한 Network Layer, DB Repository 를 필요한 곳마다 객체를 생성 또는 전달해줘야하는 이슈
// 예시
class MainCoordinator: Coordinator { 
  var nav: UINavigationController 
  
  init(nav: UINavigationController) { 
    self.nav = nav 
  } 
  
  func start() {
    let networkManager = NetworkManager()
    let dbManager = dbManager()
    let vc = MainViewController.instantiate(storyboardName: "Main")
    let vm = MainViewModel(networkLayer: networkManager,
                           dbLayer: dbManager)
    nav.pushViewController(vc, animated: false) 
  } 
}
Composition Root 활용한 코드
  • AppDependency 안에 필요한 모든 의존성 객체들을 생성 후 주입
  • MainCoordinator 화면 이동의 역할을 명확하게 해줄수 있음
// 코드 예시
struct AppDependency {
   let networkManager = NetworkManager(session: URLSession.shared)
 
   private func makeMainCoordinator() -> MainCoordinator {
        return MainCoordinator(dependency: .init(mainViewControllerFactory: makeMainViewController)
    }
  
   private func makeMainController() -> MainViewController {     
        let mainViewModel = MainViewModel(service: networkManager)
        let mainViewController = MainViewController(dataSource: coinDataSource)

        mainViewModel.failErrorHandler = mainViewController.showError
        mainViewModel.coinsHandler = mainViewController.updateCoinList
				.....
        return mainViewController
    }
}


class MainCoordinator: Coordinator { 
  var nav: UINavigationController 
  
  struct Dependency {
       let mainViewControllerFactory: () -> MainViewController
  }
  
  private let mainViewController: MainViewController
  
  init(nav: UINavigationController, dependency: Dependency) { 
    	nav = nav 
    	mainViewController = dependency.mainViewControllerFactory()
  } 
  
  func start() {
    	nav.pushViewController(mainViewController, animated: false) 
  } 
}
ViewController 안에 ViewModel 의존하지 않고 바인딩
  • ViewController 안에 ViewModel 관련 코드 없이 Closure으로 바인딩 하는 이유

    • ViewController의 역할을 좀 더 명확하게 할 수 있다.

    • ViewController, ViewModel의 코드를 재사용 용이

    • ViewController, ViewModel 서로 관련이 없기 때문에 테스트 용이


TradingLog(일지 작성) Logic Redux 단방향 데이터 흐름 구조 적용

Redux 단방향 데이터 흐름 구조를 적용한 이유
  • 많은 Action, 많은 상태를 ViewModel과 바인딩 시 코드 증가 및 복잡성 증가
  • State(UI 상태,데이터 상태 등)를 **중앙(Store)**에 전달하며 모든 변화를 총괄, 상태에 따라 UI를 변화시키고 변화된 데이터를 저장
  • **중앙(Store)**에서 데이터 흐름을 단방향으로 관리하면서 UI 변화나 데이터 상태의 변화를 쉽게 파악할 수 있는 장점
스크린샷 2021-11-23 오후 7 40 39 스크린샷 2021-11-05 오후 4 53 12
Redux 사용하면서 느낀 단점
  • 러닝 커브 높다
  • 개념을 이해하는데 시간 소요
  • iOS에서는 많이 쓰이지 않아 UIKit에 참고할 만한 예제도 찾기 어려웠다
Redux를 통해 느낀 이점
  • 유지보수에 용이함을 느낌
  • 테스트 작성에도 용이함을 느낌