C#와 SOLID 원칙

발행: (2026년 3월 7일 AM 02:34 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

소프트웨어 개발에서, 로버트 C. 마틴이 정의한 SOLID 원칙은 유연하고 가독성이 높으며 유지보수가 쉬운 객체‑지향 시스템을 만들기 위한 다섯 가지 기본 지침입니다. C#에서는 이러한 원칙이 클래스 설계 규칙으로 흔히 사용됩니다.

단일 책임 원칙 (SRP)

클래스는 하나의 책임만 가져야 하며, 즉 변경되는 이유도 하나만 있어야 합니다. 클래스가 여러 작업을 수행하게 되면(예: 데이터를 저장하고 보고서를 생성) 유지보수가 어려워지고 오류가 발생하기 쉬워집니다.

개방/폐쇄 원칙 (OCP)

클래스는 확장에 열려 있어야 하지만 수정에는 닫혀 있어야 합니다. 새로운 동작은 기존 클래스 코드를 변경하지 않고 추가되어야 하며, 일반적으로 추상 클래스나 인터페이스를 사용합니다.

// 잘못된 예: 새로운 결제 수단을 추가하려면 클래스를 수정해야 함
public class PaymentService
{
    public void Pay(string type)
    {
        if (type == "CreditCard") { /* ... */ }
        else if (type == "PayPal") { /* ... */ }
    }
}

// 올바른 예: 새로운 수단을 추가하려면 새로운 클래스를 생성해야 함
public interface IPayment
{
    void Pay();
}

public class CreditCardPayment : IPayment
{
    public void Pay() { /* ... */ }
}

public class PayPalPayment : IPayment
{
    public void Pay() { /* ... */ }
}

Liskov Substitution Principle (LSP)

파생 클래스는 기본 클래스를 대체할 수 있어야 합니다. 서브클래스는 기본 클래스가 기대되는 모든 상황에서 올바르게 동작해야 하며, 기본 클래스의 계약을 깨서는 안 됩니다.

public abstract class Bird
{
    public abstract void Fly();
}

public class Sparrow : Bird
{
    public override void Fly() => Console.WriteLine("Sparrow is flying");
}

// Wrong: Penguins cannot fly, but Fly() is forced
public class Penguin : Bird
{
    public override void Fly() => throw new NotImplementedException();
}

Penguin 클래스는 Bird 추상화가 모든 새가 날 수 있다고 가정하기 때문에 LSP를 위반합니다. 더 나은 설계는 비행 새와 비비행 새를 IFlyingBirdINonFlyingBird와 같은 별개의 인터페이스로 구분하는 것입니다.

인터페이스 분리 원칙 (ISP)

클라이언트가 사용하지 않는 인터페이스에 의존하도록 강요받아서는 안 됩니다. 크고 “뚱뚱한” 인터페이스보다 작고 집중된 인터페이스를 선호하세요.

// Wrong: All animals are forced to walk, fly, and swim
public interface IAnimal
{
    void Walk();
    void Fly();
    void Swim();
}

// Correct: Split into smaller interfaces
public interface IWalkable { void Walk(); }
public interface IFlyable { void Fly(); }
public interface ISwimmable { void Swim(); }

public class Dog : IWalkable, ISwimmable
{
    public void Walk() { /* ... */ }
    public void Swim() { /* ... */ }
}

Dependency Inversion Principle (DIP)

고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 두 모듈 모두 추상화(인터페이스 또는 추상 클래스)에 의존해야 합니다. 이 원칙은 의존성 주입의 기반이 됩니다.

// Wrong: Service depends directly on a concrete logger
public class Service
{
    private readonly ConsoleLogger _logger = new ConsoleLogger();
}

// Correct: Service depends on an abstraction
public interface ILogger
{
    void Log(string msg);
}

public class ConsoleLogger : ILogger
{
    public void Log(string msg) => Console.WriteLine(msg);
}

public class Service
{
    private readonly ILogger _logger;
    public Service(ILogger logger) => _logger = logger;
}

SOLID 적용의 장점

  • 코드 가독성과 유지보수성 향상
  • 변경에 강한 아키텍처 구축
  • 단위 테스트 간소화
  • 코드 중복 감소 및 재사용성 증가

원칙 요약

  • SRP – 클래스는 하나의 책임만 가져야 합니다.
  • OCP – 확장은 열려 있어야 하고, 수정은 닫혀 있어야 합니다.
  • LSP – 서브클래스는 기반 클래스와 교체 가능해야 합니다.
  • ISP – 작고 집중된 인터페이스를 선호합니다.
  • DIP – 구체적인 구현이 아니라 추상에 의존합니다.
0 조회
Back to Blog

관련 글

더 보기 »

C#와 F#의 주요 차이점은 무엇인가요?

소개 종종 우리 .NET 클라이언트가 이 질문을 합니다: C를 사용해야 할까요, 아니면 F를 사용해야 할까요? 두 언어 모두 동일한 .NET 런타임에서 실행되며 동일한 라이브러리에 접근합니다.

특수 함수 및 Pimpl Idiom

이점 이점은 컴파일 시간 감소와 헤더 관리 용이성 형태로 제공됩니다. 컴파일 시간이 감소하는 이유는, 매번 선언하는 대신…