SOLID 원칙이란?

lavender_je
|2025. 3. 26. 14:15
728x90

SOLID 원칙이란?

SOLID는 객체지향 프로그래밍(OOP)에서 코드를 더 유지보수하기 쉽게 만들기 위한 5가지 설계 원칙이야.
이 원칙을 따르면 유연하고 확장 가능하며 유지보수가 쉬운 코드를 작성할 수 있어.

 


SOLID 5대 원칙 정리

원칙설명
S (Single Responsibility Principle) 단일 책임 원칙 - 하나의 클래스는 하나의 책임만 가져야 한다.
O (Open/Closed Principle) 개방-폐쇄 원칙 - 확장에는 열려 있어야 하지만, 수정에는 닫혀 있어야 한다.
L (Liskov Substitution Principle) 리스코프 치환 원칙 - 하위 클래스는 부모 클래스를 대체할 수 있어야 한다.
I (Interface Segregation Principle) 인터페이스 분리 원칙 - 클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
D (Dependency Inversion Principle) 의존 역전 원칙 - 상위 모듈은 하위 모듈에 의존하면 안 되고, 추상화에 의존해야 한다.

 


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

  • 클래스는 하나의 책임(변화의 이유)만 가져야 한다.
  • 한 클래스가 너무 많은 역할을 하면 유지보수하기 어려워지고, 코드가 복잡해져.

잘못된 예시

public class User {
    public void saveToDatabase() { 
        // 사용자 정보를 DB에 저장 
    }
    
    public void sendEmail() { 
        // 이메일 전송 로직 
    }
}
  • 문제점:
    • 이 클래스는 "사용자 정보 관리""이메일 전송"이라는 두 가지 책임을 가지고 있어.

올바른 예시 (책임 분리)

public class UserRepository {
    public void save(User user) { 
        // 사용자 정보를 DB에 저장 
    }
}

public class EmailService {
    public void sendEmail(User user) { 
        // 이메일 전송 로직 
    }
}
  • 이점: 변경 사항이 생기면 하나의 클래스만 수정하면 됨 → 유지보수 용이.

 


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

  • 코드는 확장에 열려 있어야 하지만, 수정에는 닫혀 있어야 한다.
  • 즉, 기존 코드를 변경하지 않고 기능을 추가할 수 있어야 한다.

잘못된 예시

public class PaymentProcessor {
    public void processPayment(String type) {
        if (type.equals("credit")) {
            System.out.println("Processing credit card payment");
        } else if (type.equals("paypal")) {
            System.out.println("Processing PayPal payment");
        }
    }
}
  • 문제점:
    • 새로운 결제 수단이 추가될 때마다 processPayment()를 수정해야 함 → 유지보수가 어려움.

올바른 예시 (OCP 적용)

public interface PaymentMethod {
    void pay();
}

public class CreditCardPayment implements PaymentMethod {
    public void pay() {
        System.out.println("Processing credit card payment");
    }
}

public class PayPalPayment implements PaymentMethod {
    public void pay() {
        System.out.println("Processing PayPal payment");
    }
}

public class PaymentProcessor {
    public void processPayment(PaymentMethod paymentMethod) {
        paymentMethod.pay();
    }
}
  • 이점: 새로운 결제 방식 추가 시 PaymentProcessor 수정 없이 클래스를 추가하면 됨.

 


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

  • 부모 클래스의 객체를 자식 클래스로 교체해도 프로그램이 정상적으로 작동해야 한다.
  • 즉, 자식 클래스는 부모 클래스의 기능을 깨뜨리면 안 된다.

잘못된 예시

public class Rectangle {
    protected int width, height;

    public void setWidth(int width) { this.width = width; }
    public void setHeight(int height) { this.height = height; }

    public int getArea() { return width * height; }
}

public class Square extends Rectangle {
    @Override
    public void setWidth(int width) { 
        this.width = width; 
        this.height = width; 
    }

    @Override
    public void setHeight(int height) { 
        this.width = height; 
        this.height = height; 
    }
}
  • 문제점: Rectangle을 Square로 대체하면 예상한 동작을 하지 않을 수 있음.

올바른 예시 (LSP 적용)

public interface Shape {
    int getArea();
}

public class Rectangle implements Shape {
    protected int width, height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public int getArea() { return width * height; }
}

public class Square implements Shape {
    private int side;

    public Square(int side) { this.side = side; }

    public int getArea() { return side * side; }
}
  • 이점: Rectangle과 Square가 독립적으로 동작하여 LSP를 준수함.

 

 


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

  • 클라이언트는 자신이 사용하지 않는 메서드에 의존하면 안 된다.
  • 즉, 큰 인터페이스를 여러 개의 작은 인터페이스로 나누어야 한다.

잘못된 예시 (하나의 인터페이스에 너무 많은 기능)

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

public class Robot implements Worker {
    public void work() { System.out.println("Robot is working"); }
    public void eat() { throw new UnsupportedOperationException(); }
}
  • 문제점: Robot은 eat()을 구현할 필요가 없음.

올바른 예시 (ISP 적용)

public interface Workable {
    void work();
}

public interface Eatable {
    void eat();
}

public class Human implements Workable, Eatable {
    public void work() { System.out.println("Human is working"); }
    public void eat() { System.out.println("Human is eating"); }
}

public class Robot implements Workable {
    public void work() { System.out.println("Robot is working"); }
}
  • 이점: Robot은 Eatable을 구현할 필요가 없어짐.

 


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

  • 상위 모듈은 하위 모듈에 의존해서는 안 되고, 추상화에 의존해야 한다.

잘못된 예시 (구체적인 클래스에 의존)

public class Keyboard {}
public class Monitor {}

public class Computer {
    private Keyboard keyboard;
    private Monitor monitor;

    public Computer() {
        this.keyboard = new Keyboard();
        this.monitor = new Monitor();
    }
}
  • 문제점: Computer가 Keyboard와 Monitor에 강하게 결합됨.

올바른 예시 (DIP 적용)

public interface Keyboard {}
public interface Monitor {}

public class Computer {
    private Keyboard keyboard;
    private Monitor monitor;

    public Computer(Keyboard keyboard, Monitor monitor) {
        this.keyboard = keyboard;
        this.monitor = monitor;
    }
}
  • 이점: Computer가 Keyboard와 Monitor의 추상화에 의존 → 유연한 코드.

 


🎯 정리

SOLID 원칙을 따르면 유지보수성, 확장성, 가독성이 향상됨.
결합도를 낮추고, 유연한 코드를 만들 수 있음.

 

 

 


 

728x90

'프로그래밍 > Spring' 카테고리의 다른 글

Spring셋팅 하기  (0) 2025.03.27
Spring에서 빈(Bean)의 종류  (0) 2025.03.26
빈(bean)이란?  (0) 2025.03.26
Component Scan이란?  (0) 2025.03.26
Spring Container란?  (0) 2025.03.26