Computer Science

Service Locator Pattern, 서비스 로케이터 패턴

호기심이 많은 mayleaf 2021. 8. 23. 02:08

이 글은 서비스 로케이터 패턴에 대해서 다루는 글입니다.

이 글에서 다루고자 하는 내용은 세 가지로

첫 번째는 서비스 로케이터에 대한 소개와 예제코드를 이용한 설명이고,

두 번째는 의존성 주입을 예제 코드를 통해서 설명한 후 서비스 로케이터와 비교하는 것이고,

세 번째는 제가 서비스 로케이터를 사용하는 방법을 소개하는 것입니다.

서비스 로케이터 패턴(Service Locator Pattern)이란?

Service Locator 패턴은 마틴 파울러가 블로그 글에서 제시한 패턴입니다.

이 패턴의 목표는 모듈화 수준을 높이는 것입니다. 클라이언트와 인터페이스사이의 의존성을 제거하는 방식으로 모듈화 수준을 높이는데요. 우리는 유연한 코드를 짜기 위해서 인터페이스를 자주 사용합니다. 그렇지만 인터페이스를 사용하다보면 어느 순간 구현체를 사용해야하죠. 당연한 이야기이지만, 인터페이스를 사용하더라도 클라이언트 클래스 내부에서 구현체를 사용한다면 클라이언트와 구현체 사이의 종속성은 사라지지 않습니다. 이는 Composition has a 관계이기 때문입니다.

 

Composition has a의 관계를 사용하면서 이런 종속성을 피하기 위해서 제시된게 바로 서비스 로케이터 패턴입니다.

서비스 로케이터는 중앙 레지스트리같은 역할을 하는데요. 서로 다른 인터페이스에 대한 구현체를 저장하고, 사용하게 도와줍니다.

이렇게 해서 클라이언트는 구현체와 더 느슨한 관계를 가지게 됩니다. 일반적인 Composition has a 관계와 다르게 서비스 로케이터를 통해 구현체를 가져오기 때문이죠.

 

예제코드

앞으로 예제코드에 대해서 보여드리려고 하는데요. dart에서 getIt라는 서비스 로케이터 패키지를 이용하여 예제코드를 짜봤습니다.

final getIt = GetIt.instance;

Future<void> setupLocator() async {
  getIt.registerSingletonAsync<Database>(() => LocalModule.provideDatabase());
  // registerSigletonAsync async 하게 서비스 로케이터에 구현체를 등록할 것이다.
}

class PostDataSoure {
  final _db = getIt.getAsync<Database>();
  // 구현체를 생성한다.
}

위 코드를 보면 setupLocator에서 데이터베이스를 서비스 로케이터에 등록을 하고, PostDataSource에서 가져다가 쓰고 있습니다.

PostDataSource는 Database의 구현체를 모르지만 서비스로케이터를 통해서 구현체를 가져다가 사용하고 있습니다.

서비스 로케이터의 단점

여기까지만 보면 서비스 로케이터는 훌륭한 패턴입니다. 하지만 서비스 로케이터는 완벽한 친구가 이니었죠.

수 많은 비난을 받았는데요. 이들을 정리하면 세 가지 정도로 줄일 수 있습니다.

첫 번째, 모든 컴포넌트들이 싱글톤 서비스 로케이터에 대한 레퍼런스를 가지고 있어야한다는 것,

두 번째, 서비스 로케이터 패턴은 테스트를 하기 어렵게 만든다는 것 

마지막으로 서비스 로케이터 패턴을 사용하다보면 인터페이스를 쉽게 바꿀 수 있어 문제가 될 법한 인터페이스변경을 야기한다는 것

이었습니다.

 

그래서 서비스 로케이터에 대한 대안이 많이 나타났는데요. 그중 대표적인 예시가 의존성 주입(Dependency Injection)입니다.

의존성 주입(Dependency Injection)

의존성 주입이란 말 그대로 의존성 주입입니다.

말이 조금 어렵게 느껴질 수 있지만 간단하게 설명하면 클라이언트에서 필요한 의존성을 동일한 클라이언트 내부에서 생성하지 않고 외부에서 set을 하거나, 생성시점에 파라미터로 받는 것입니다. 이렇게 하면 클라이언트는 구현체에 대해서 알 필요가 없습니다. 인자로 들어오는 대로 구현체를 사용할 뿐이죠. 이런 구현체와 클라이언트의 관계를 소프트웨어 공학에서는 Association has a 관계라고 합니다.

 

예제코드

class PostDataSource {
  PostDataSource(this._db);
  // 생성자를 통해서 데이터베이스 객체를 주입받는다.
  
  late Database _db;
  // PostDataSource는 데이터베이스 인터페이스만 알고, 어떤 구현체가 들어올지 알 수 없다.
  // 따라서 구현체와 PostDataSource는 완전하게 디커플링되어있다.
}


class PostStore {
  PostDataSource _postDataSoure = PostDataSource(LocalModule.provideDatabase());
  // 서비스 로케이터 패턴과 동일하게, 같은 데이터베이스 구현체를 주입해주므로 동일하게 사용할 수 있다.
}

Future<void> testPostDataSource() {
  DataBase db = TestDataBase();
  // 테스트 데이터베이스 구현체를 생성한다.
  var PostDataSource = PostDataSource(db);
  // 테스트 데이터베이스 구현체를 생성하고, PostDataSource에 파라미터로 넘긴다.
}

이 예제 코드를 보면 PostDataSource 는 Database 라는 인터페이스만 아는 상태로 구현체를 사용하고 있습니다. 테스트를 할때에도 편리하게 테스트용 구현체를 사용해서 PostDataSource를 테스트할 수 있는게 보이죠.

의존석 주입 패턴은 서비스 로케이터 패턴과 다르게 의존적인 모듈을 파라미터로 받기 때문에, 테스트용 구현체를 넘겨받을 수 있고, PostDataSource에서 별도로 참조해야하는 중앙레지스트리 싱글턴도 없습니다. 그리고 클라이언트의 인터페이스에서 Database가 필요하다는 것을 알 수 있기 때문에, 인터페이스를 함부로 바꾸지 않게 됩니다.

 

서비스 로케이터가 받는 주된 비난을 해결한 패턴이죠.

서비스 로케이터를 사용하는 방법

여기까지만 보면 왜 서비스 로케이터를 소개했는지 알기 어렵습니다.

저는 서비스 로케이터를 정말 중앙 레지스트리 역할로만 사용합니다. 서비스 로케이터에 구현체를 등록한 후, 이를 의존성 주입하는데에 사용하는데요. 의존성 주입에서 서비스 로케이터를 의존성 컨테이너로 사용한다는 관점으로도 바라볼 수 있습니다.

 

예제코드

final getIt = GetIt.instance;

Future<void> setupLocator() async {
  getIt.registerSingletonAsync<Database>(() => LocalModule.provideDatabase());
  // registerSigletonAsync async 하게 서비스 로케이터에 구현체를 등록할 것이다.
}

class PostDataSource {
  PostDataSource(this._db);
  // 생성자를 통해서 데이터베이스 객체를 주입받는다.
  
  late Database _db;
  // PostDataSource는 데이터베이스 인터페이스만 알고, 어떤 구현체가 들어올지 알 수 없다.
  // 따라서 구현체와 PostDataSource는 완전하게 디커플링되어있다.
}

class PostStore {
  PostDataSource _postDataSoure = PostDataSource(getIt.getAsync<Database>());
  // 서비스 로케이터에서 데이터베이스 구현체를 생성해서, PostDataSource에 파라미터로 넘긴다.
}

Future<void> testPostDataSource() {
  DataBase db = TestDataBase();
  // 테스트 데이터베이스 구현체를 생성한다.
  var PostDataSource = PostDataSource(db);
  // 테스트 데이터베이스 구현체를 생성하고, PostDataSource에 파라미터로 넘긴다.
}

위의 예제코드를 보면 PostDataSource has a database 관계를 유지하는 것을 알 수 있습니다. setupLocator에서는 구현체를 등록하고, 필요할때 꺼내서 사용합니다. 이로 인해 테스트용 구현체를 PostDataSource에 주입할 수 있고, 구현체의 등록 및 관리도 서비스로케이터를 셋업하는 곳에서 일괄적으로 할 수 있습니다.

마무리

서비스 로케이터와 간단한 의존성 주입의 설명을 하는 글을 썼습니다.

컴퓨터 공학과 관련된 글을 시리즈로 적고 있습니다.

궁금하거나, 하고 싶은 말이 있으시면 댓글을 통해서 말해주시길 바랍니다.

 

감사합니다.

'Computer Science' 카테고리의 다른 글

Service Locator Pattern, 서비스 로케이터 패턴  (0) 2021.08.23