Giter Club home page Giter Club logo

practice-architecture's Introduction

Sangwon Shin ๐Ÿ‘‹

Hits

who am I?

  • ๐Ÿ‘ฏ I majored in Electronic Engineering
  • ๐Ÿ“ซ Konkuk University (2015.03 ~ 2021.08)

sangwon's github stats

sangwon's github stats

practice-architecture's People

Contributors

brandnew-one avatar

Watchers

 avatar

practice-architecture's Issues

DI ๊ด€๋ จ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… - 02

2) EnviornmentObject๋ฅผ ํ†ตํ•œ DI๋Š” ์–ด๋””๊นŒ์ง€ ์ „ํŒŒ๋ ๊นŒ?

์ง€๊ธˆ๋ถ€ํ„ฐ ์˜ˆ์‹œ๋กœ ์‚ฌ์šฉํ•œ ํ”„๋กœ์ ํŠธ๋งŒ ๋ณด๋ฉด DI๋ž‘ ๋ฌด์Šจ ๊ด€๊ณ„๊ฐ€ ์žˆ๋‚˜ ์‹ถ์„ ์ˆ˜ ์žˆ์ง€๋งŒ ํ˜„์žฌ Clean-Architecture ์˜ˆ์ œ์˜ DI๋ฅผ EnvrionmentObject๋ฅผ ํ†ตํ•ด์„œ ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋™์ผํ•œ ๋‚ด์šฉ์ด๋‹ค!

import Foundation

final class AppState: ObservableObject {
  @Published var count: Int = 0
}
import SwiftUI

@main
struct DI_TestApp: App {
  var body: some Scene {
    let appDI = AppState()
    WindowGroup {
      RootView()
        .environmentObject(appDI)
    }
  }
}

์œ„์™€ ๊ฐ™์€ ObservableObject๋ฅผ EnvironmentObject๋กœ ์‚ฌ์šฉํ•˜๋ฉด AppState๋Š” ์–ด๋””๊นŒ์ง€ ์ „ํŒŒ๋ ๊นŒ?

RootView โ”œโ”€โ”€ TestView โ”€โ”€ TestView2
         โ”œโ”€โ”€ ContentView

RootView - TestView - TestView2 ๋Š” navigation push๋กœ ์Šคํƒ์ด ์Œ“์ด๋Š” ํ˜•ํƒœ์ด๊ณ 

RootView - ContentView ๋Š” UIKit ๋ฐฉ์‹์˜ RootViewController๋ฅผ ๋ฐ”๊ฟ”์ฃผ๊ณ  ์žˆ๋‹ค

import SwiftUI

struct TestView: View {
  @EnvironmentObject
  var appState: AppState

  var body: some View {
    VStack {
      NavigationLink(
        destination: { TestView2() },
        label: {
          Text("Push")
        }
      )

      Button(
        action: {
          appState.count += 1
        },
        label: {
          Text("up")
        }
      )
    }
  }
}

struct TestView2: View {
  @EnvironmentObject
  var appState: AppState

  var body: some View {
    Text("\(appState.count)")
  }
}

TestView์—์„œ EnvironmentObject์˜ count ํ”„๋กœํผํ‹ฐ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๊ณ  TestView2๋กœ ํ™”๋ฉด์ „ํ™˜๋ฅผ ํ•œ ๊ฒฝ์šฐ, ์ •์ƒ์ ์œผ๋กœ ์ฆ๊ฐ€๋œ Count๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. (EnviornmentObject์— ์ ‘๊ทผ๊ฐ€๋Šฅ)

private func rootViewChange() {
    let window = UIApplication
      .shared
      .connectedScenes
      .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
      .first { $0.isKeyWindow }

    window?.rootViewController = UIHostingController(rootView: ContentView())
    window?.makeKeyAndVisible()
  }

ํ•˜์ง€๋งŒ ํ•ด๋‹น ๋™์ž‘ ์ดํ›„, RootView์—์„œ ContentView๋กœ ํ™”๋ฉด ์ „ํ™˜์„ ์‹œ๋„ํ•˜๋ฉด ์•ฑ์ด Crash๊ฐ€ ๋‚˜๋Š”๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

struct ContentView: View {
  @EnvironmentObject
  var appState: AppState

  var body: some View {
    VStack {
      Image(systemName: "globe")
        .imageScale(.large)
        .foregroundColor(.accentColor)

      Text("Hello, world!")

      Text("\(appState.count)")
    }
    .padding()
  }
}

์ด๋Š” RootView๋ฅผ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ณผ์ •์—์„œ enviornmetObject๋ฅผ ๋‹ค์‹œ ์ฃผ์ž…ํ•ด์ฃผ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— AppState์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋”ฐ๋ผ์„œ, UIKit ๋ฐฉ์‹์˜ RootView Change๋ฅผ ์‹œ๋„ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” UIHostingViewController์— ๋‹ค์‹œ .enviornmentObject() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ ์‚ฌ์šฉํ•  ObservableObject๋ฅผ ๋‹ค์‹œ ์ฃผ์ž…ํ•ด์ฃผ๊ฑฐ๋‚˜, SwiftUI ๋ฐฉ์‹์˜ Group์„ ํ†ตํ•ด์„œ ๋ณ€๊ฒฝํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

ํ”„๋กœ์ ํŠธ๋Š” ์—ฌ๊ธฐ์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

DI ๊ด€๋ จ ํŠธ๋Ÿฌ๋ธ” ์ŠˆํŒ… - 1

DI์ค‘ ๋งŒ๋‚œ ๋ฌธ์ œ

๊นƒํ—™ ๋ ˆํฌ์— ์˜ฌ๋ฆฐ ์•„์ฃผ ์ž‘์€ ํ† ์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ฏธ์ฒ˜ ์ƒ๊ฐํ•˜์ง€ ๋ชปํ–ˆ์—ˆ๋Š”๋ฐ ํšŒ์‚ฌ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•˜๋ฉด์„œ ์ƒ๊ฐ ํ•ด๋ณผ๋งŒํ•œ ๋ฌธ์ œ๋ฅผ ๋งˆ์ฃผ์ณค๋‹ค.

1) Usecase์˜ ์ค‘๋ณต init

final class DefaultSearchMediaUseCase: SearchMediaUseCase {
  private let mediaRepository: MediaRepository
  private let mediaDetailRepository: MediaDetailRepository

  init(
    mediaRepository: MediaRepository,
    mediaDetailRepository: MediaDetailRepository
  ) {
    self.mediaRepository = mediaRepository
    self.mediaDetailRepository = mediaDetailRepository
  }
...
}
final class ~~ViewModel {
	private let usecase: SearchMediaUsecase

	init(
		usecase: SearchMediaUsecase
	) {
		self.usecase = usecase
	}
}

์—ฌ๋Ÿฌ viewModel์„ ๋งŒ๋“ค๋‹ค ๋ณด๋ฉด ์ค‘๋ณต๋˜๋Š” usecase๊ฐ€ ์ƒ๊ธด๋‹ค.

usecase์— Datatorage๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” Repository๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, Realm์ด ์—ฌ๋Ÿฌ๋ฒˆ init ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค

Realm ๊ณต์‹ ํ™ˆํŽ˜์ด์ง€์—์„œ zero-copy design์œผ๋กœ db์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์ด ๋™์ผํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ชจ๋“  ๊ฐ์ฒด์— ๋ฐ˜์˜๋œ๋‹ค๋Š” ๋‚ด์šฉ์„ ํ™•์ธํ•ด ๋Ÿฐํƒ€์ž„ ์ค‘ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€๋Š” ์•Š์•˜์ง€๋งŒ, ์–ด์ฐจํ”ผ ๋™์ผํ•œ DataStorage๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” usecase๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ viewModel์ด ๋งŒ๋“ค์–ด์งˆ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค์–ด์งˆ ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ? ํ•˜๋Š” ์˜๋ฌธ์ด ๋“ค์—ˆ๋‹ค.

import Foundation

final class AppDI: AppDIInterface {
  private let mediaUsecase: SearchMediaUseCase = DefaultSearchMediaUseCase(
    mediaRepository: MediaRepositoryImpl(),
    mediaDetailRepository: MediaDetailRepositoryImpl(RealmDataStorage.shared)
  )

  func mediaDetailViewModel(
    _ media: Media
  ) -> MediaDetailViewModel {
    return MediaDetailViewModel(
      media: media,
      usecase: mediaUsecase
    )
  }
}

๊ธฐ์กด์— ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ usecsae๋ฅผ ๋งŒ๋“ค๋˜ ํ˜•ํƒœ์—์„œ AppDI์˜ ํ”„๋กœํผํ‹ฐ์— usecase๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ AppDI๋Š” ์•ฑ์ด ์‹œ์ž‘ํ•˜๋Š” ์‹œ์ ์— Init๋˜๋Š”๋ฐ ๊ทธ๋Ÿผ ์œ ์ €๊ฐ€ ๋ชจ๋“  ํ™”๋ฉด์„ ๋Œ์•„๋‹ค๋‹ˆ์ง€ ์•Š์•„์„œ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ์™€ ์žˆ์„ ํ•„์š”๊ฐ€ ์—†๋Š” usecase๋„ ์‹œ์ž‘ํ•˜๋Š” ์‹œ์ ์— ๋ชจ๋‘ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐ€๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

import Foundation

final class AppDI: AppDIInterface {
  private lazy var mediaUsecase: SearchMediaUseCase = {
    let mediaRepository: MediaRepository = MediaRepositoryImpl()
    let mediaDetailRepository: MediaDetailRepository = MediaDetailRepositoryImpl(RealmDataStorage.shared)
    return DefaultSearchMediaUseCase(
      mediaRepository: mediaRepository,
      mediaDetailRepository: mediaDetailRepository
    )
  }()

  func mediaDetailViewModel(
    _ media: Media
  ) -> MediaDetailViewModel {
    return MediaDetailViewModel(
      media: media,
      usecase: mediaUsecase
    )
  }
}

์œ„์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ usecase๋ฅผ lazy๋ฅผ ํ†ตํ•ด์„œ ์‚ฌ์šฉ๋˜๋Š” ์‹œ์ ์— ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐ€๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

๋ฌผ๋ก , ์œ ์ €๊ฐ€ ๋ชจ๋“  ํ™”๋ฉด์„ ๋Œ์•„๋‹ค๋‹ˆ๋ฉด ํ˜„์žฌ ๋‹น์žฅ ํ•„์š”์—†๋Š” usecase๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ผ๊ฐ€๋Š” ๋ฌธ์ œ๋Š” ํ”ผํ•  ์ˆ˜ ์—†๊ฒ ์ง€๋งŒ ์•ฑ์ด ์‹œ์ž‘ํ•˜๋Š” ์‹œ์ ์— ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์€ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค.

(๋” ์ข‹์€ ์•„์ด๋””์–ด๊ฐ€ ๋– ์˜ค๋ฅด๋ฉด ์ถ”๊ฐ€ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.