본문 바로가기
.NET

IServiceCollection를 확장하는 메서드

by leo21c 2026. 6. 16.

## 1. IServiceCollection이란 무엇인가요?

IServiceCollection은 .NET 8 프레임워크가 제공하는 '애플리케이션 부품(서비스) 등록 장부'입니다.

디자인 패턴에서는 이를 IoC (Inversion of Control, 제어의 역전) 컨테이너 또는 DI (Dependency Injection, 의존성 주입) 컨테이너라고 부릅니다.

  • 레거시 방식 (C++/Qt/WinForm 스타일): 통신 클래스나 암호화 클래스가 필요하면 개발자가 코드 곳곳에서 new BLEService()나 싱글톤 포인터 BLEService::GetInstance()를 직접 호출해 객체를 만들었습니다. 이는 모듈 간 결합도를 극도로 높여 장비 시뮬레이터를 붙이거나 모바일로 이식할 때 코드를 다 갈아엎어야 하는 주원인이 됩니다.
  • IServiceCollection 방식: 프로그램이 시작될 때(HostApplicationBuilder 단계) "앞으로 우리 프로그램은 이 통신 클래스와 이 암호화 클래스를 사용할 거야"라고 IServiceCollection 장부에 먼저 등록해 둡니다. 그러면 프레임워크가 객체의 생명주기를 알아서 관리하며 필요한 곳에 자동으로 꽂아줍니다.

## 2. IServiceCollection 등록 메서드 종합 가이드

등록 메서드 종류 생명주기
(Lifetime)
설명 및 특징 통신 시스템 활용 예시
AddSingleton<T> Single
(앱 실행 동안 단 하나)
프로그램이 켜져서 꺼질 때까지 단 하나의 메모리 인스턴스만 공유합니다. BleDeviceWatcherService (무선 통신 연결 및 감시자)

• 전역 암호화 엔진 (AesCryptoProvider)
AddTransient<T> Transient
(요청 시마다 생성)
부품을 요청할 때마다 매번 new로 새로 만들고 가비지 컬렉터(GC)가 수거합니다. • 장치 세팅 화면 UI/ViewModel

• 일회성 패킷 분석 및 직렬화 컴포넌트
AddScoped<T> Scoped
(범위/세션 단위)
하나의 요청 세션(Scope) 내에서만 동일 인스턴스를 보장합니다. • 클라이언트가 접속하여 유지되는 통신 세션별 처리 컨텍스트
AddHostedService<T> Singleton 기반 백그라운드 앱 시작 시 자동으로 실행되어 background 스레드로 동작하는 데몬 서비스입니다. ExecuteAsync 루프를 돌며 장치의 이벤트 로그를 주기적으로 폴링하는 통신 서비스
AddHttpClient<T> Transient + 하위 소켓 풀링 외부 Web API나 소켓 통신을 효율적으로 처리하기 위해 프레임워크가 내부 커넥션 풀을 관리해 줍니다. 상위 시스템이나 웹 서버로 계측 데이터를 REST API로 전송하는 HTTP 클라이언트 모듈
TryAddSingleton<T>

TryAddTransient<T>
조건부 등록
(동일 생명주기)
장부에 이미 동일한 이름/인터페이스의 서비스가 등록되어 있다면 무시하고, 없을 때만 등록합니다. • 공통 클래스 라이브러리(DLL) 배포 시, 개발자가 실수로 중복 등록하여 의존성 충돌이 나는 것을 방지하는 방어적 코드 작성 시 활용
TryAddEnumerable<T> 다중 구현체 등록
(동일 생명주기)
동일한 인터페이스를 상속받은 여러 개의 서로 다른 클래스를 리스트 형태로 장부에 동시 등록합니다. IProtectionRule 인터페이스 아래에 규칙들을 다중 등록하여 특정 알고리즘 적용 시

## 3. 코드로 보는 실제 작동 원리

IServiceCollection에 등록된 부품들이 어떻게 생성자 주입(Constructor Injection)을 통해 자동으로 조립되는지 흐름을 보겠습니다.

// 1. 장부(IServiceCollection)에 서비스와 UI 등록
var builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<IBLECommunication, EntecBleService>(); // 싱글톤 등록
builder.Services.AddTransient<CmrSettingViewModel>();              // Transient 등록

var host = builder.Build();

이제 뷰모델(ViewModel) 클래스를 만들 때, 생성자에 통신 인터페이스를 적어주기만 하면 프레임워크가 장부(IServiceCollection)를 뒤져서 알아서 객체를 넘겨줍니다. new 키워드가 사라집니다.

public class CmrSettingViewModel
{
    private readonly IBLECommunication _bleService;

    // 생성자 주입: .NET 8 프레임워크가 장부를 보고 EntecBleService 인스턴스를 자동으로 넘겨줌
    public CmrSettingViewModel(IBLECommunication bleService)
    {
        _bleService = bleService;
    }

    public async Task UploadSettingAsync()
    {
        // 장부에서 주입받은 통신 모듈을 안전하게 사용
        await _bleService.SendPacketAsync(new byte[] { 0x01, 0x02 });
    }
}

 


ServiceCollectionExtensions는 .NET 8 개발에서 의존성 주입(DI) 코드를 깔끔하고 모듈화된 구조로 유지하기 위해 사용하는 핵심 개발 패턴(관례)입니다.

MFC 개발자분들이 쉽게 이해하실 수 있도록 비유하자면, 여러 모듈의 초기화 기능들을 파일별로 깔끔하게 쪼개어 정리하는 '모듈별 전용 초기화 헤더/소프트웨어 컴포넌트 패키지'를 만드는 방법이라고 생각하시면 됩니다.

## 4. 이 클래스가 왜 필요한가요? (배경)

프로젝트 규모가 커지면 HostApplicationBuilder를 사용하는 App.xaml.cs나 Program.cs 파일 안에 수십, 수백 개의 서비스(BLE 통신, 데이터베이스, 암호화 알고리즘 등)를 등록하게 됩니다.

// 레거시/나쁜 예: App.xaml.cs 한 곳에 모든 등록 코드가 몰려 지저분해짐
builder.Services.AddSingleton<IBluetoothService, BluetoothService>();
builder.Services.AddSingleton<IAesEncryptionService, AesEncryptionService>();
builder.Services.AddTransient<CalculatorViewModel>();
builder.Services.AddTransient<SubstationViewModel>();
// ... 밑으로 100줄 이상 늘어남 ...

이 상태가 되면 코드가 읽기 어려워지고 유지보수가 불가능해집니다. 이를 방지하기 위해 기능별로 등록 코드를 쪼개서 분리하는 단위가 바로 ServiceCollectionExtensions 확장 메서드 클래스입니다.

## 5. MFC 개념과의 비교 (이해 돕기)

  • MFC 방식: 통신 관련 모듈들을 초기화할 때 InitCommunication(), UI 관련 초기화를 할 때 InitUILayout() 같은 전역/멤버 함수를 따로 만들어 내부에서 수동 객체 생성을 처리하곤 했습니다.
  • .NET 8 방식: IServiceCollection이라는 DI 인터페이스에 확장 메서드(Extension Method)를 붙여서 builder.Services.AddCommunicationServices() 형태로 세련되게 호출합니다.

## 6. 구현 샘플 코드

통신(BLE) 모듈 영역과 비즈니스 로직(계산기) 영역을 깔끔하게 분리하는 확장 클래스를 구현해 보겠습니다.

### 1) 확장 클래스 정의 (분리된 파일들)

C#의 static class와 this 키워드를 활용하여 기존 프레임워크 인터페이스에 새로운 메서드를 주입합니다.

using Microsoft.Extensions.DependencyInjection;

namespace Substation.DependencyInjection;

// 1. 통신 관련 서비스만 모아놓은 확장 클래스
public static class CommunicationServiceExtensions
{
    public static IServiceCollection AddCommunicationServices(this IServiceCollection services)
    {
        [cite_start]// 암호화 및 무선 통신 인프라 등록 [cite: 31, 295, 296]
        services.AddSingleton<IAesCryptoProvider, AesCryptoProvider>(); 
        services.AddSingleton<IBluetoothService, BluetoothService>();
        
        return services; // 체이닝(연속 호출)이 가능하도록 services를 반환하는 것이 관례입니다.
    }
}

// 2. 비즈니스 로직 및 UI(ViewModel) 관련 서비스만 모아놓은 확장 클래스
public static class BusinessLogicServiceExtensions
{
    public static IServiceCollection AddCalculatorBusinessServices(this IServiceCollection services)
    {
        services.AddTransient<ICalculatorEngine, CalculatorEngine>();
        services.AddTransient<MainViewModel>();
        
        return services;
    }
}

### 2) 메인 진입점에서의 활용 (App.xaml.cs 또는 Program.cs)

위와 같이 분리해 두면 메인 함수 내부가 서랍장 정리하듯 매우 명확하고 깔끔해집니다.

using Microsoft.Extensions.Hosting;
using Substation.DependencyInjection; // 확장 메서 네임스페이스 로드

var builder = Host.CreateApplicationBuilder(args);

// 프레임워크 기본 메서드처럼 당당하게 마침표(.)를 찍고 호출할 수 있습니다.
builder.Services
    .AddCommunicationServices()       // 통신 모듈 그룹 등록
    .AddCalculatorBusinessServices();  // 계산 로직 그룹 등록

var host = builder.Build();
await host.RunAsync();

 

LIST