재사용 가능한 서비스(Composable Services) 설계 원칙

Design Principle: Composable Services

작성자
발행일
2025년 08월 14일

핵심 요약

  • 1 Composable Services는 작고 예측 가능하며 독립적으로 실행 가능한 단위로, 균일한 인터페이스를 통해 입력과 결과를 처리합니다.
  • 2 각 서비스는 해시 입력을 받아 Success() 또는 Failure() 결과를 반환하며, 이는 재사용성, 조합 가능성 및 시스템 확장성을 높입니다.
  • 3 이러한 설계 원칙은 복잡성과 숨겨진 결합을 방지하고, 장기적인 유연성과 유지보수성을 보장합니다.

도입

대규모 시스템에서 복잡성과 숨겨진 결합을 최소화하기 위해 '재사용 가능한 서비스(Composable Services)' 설계 원칙이 도입되었습니다. 이 원칙은 Looping 프로젝트의 핵심 아키텍처 지침으로, 시스템의 명확성, 유지보수성, 그리고 장기적인 유연성 확보를 목표로 합니다. 각 서비스는 작고 예측 가능한 독립적 단위로, 균일한 인터페이스를 통해 입력과 결과를 처리하며, 이는 시스템의 확장성을 높이는 데 기여합니다.

Composable Services의 핵심은 ‘균일한 인터페이스’에 있습니다. Looping의 모든 서비스는 ApplicationService 기본 클래스를 상속받아 엄격한 패턴을 따릅니다. 서비스는 Class.call(payload) 방식으로 호출되며, payload는 항상 일반 해시 형태입니다. 결과는 Dry::Monads::Result 타입으로 Success(value) 또는 Failure(error) 중 하나를 반환하며, call 메서드는 private으로 설정되어 캡슐화를 강화합니다. 이러한 일관성은 서비스 간의 상호 교환성과 격리된 테스트 용이성을 보장합니다. 실제 예시로, 사용자 로그인 흐름은 EmailService::Normalizer, UserService::Authenticator, SessionService::Creator 세 가지 서비스의 조합으로 이루어집니다. 각 서비스는 이메일 정규화, 사용자 인증, 세션 생성과 같은 단일 책임을 가지며, 독립적으로 실행되고 Success 또는 Failure를 반환합니다. 이들은 서로의 내부 로직을 알 필요 없이 정해진 인터페이스를 통해 데이터를 주고받으며 쉽게 조합됩니다. 또한, ApplicationService의 초기화 시 Current.sources 스택에 실행된 서비스의 클래스 이름을 기록하는 메커니즘을 통해, 서비스 조합 과정에서 어떤 코드 경로가 실행되었는지 추적할 수 있습니다. 이는 요청 로그, 오류 보고서 등에서 상세한 실행 흐름을 파악하는 데 유용하며, 추가적인 로깅 없이도 구조화된 관찰 가능성(observability)을 제공합니다.

결론

Composable Services는 복잡하고 재사용이 어려운 코드베이스의 문제를 해결하며, '단 하나의 조건문 추가' 대신 서비스 추출과 의도적인 로직 조합을 장려합니다. 각 서비스가 일관된 형태와 단일 책임을 가질 때, 저수준 작업을 조합하여 고수준의 기능을 쉽게 구축할 수 있습니다. 이는 장기적인 유연성과 제품 성장에 따른 시스템 적응력을 보장하며, 유지보수 및 확장성이 뛰어난 견고한 시스템 구축에 필수적인 설계 원칙입니다.

댓글 0

댓글 작성

0/1000
정중하고 건설적인 댓글을 작성해 주세요.

아직 댓글이 없습니다

첫 번째 댓글을 작성해보세요!