SwiftUI 나 Flutter 를 사용하다보면, 상태관리에 대한 다양한 방법들을 접하게 됩니다.
SwiftUI 에서 EnvironmentObject
나 Flutter 에서의 Provider.of(context)
와 같은 것들이죠. 이 둘의 공통점은, 어디선가 "등록" 하는 과정이 있고, 그것을 "호출" 하는 과정으로 나눠져 있다는 겁니다.
여기서 적용된 패턴을 "서비스 로케이터" 라고 부릅니다.
서비스 로케이터에 대해서 정리하고 동시에 DI 와 어떤점이 다른지 궁금증이 생겨서 글을 쓰게 되었습니다.
특정 객체를 위해 위존성을 전달하는 방법에는 3 가지가 존재합니다.
1. Dependency Injection
2. Service Locator
3. Self access(or creation)
A technicque whereby one object supplies the dependencies of another object.
-위키피디아-
window = Window();
door = Door();
house = House(window, door);
struct 회원정보 {...}
class 사용자정보관리객체 {...}
이렇게 두 객체가 있다고 가정합니다. 사용자 관리 객체는 로그인 시, 서버로부터 전달받은 "회원정보" 가 있어야 동작이 가능합니다.
struct 회원정보 {
var userID: Int
var userName: String
init(userID: Int, userName: String) {
self.userID = userID
self.userName = userName
}
}
class 사용자정보관리객체 {
init(userInfo: 회원정보) {
self.userInfo = 회원정보
}
...
public func edit(userName: String) throws -> Bool {
if userName == self.userInfo.userName { throws Error.변경된정보없음 }
...(유저이름 변경 코드)...
return true
}
}
edit
를 동작시키기 위해서는 "회원정보" 구조체가 반드시 있어야 합니다.딱히 단점은 모르겠습니다.
DI 가 Service Locator 라고 오해하는 경우가 많은데, 둘은 다른 용어로 부르는게 맞습니다.
With service locator the application class asks for it explicitly by a message to the locator. With (dependency) injection there is no explicit request, the serivce appears in the application class
-Martin Fowler-
- 서비스 로케이터는, 아주 구체적이고 명확한 의존성에 대한 요청받고, 서비스 로케이터를 통해 의존성에 접근하도록 해줍니다.
- 의존성 주입은, 명시적인 요청은 없습니다. 그냥 요청자(어플리케이션) 에 있습니다.
- 추가로, 둘의 공통점은 "디커플링" 을 위한 행위라는 점입니다.
house = serviceLocator.get(House);
house = serviceLocator.get(House.self)
serviceLocator
라는 객체에게 명확히 어떤 값을 달라고 문의합니다.House
라는 타입을 등록해두어야 합니다.ServiceLocator
에게 모두 위임할 수 있고, 해당 코드가 모두 한 곳에서 관리할 수 있다는 점// 인터페이스 1
protocol Authentication {
var userID: Int { get }
}
// 인터페이스 2
protocol UserInfo {
var userName: String { get }
}
// 구현 객체 1
class LoginViewModel: Authentication {
var userID: Int { 1 }
}
// 구현 객체 2
class ProfileViewModel: UserInfo {
var userName: String { "woosung" }
}
// 서비스 로케이터 인터페이스
protocol ServiceLocating {
func getService<T>() -> T?
func addService<T>(_ service: T)
}
// 서비스 로케이터 구현 객체
class Locator: ServiceLocating {
static let shared = Locator()
private init() {}
private var services: [String: Any] = [:]
private func typeName<T>(_ service: T) -> String {
return "\(type(of: service))"
}
public func getService<T>() -> T? {
let key = typeName(T.self)
return services[key] as? T
}
public func addService<T>(_ service: T) {
let key = typeName(T.self)
services[key] = service
}
}
/* 실행코드 */
let loginViewModel = LoginViewModel()
let profileViewModel = ProfileViewModel()
let locator = Locator.shared
locator.addService(loginViewModel)
locator.addService(profileViewModel)
let loginVM: LoginViewModel = locator.getService()!
let profileVM: ProfileViewModel = locator.getService()!
print(loginVM.userID)
print(profileVM.userName)
감사합니다.