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 |