SOLID

Jerry·2025년 8월 1일

SOLID

S - Single Responsibility Principle (SRP)  
O - Open/Closed Principle (OCP)  
L - Liskov Substitution Principle (LSP)  
I - Interface Segregation Principle (ISP)  
D - Dependency Inversion Principle (DIP)

SRP - 단일 책임 원칙

클래스는 하나의 책임만 가져야 한다. (변경 이유는 하나여야 한다)

예:

public class UserService {
    public void registerUser(User user) {
        // 회원 등록
    }

    public void sendWelcomeEmail(User user) {
        // 이메일 발송 ❌ (책임이 다름)
    }
}

개선:

public class UserService {
    public void registerUser(User user) {
        // 등록
    }
}

public class MailService {
    public void sendWelcomeEmail(User user) {
        // 메일 전송
    }
}

유지보수, 테스트, 책임 분리가 쉬워짐

OCP - 개방/폐쇄 원칙

확장에는 열려 있고, 변경에는 닫혀 있어야 한다

예: 결제 방식 추가하려면 기존 코드 뜯어야 함 ❌

public class PaymentService {
    public void pay(String type) {
        if (type.equals("kakao")) { ... }
        else if (type.equals("payco")) { ... }
    }
}

개선: 인터페이스 기반 설계

public interface PaymentStrategy {
    void pay();
}

public class KakaoPay implements PaymentStrategy { ... }
public class Payco implements PaymentStrategy { ... }

public class PaymentService {
    private final PaymentStrategy strategy;
    public PaymentService(PaymentStrategy strategy) {
        this.strategy = strategy;
    }
    public void executePay() {
        strategy.pay();
    }
}

기능 추가는 클래스만 추가하면 되고, 기존 코드는 안 건드림

LSP - 리스코프 치환 원칙

서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다

예: 부모 타입을 사용하는 코드가 자식 클래스에서 깨지면 ❌

public class Bird {
    public void fly() { ... }
}

public class Ostrich extends Bird {
    public void fly() {
        throw new UnsupportedOperationException(); // 날지 못함 ❌
    }
}

해결: 인터페이스나 계층 분리를 통해 fly()가 필요한 클래스들만 따로 관리

ISP - 인터페이스 분리 원칙

클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다

예:

public interface Worker {
    void work();
    void eat();
}

모든 클래스가 eat()을 구현해야 할까? ❌

개선:

public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

필요한 기능만 구현하면 됨. 코드가 더 유연해짐

DIP - 의존 역전 원칙

고수준 모듈은 저수준 모듈에 의존하면 안 된다. 추상화에 의존해야 한다.

예: 직접 구현체에 의존 ❌

public class OrderService {
    private final MySQLRepository repo = new MySQLRepository();
}

개선: 인터페이스 도입 + 주입

public class OrderService {
    private final Repository repo;

    public OrderService(Repository repo) {
        this.repo = repo;
    }
}

유연한 테스트, 구조 확장 가능

정리

원칙요약 문구목적
SRP클래스는 하나의 책임만변경 이유 명확하게
OCP확장에는 열려, 변경에는 닫혀기능 추가를 유연하게
LSP부모로 대체 가능해야 함타입 안전성
ISP인터페이스는 작게 나눠라불필요 의존 제거
DIP구체화가 아닌 추상화에 의존유연한 구조 설계
profile
Backend engineer

0개의 댓글