비관리형(unmanaged) 개체를 해제(dispose)해야 하는 상황이라면 IDisposable 디자인 패턴(Dispose 패턴)을 사용하는 것이 바람직합니다.
이 패턴을 구현하려면 비관리형 개체를 직접 혹은 간접적으로 다루는 클래스가 IDisposable 인터페이스를 구현하고, 해당 인터페이스에 선언된 Dispose 메서드를 작성해야 합니다. 대부분의 경우 개발자가 비관리형 개체를 직접 다루지는 않으며, 대신 비관리형 개체를 내부적으로 처리하는 관리형(managed) 클래스를 사용하게 됩니다. 예를 들어 파일 핸들러(File Handler), 데이터베이스 연결 문자열(Connection String), HTTP 스트림 등이 대표적인 예입니다.
이 패턴의 중요한 특징 중 하나는 재정의 가능한(overridable) Dispose 메서드를 제공하여, 이 클래스를 상속받는 하위 클래스들이 Dispose 패턴을 손쉽게 따를 수 있도록 한다는 점입니다. 또한 이 패턴은 종료자(Finalizer, C#에서는 소멸자라고 부름)의 사용을 권장하지만, 종료자는 가비지 컬렉션 과정에서 성능 저하를 유발할 수 있으므로 반드시 신중하게 관리하면서 사용해야 합니다.
구현 예제
static class Program {
static void Main(string[] args) {
using var serviceProxy = new ServiceProxy(null);
serviceProxy.Get();
serviceProxy.Post("");
Console.ReadLine();
}
}
public class ServiceProxy : System.IDisposable {
private readonly HttpClient httpClient;
private bool disposed;
public ServiceProxy(IHttpClientFactory httpClientFactory) {
httpClient = httpClientFactory.CreateClient();
}
~ServiceProxy() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposed) {
return;
}
if (disposing) {
// Dispose managed objects
httpClient.Dispose();
}
// Dispose unmanaged objects
disposed = true;
}
public void Get() {
var response = httpClient.GetAsync("");
}
public void Post(string request) {
var response = httpClient.PostAsync("", new StringContent(request));
}
}
코드 핵심 포인트 정리
disposed플래그: Dispose 로직이 여러 번 실행되는 것을 방지하여 멱등성(idempotency)을 보장합니다. 이미 해제된 객체에 대해 다시 호출되더라도 아무 작업 없이 즉시 반환됩니다.Dispose(bool disposing)오버로드: 매개변수 값에 따라 관리형 리소스와 비관리형 리소스의 해제 시점을 구분합니다.true일 때는 관리형 개체(httpClient.Dispose())까지 함께 해제하고, 종료자에서 호출될 때(false)는 비관리형 리소스만 처리합니다.GC.SuppressFinalize(this):Dispose()가 이미 명시적으로 호출된 경우, 가비지 컬렉터가 불필요하게 종료자(Finalizer)를 실행하지 않도록 하여 성능 오버헤드를 줄여줍니다.protected virtual선언: 하위 클래스에서 자유롭게 재정의할 수 있도록 하여, 상속 구조에서도 Dispose 패턴이 일관되게 유지되도록 합니다.using문 활용: 예제의Main메서드처럼using선언을 사용하면 코드 블록이 끝나는 시점에Dispose()가 자동으로 호출되므로, 리소스 누수를 예방하는 가장 안전한 방법입니다.
이처럼 IDisposable 디자인 패턴을 올바르게 구현하면 관리형·비관리형 리소스를 체계적으로 정리할 수 있고, 상속 계층 전반에서 일관된 리소스 해제 동작을 보장할 수 있습니다.