SOLID 원칙
단일 책임 원칙(SRP)과 개방-폐쇄 원칙(OCP)
📌개요
한 언어에만 국한되는 내용이 아닌 객체지향 프로그래밍의 핵심 설계 방식이다.
SOLID 원칙은 2000년대 초반 로버트 C. 마틴(Robert C. Martin) - ‘엉클 밥(Uncle Bob)’ 미국의 소프트웨어 공학자가 정리했지만, 그 기원은 1980년대 후반부터 시작된 객체 지향 설계 연구에 뿌리를 두고 있다.
- SOLID 원칙이 핵심 원칙이라 불리는 이유?
- 변경 비용 감소: 기능 추가/수정 시 영향 범위 최소화
- 시스템 수명 연장: 유지보수성 향상으로 기술 부채 감소
- 팀 협업 효율화: 코드 가독성과 예측 가능성 증가
그 중 유지보수성과 확장성이 높은 소프트웨어를 설계하는 데 필수적인 SRP, OCP 두 가지 원칙을 알아본다.
📌내용
SRP (Single Responsibility Principle) - 단일 책임 원칙
정의: 한 클래스는 하나의 책임만 가져야 한다.
“한 우물만 파라!”
- 하나의 클래스는 오직 하나의 이유로만 변경되어야 한다.
- 책임 = 변경 이유
- 응집도 ↑, 유지보수성 ↑
잘못된 예
문제 상황: 뭐든 다 하는 만능 클래스
User
클래스가 두 가지 책임(사용자 정보 관리 + 데이터베이스 작업)을 동시에 가진다.- 만약 데이터베이스 로직이 변경되면
User
클래스도 수정해야 한다. - 사용자 정보의 필드 변경 시에도
User
클래스를 수정해야 하므로 변경의 이유가 두 가지가 된다.
1
2
3
4
5
6
7
8
9
10
11
12
class User {
private String name;
private String email;
// 사용자 정보 관련 책임
public String getName() { return name; }
public void setName(String name) { this.name = name; }
// 데이터베이스 관련 책임 (SRP 위반)
public void saveUserToDatabase(User user) { /* ... */ }
public User getUserFromDatabase(String userId) { /* ... */ }
}
올바른 예
해결책: 전문가를 따로 둡시다
User
클래스는 사용자 정보 관리만 담당한다.UserRepository
클래스는 데이터베이스 작업만 담당한다.- 데이터베이스 로직이 변경되어도
User
클래스는 영향을 받지 않으며 반대의 경우도 마찬가지다.
1
2
3
4
5
6
7
8
9
10
11
12
class User {
private String name;
private String email;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
class UserRepository {
public void saveUserToDatabase(User user) { /* ... */ }
public User getUserFromDatabase(String userId) { /* ... */ }
}
OCP (Open/Closed Principle) - 개방/폐쇄 원칙
정의: 소프트웨어 개체는 확장에는 열려 있어야 하고, 수정에는 닫혀 있어야 한다.
“확장은 쉽게, 수정은 최소로!”
- 기존 코드를 변경하지 않고도 시스템의 기능을 확장할 수 있어야 한다.
- 이는 추상화와 다형성을 통해 구현된다.
- 확장성 ↑, 기존 코드 안정성 ↑
잘못된 예
문제 상황: 수정이 불가피한 코드
- 새 보고서 형식(예: Excel) 추가 시 기존 코드 수정 불가피
- 한 형식의 버그 수정이 다른 형식에 영향 줄 수 있음
1
2
3
4
5
6
7
8
9
10
class ReportGenerator {
public void generate(String type) {
if ("PDF".equals(type)) {
// PDF 생성 100줄 코드...
} else if ("CSV".equals(type)) {
// CSV 생성 80줄 코드...
}
// 새 형식 추가할 때마다 이 클래스를 수정해야 함
}
}
올바른 예
해결책: 확장 가능한 구조
- 새 보고서 형식(예:
ExcelReport
) 추가 시 기존 코드 변경 불필요 - 각 형식의 코드가 독립적, 유지보수 용이
- 실행 시점에 보고서 형식 결정 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 모든 보고서가 구현할 인터페이스
interface Reportable {
byte[] generate();
}
// PDF 전문 생성기
class PdfReport implements Reportable {
public byte[] generate() { /* PDF 전용 로직 */ }
}
// CSV 전문 생성기
class CsvReport implements Reportable {
public byte[] generate() { /* CSV 전용 로직 */ }
}
// 확장 가능한 보고서 생성기
class ReportGenerator {
public byte[] createReport(Reportable reporter) {
return reporter.generate(); // 어떤 형식이든 동일한 방식으로 처리
}
}
🎯결론
- SRP로 잘 분리된 코드는 OCP 적용이 자연스럽다.
OCP를 구현하려면 SRP가 먼저 지켜져야 한다.
- SRP 적용 팁:
- 클래스 설명 시 “~와 ~를 한다”고 말하게 된다면 경고 신호
- 하나의 기능 변경이 다른 기능의 테스트를 깨트린다면 리팩토링 필요
- OCP 적용 팁:
if-else
/switch-case
가 자주 등장하면 OCP 위반 의심- 새로운 기능을 추가할 때 기존 코드를 건드리지 않아야 한다.
“처음부터 완벽하게 만들려고 하지 마세요. 하지만 변경이 필요할 때마다 조금씩 개선해나가세요” - 마틴 파울러
⚙️EndNote
단일 책임 원칙
원문: [The Single Responsibility Principle - by Robert C. Martin (Uncle Bob)](https://8thlight.com/blog/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html](https://8thlight.com/blog/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html)
1972년에 데이비드 파나스가 내놓은 고전적인 논문 “Criteria To Be used in decomposing systems into modules”. 이 논문은 Communications of the ACM Vol 15 No 12에 게제되었습니다.
이 논문에서 파나스는 간단한 알고리즘을 분해(Decompose)하고 분리(Seprate)하는 두가지 전략을 비교하고 있습니다. 이 논문은 매우 흥미롭게 읽을수 있기 때문에 읽어 보는 것을 강력 추천합니다.
출처: narusas’s blog