J

[Swift] DI, DIP ๋ณธ๋ฌธ

iOS/Swift

[Swift] DI, DIP

yujaehui 2024. 8. 17. 14:32

๐ŸŽฃ DI (Dependency Injection) — ์˜์กด์„ฑ ์ฃผ์ž…

๊ฐœ๋…

  • ๊ฐ์ฒด๊ฐ€ ์ž์‹ ์ด ์‚ฌ์šฉํ•  ๊ฐ์ฒด(์˜์กด์„ฑ)๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ์‹.
  • ๊ฐ์ฒด ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ํ…Œ์ŠคํŠธ๋‚˜ ํ™•์žฅ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Œ.

Swift ์˜ˆ์ œ

protocol NetworkService {
    func fetchRandomNumber() async -> Int?
}

class RealNetworkService: NetworkService {
    func fetchRandomNumber() async -> Int? {
        let urlString = "http://www.randomnumberapi.com/api/v1.0/random"
        guard let url = URL(string: urlString) else { return nil }

        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            let numbers = try JSONDecoder().decode([Int].self, from: data)
            return numbers.first
        } catch {
            return nil
        }
    }
}

class MockNetworkService: NetworkService {
    func fetchRandomNumber() async -> Int? {
        // ํ•ญ์ƒ ๊ณ ์ •๋œ ๊ฐ’ ๋ฐ˜ํ™˜ (์˜ˆ: 42)
        return 42
    }
}

// ์˜์กด์„ฑ ์ฃผ์ž…์„ ๋ฐ›๋Š” ์ชฝ
class RandomNumberViewModel: ObservableObject {
    private let networkService: NetworkService
    @Published var number: Int?

    init(service: NetworkService) {
        self.networkService = service
    }

    @MainActor
    func loadNumber() async {
        self.number = await networkService.fetchRandomNumber()
    }
}

// ์‹ค์ œ ์‚ฌ์šฉ
let realVM = ViewModel(networkService: RealNetworkService())
let mockVM = ViewModel(networkService: MockNetworkService())

์ข…๋ฅ˜

  • ์ƒ์„ฑ์ž ์ฃผ์ž… (Constructor Injection) → ์œ„ ์˜ˆ์‹œ
  • ์†์„ฑ ์ฃผ์ž… (Property Injection)
  • ๋ฉ”์„œ๋“œ ์ฃผ์ž… (Method Injection)
 

๐ŸŽฃ DIP (Dependency Inversion Principle) — ์˜์กด ์—ญ์ „ ์›์น™

๊ฐœ๋… (SOLID ์›์น™ ์ค‘ ํ•˜๋‚˜)

  • ์ƒ์œ„ ๋ชจ๋“ˆ(๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง)์ด ํ•˜์œ„ ๋ชจ๋“ˆ(๊ตฌํ˜„์ฒด)์— ์˜์กดํ•˜์ง€ ์•Š๊ณ ,
  • ๋‘˜ ๋‹ค ์ถ”์ƒํ™”(์ธํ„ฐํŽ˜์ด์Šค, ํ”„๋กœํ† ์ฝœ)์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค.

์ฆ‰, ๊ตฌ์ฒด์ ์ธ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ์ถ”์ƒํ™”๋œ ํƒ€์ž…(Protocol)์— ์˜์กดํ•˜๋ผ๋Š” ์›์น™.

Swift์—์„œ์˜ ์ ์šฉ

// ViewModel์€ ๊ตฌ์ฒด์ ์ธ RealNetworkService์— ์˜์กดํ•˜์ง€ ์•Š์Œ
// NetworkService๋ผ๋Š” ์ถ”์ƒํ™”์— ์˜์กด
protocol NetworkService {
    func fetchRandomNumber() async -> Int?
}

class RealNetworkService: NetworkService {
    func fetchRandomNumber() async -> Int? {
        let urlString = "http://www.randomnumberapi.com/api/v1.0/random"
        guard let url = URL(string: urlString) else { return nil }

        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            let numbers = try JSONDecoder().decode([Int].self, from: data)
            return numbers.first
        } catch {
            return nil
        }
    }
}

class RandomNumberViewModel: ObservableObject {
    private let networkService: NetworkService
    @Published var number: Int?

    init(service: NetworkService) {
        self.networkService = service
    }

    @MainActor
    func loadNumber() async {
        self.number = await networkService.fetchRandomNumber()
    }
}
  • ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ViewModel์€ NetworkService์˜ ๊ตฌํ˜„์ด ๋ญ”์ง€ ์•Œ ํ•„์š” ์—†์Œ
  • ํ…Œ์ŠคํŠธ ์‹œ MockNetworkService๋ฅผ ์ฃผ์ž…ํ•ด๋„ ๋ฌธ์ œ ์—†์Œ
  • DIP๋ฅผ ๋งŒ์กฑํ•˜๋ฉด DI๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง€๊ณ , DI๋Š” DIP๋ฅผ ์‹คํ˜„ํ•˜๋Š” ์ˆ˜๋‹จ์ด ๋˜๊ธฐ๋„ ํ•จ

DI, DIP ์š”์•ฝ

DI "์˜์กด์„ฑ ์ฃผ์ž…" – ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋ฐ›์Œ
DIP "์˜์กด ์—ญ์ „ ์›์น™" – ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์ด ์ €์ˆ˜์ค€ ๊ตฌํ˜„์ด ์•„๋‹Œ ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•จ
๊ด€๊ณ„ DIP๋ฅผ ์‹คํ˜„ํ•˜๊ธฐ ์œ„ํ•ด DI๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ

๐Ÿ“Œ ์‹ค์ œ ์•ฑ ๊ตฌ์กฐ์—์„œ ์–ด๋–ป๊ฒŒ ์“ฐ์ผ๊นŒ?

์˜ˆ: MVVM ์•„ํ‚คํ…์ฒ˜์—์„œ

class SomeView: View {
    @StateObject var viewModel: SomeViewModel

    init(service: NetworkService) {
        _viewModel = StateObject(wrappedValue: SomeViewModel(service: service))
    }
}
  • SomeViewModel์€ NetworkService๋ผ๋Š” ์ถ”์ƒํ™”์— ์˜์กด
  • ์˜์กด์„ฑ์€ View ๋˜๋Š” ์™ธ๋ถ€ DI Container์—์„œ ์ฃผ์ž…
  • ํ…Œ์ŠคํŠธ์—์„œ๋„ MockNetworkService ์ฃผ์ž… ๊ฐ€๋Šฅ

'iOS > Swift' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Swift] JSONEncoder.KeyEncodingStrategy  (0) 2024.06.27
[Swift] @available  (0) 2024.05.19
[Swift] #available  (0) 2024.05.19
[Swift] ๋ฒ„์ „ ๋Œ€์‘ Wrapper  (0) 2024.05.19
[Swift] Property Wrapper  (0) 2024.04.29