객체 지향 설계(Object-Oriented Design)는 단순히 클래스를 나누고 상속하는 것을 넘어서, 유지보수와 확장이 쉬운 구조를 만드는 것이 핵심이다. 이를 위해 로버트 C. 마틴(Robert C. Martin)이 제안한 SOLID 원칙은 객체 지향 설계의 핵심 원칙으로 널리 사용된다. SOLID는 다음 5가지 원칙의 앞글자를 딴 약어이다.
1️⃣ 단일 책임 원칙(Single Responsibility Priniciple, SRP)
하나의 클래스는 하나의 책임만 가져야 한다. 클래스는 하나의 기능이나 역할만을 수행해야 한다는 원칙이다.
여기서 말하는 ‘책임’은 클래스가 변경되어야 할 단 하나의 이유를 의미한다. 하나의 클래스가 둘 이상의 책임을 가지면, 한 책임의 변경이 다른 책임에 부수적인 영향을 끼치게 되고, 이는 불필요한 수정과 버그의 원인이 된다.
예를 들어, 데이터 저장과 화면 출력 로직을 하나의 클래스가 동시에 담당하고 있다면, 저장 방식이 바뀌는 순간 출력 기능에도 영향을 미칠 수 있다. 이를 분리하면 각 기능은 독립적으로 변경될 수 있으며, 테스트도 더 간단하고 명확해진다.
역할과 책임이 명확히 분리된 코드는 확장과 유지보수에 강하며, 개발자의 업무 분담과 협업 구조를 더 효율적으로 만든다.
2️⃣ 개방 - 폐쇄 원칙 (Open/Closed Principle, OCP)
기존 코드를 수정하지 않고도 새로운 기능을 확장할 수 있어야 한다.
개방-폐쇄 원칙은 기능을 확장해야 할 필요가 있을 때 기존의 소스 코드를 직접 수정하지 않고, 새로운 구조를 추가함으로써 확장할 수 있어야 한다는 원칙이다. 이를 통해 기존 기능의 안정성을 유지하면서 새로운 기능을 안전하게 도입할 수 있다.
이 원칙을 실현하기 위해서는 인터페이스나 추상 클래스 같은 추상화 계층을 활용하고, 상속, 위임, 컴포지션, 전략 패턴 등 다양한 설계 기법을 적절히 적용해야 한다. 또한, 스프링 같은 프레임워크에서는 의존성 주입(Dependency Injection)을 통해 OCP를 자연스럽게 구현할 수 있다.
OCP는 변화가 자주 일어나는 부분과 안정성을 유지해야 하는 부분을 분리함으로써, 시스템 전체의 리스크를 줄인다.
3️⃣ 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)
서브타입은 언제나 슈퍼타입으로 대체할 수 있어야 한다.
리스코프 치환 원칙은 객체 지향에서 상속과 다형성을 사용할 때 반드시 고려해야 하는 원칙이다. 이 원칙은 부모 클래스의 인스턴스가 사용되는 곳에서 자식 클래스의 인스턴스를 넣더라도 프로그램이 논리적으로 동일하게 동작해야 한다는 것을 의미한다.
예를 들어, 자식 클래스가 부모 클래스의 메서드를 오버라이드하면서 기존 동작을 무효화하거나, 예외를 발생시키는 등의 방식으로 상속 계층의 계약을 깨트리면 LSP를 위반한 것이다. 이 경우, 상속보다는 역할 기반 인터페이스 설계가 더 적절할 수 있다
LSP는 OCP와 깊은 연관이 있으며, LSP가 지켜지지 않으면 OCP의 효과도 무너질 수 있다.
4️⃣ 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)
클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
인터페이스 분리 원칙은 작고 구체적인 인터페이스를 여러 개로 나누고, 각 구현 클래스가 자신이 필요한 인터페이스만 구현하도록 설계하라는 원칙이다. 인터페이스가 커질수록 그 인터페이스를 구현하는 클래스는 필요하지 않은 기능까지 강제로 구현하게 되며, 이는 불필요한 결합을 초래한다.
예를 들어, ‘작업’, ‘식사’, ‘휴식’ 기능이 모두 포함된 인터페이스를 로봇이 구현하게 되면, 로봇은 먹지도 쉬지도 않는데 불필요한 메서드를 구현하게 된다. 이 경우 각 기능별로 인터페이스를 나누는 것이 적절하다.
인터페이스 분리 원칙은 소프트웨어 설계에서 "역할(Role)"을 중심으로 인터페이스를 구성하라는 철학을 담고 있다. 이는 모듈화 수준을 높이고, 테스트와 유지보수 또한 간결하게 만든다.
5️⃣ 의존 역전 원칙 (Dependency Inversion Principle, DIP)
고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다.
전통적인 구조에서는 고수준 모듈(비즈니스 로직)이 저수준 모듈(데이터베이스, 파일, 네트워크 등 세부 구현)에 직접 의존하는 경우가 많다. 이런 구조에서는 구현이 변경될 때마다 상위 모듈까지 수정해야 하므로 유연성과 재사용성이 크게 떨어진다.
의존 역전 원칙은 이러한 문제를 해결하기 위해 구현이 아닌 추상화(인터페이스나 추상 클래스)에 의존하도록 설계하라고 제안한다. 고수준 모듈과 저수준 모듈이 동일한 추상화에 의존하면, 구현체를 변경해도 상위 로직은 그대로 유지될 수 있다.
이 원칙은 의존성 주입(DI), IoC(Inversion of Control)와 매우 밀접한 관계가 있으며, 스프링 프레임워크는 이러한 원칙을 구조적으로 반영하여 구성되어 있다.
이와 같이 SOLID를 바탕으로 한 설계는 코드 품질을 높이고, 변화에 강한 구조를 만들며, 팀 개발에서도 더 나은 협업 환경을 제공한다.
[출처]
https://blog.siner.io/2020/06/18/solid-principles/
'Java > SE' 카테고리의 다른 글
[Java] JDK 버전 여러 개 사용하는 방법 (2) | 2025.05.08 |
---|---|
[Java] 조건문 (1) | 2025.05.08 |
[Java] 연산자 (0) | 2025.05.08 |
[Java] 변수와 타입 (3) | 2025.05.07 |
[Java] 소스 코드 개발 도구(Eclipse) (0) | 2025.05.06 |