Jin's IT Story
[JAVA] SOLID 원칙 이해하기(SRP과 OCP) 본문
목차
SOLID 원칙이 중요한 이유
자바(Java)를 포함한 객체지향 프로그래밍(OOP)에서 코드 품질과 유지보수성은 매우 중요한 요소입니다.
프로젝트가 커질수록 코드 구조가 복잡해지고, 새로운 요구사항이나 기능 추가가 발생할 때 기존 코드의 안정성을 유지하기 어려워집니다. 이러한 문제를 해결하기 위해 고안된 대표적인 설계 철학이 바로 SOLID 원칙입니다.
SOLID는 다섯 가지 원칙의 앞 글자를 모은 약어로, 그중에서도 단일 책임 원칙(SRP)과 개방-폐쇄 원칙(OCP)은 자바 개발자들이 반드시 숙지해야 할 핵심 개념입니다.
본 글에서는 SRP와 OCP가 무엇인지, 각각의 개념과 자바 코드에서의 적용 예시, 그리고 실무에서 어떻게 활용할 수 있는지 심도 있게 살펴봅니다.
단일 책임 원칙(SRP)과 개방-폐쇄 원칙(OCP) 이해하기
1) SOLID 원칙의 개요
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와 OCP를 중심으로 설명하겠습니다.
2) 단일 책임 원칙(SRP) – 하나의 클래스는 하나의 책임만 가져야 한다
SRP의 정의는 단순합니다. “한 클래스는 하나의 책임만 가져야 한다”는 것입니다. 즉, 클래스는 하나의 기능 또는 역할만 담당해야 하며, 변경의 이유가 단 하나만 존재해야 한다는 뜻입니다.
예를 들어, 자바에서 다음과 같은 클래스가 있다고 가정해봅시다.
public class ReportManager {
public void generateReport() {
// 보고서 생성 로직
}
public void saveReportToFile(String filePath) {
// 파일 저장 로직
}
public void sendReportByEmail(String email) {
// 이메일 발송 로직
}
}
위 코드의 문제는 ReportManager
클래스가 보고서 생성, 파일 저장, 이메일 발송이라는 세 가지 책임을 동시에 가지고 있다는 점입니다. 이는 유지보수 시 문제가 될 수 있습니다. 파일 저장 로직을 수정하려다 보고서 생성 코드에 영향을 주는 등, 서로 관련 없는 기능들이 얽히게 되기 때문입니다.
SRP에 맞게 코드를 리팩토링하면 다음과 같이 나눌 수 있습니다.
public class ReportGenerator {
public void generateReport() {
// 보고서 생성 로직
}
}
public class ReportFileSaver {
public void saveReportToFile(String filePath) {
// 파일 저장 로직
}
}
public class ReportEmailSender {
public void sendReportByEmail(String email) {
// 이메일 발송 로직
}
}
이렇게 역할을 분리하면 클래스마다 하나의 책임만 담당하게 되고, 변경의 이유도 명확히 구분됩니다. 이는 코드 가독성, 유지보수성, 테스트 용이성을 크게 향상시킵니다.
3) 개방-폐쇄 원칙(OCP) – 확장에는 열려 있고 변경에는 닫혀 있어야 한다
OCP는 “소프트웨어 요소는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다”는 원칙입니다. 즉, 새로운 기능을 추가하거나 확장할 수는 있어야 하지만, 기존 코드를 직접 수정하지 않고도 이를 가능하게 해야 한다는 뜻입니다.
예를 들어, 결제 시스템을 자바로 구현한다고 가정해봅시다.
public class PaymentService {
public void pay(String type) {
if (type.equals("CARD")) {
System.out.println("카드 결제");
} else if (type.equals("CASH")) {
System.out.println("현금 결제");
}
}
}
위 코드는 새로운 결제 수단(예: 간편결제, 포인트 결제)을 추가할 때마다 PaymentService
내부를 수정해야 합니다. 이는 OCP 위반입니다. 기존 코드를 직접 변경해야 하므로 오류 발생 가능성이 커집니다.
OCP에 맞게 리팩토링하면 다음과 같이 설계할 수 있습니다.
public interface Payment {
void pay();
}
public class CardPayment implements Payment {
public void pay() {
System.out.println("카드 결제");
}
}
public class CashPayment implements Payment {
public void pay() {
System.out.println("현금 결제");
}
}
public class PaymentService {
public void processPayment(Payment payment) {
payment.pay();
}
}
이제 새로운 결제 방식을 추가하려면 Payment
인터페이스를 구현한 새로운 클래스를 작성하기만 하면 됩니다.
PaymentService
자체는 수정할 필요가 없습니다. 이렇게 하면 기존 코드의 안정성을 유지하면서도 기능 확장이 용이해집니다.
4) SRP와 OCP의 관계와 실무 적용
SRP는 클래스 설계의 내부 응집도를 높여 유지보수성을 강화하고, OCP는 외부 확장성을 확보하는 데 초점을 둡니다.
두 원칙은 서로 보완 관계에 있습니다. SRP를 잘 지키면 클래스가 단일 책임만 가지게 되고, OCP를 통해 해당 책임을 확장 가능한 구조로 설계할 수 있습니다. 이 조합은 자바와 같은 객체지향 언어에서 안정적이고 유연한 시스템을 만드는 핵심 토대가 됩니다.
자바 개발자가 SOLID 원칙을 반드시 이해해야 하는 이유
단일 책임 원칙(SRP)과 개방-폐쇄 원칙(OCP)은 객체지향 프로그래밍에서 가장 많이 활용되는 설계 원칙입니다.
SRP는 클래스가 하나의 책임만 가지도록 하여 코드 유지보수를 쉽게 만들고, OCP는 확장에는 열려 있으면서 변경에는 닫혀 있는 구조를 만들어 코드의 안정성과 확장성을 동시에 보장합니다.
자바 개발자라면 이러한 원칙을 코드에 적용하는 습관을 가져야 합니다. 작은 예제 프로젝트부터 시작해 SRP와 OCP를 실천하다 보면, 대규모 시스템에서도 유지보수와 확장이 수월해집니다. 이는 곧 프로젝트의 장기적인 성공과 직결됩니다.
2025.09.11 - [DevBasics: 개발 개념 기초 다지기] - [JAVA] SOLID 원칙 이해하기(LSP, ISP, DIP)
'DevBasics: 개발 개념 기초 다지기' 카테고리의 다른 글
WCAG 웹 접근성 가이드(WCAG 핵심 변화, KWCAG) (0) | 2025.09.08 |
---|---|
UX/UI ISO 표준 총정리: 최신 동향과 적용 사례 (0) | 2025.09.07 |
인터프리터 실행 과정과 동작 원리 (0) | 2025.09.04 |
Docker의 탄생과 발전, 그리고 실무 활용 사례 (0) | 2025.09.01 |
인터프리터 언어 성능 비교 (속도, 확장성, 활용 분야) (0) | 2025.08.30 |