Spring

[Spring 완전 정복 시리즈] 4편 - 좋은 객체 지향 설계의 5가지 원칙 (SOLID)

dev-nadan 2025. 7. 23. 18:31

1. 왜 원칙이 필요한가?

객체 지향은 단순히 클래스를 만들고 상속을 사용하는 것이 아닙니다. 제대로 된 객체 지향 설계를 하려면 변경에 강하고, 확장에 유연하며, 테스트가 쉬운 코드를 작성할 수 있어야 합니다.

이를 실현하기 위한 설계 원칙이 바로 SOLID 원칙입니다.


2. SOLID 5가지 원칙 정리

1) SRP - 단일 책임 원칙 (Single Responsibility Principle)

  • 클래스는 단 하나의 책임만 가져야 함
  • 변경이 발생했을 때 수정의 이유가 오직 하나여야 함
// Bad
class ReportManager {
    void generateReport() {}
    void saveToFile() {}
    void sendEmail() {}
}
// Good
class ReportGenerator {}
class ReportSaver {}
class EmailSender {}

2) OCP - 개방/폐쇄 원칙 (Open/Closed Principle)

  • 확장에는 열려있고, 변경에는 닫혀 있어야 함
  • 새로운 기능이 필요할 때 기존 코드를 변경하지 않고 확장할 수 있어야 함
interface DiscountPolicy {
    int discount(int price);
}

class FixDiscountPolicy implements DiscountPolicy { ... }
class RateDiscountPolicy implements DiscountPolicy { ... }

3) LSP - 리스코프 치환 원칙 (Liskov Substitution Principle)

  • 자식 클래스는 부모 클래스를 대체할 수 있어야 함
  • 부모가 동작하던 자리에 자식 클래스를 넣어도 정상적으로 동작해야 함

4) ISP - 인터페이스 분리 원칙 (Interface Segregation Principle)

  • 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 함
  • 인터페이스는 작고 명확하게 쪼개는 것이 좋음

5) DIP - 의존 역전 원칙 (Dependency Inversion Principle)

  • 고수준 모듈은 저수준 모듈에 의존하지 않고 추상화에 의존해야 함
  • 스프링의 DI(Dependency Injection) 는 이 원칙을 실현하는 대표적인 도구
// Bad - 직접 구현체 의존
class OrderService {
    private final MemoryOrderRepository repository = new MemoryOrderRepository();
}

// Good - 추상화에 의존
class OrderService {
    private final OrderRepository repository;

    public OrderService(OrderRepository repository) {
        this.repository = repository;
    }
}

3. 스프링이 SOLID를 실현하는 방법

  • SRP → Bean을 역할별로 나누어 설계
  • OCP → 설정만 바꿔 구현체 변경 가능
  • LSP → 인터페이스를 기준으로 하위 클래스도 유연하게 설계
  • ISP → 인터페이스 단위의 컴포넌트 설계
  • DIP → DI 컨테이너로 의존성 주입, 추상화에 의존

4. 결론

SOLID 원칙은 객체 지향 설계의 뼈대이자 기준입니다. 이 원칙을 따르면 유지보수하기 쉬운 코드를 만들 수 있고, 스프링 프레임워크의 DI 컨테이너와 만나며 그 진가를 발휘합니다.