SOLID 원칙, 객체지향 설계를 단단하게 만드는 다섯 가지 원칙

코모도왕도마뱀·2025년 4월 21일
6

객체지향 설계를 잘 한다는 건 결국 "변화에 유연하게 대응할 수 있는 시스템을 만든다"는 말과 같다.
A가 바뀌었는데 B, C, D, E...가 줄줄이 다 깨지면, 그건 소프트웨어가 아니라 그냥 딱딱웨어다.

그런 의미에서 객체지향 설계를 잘 하고 싶다면, SOLID 원칙은 한 번쯤 제대로 고민해볼 만한 기준점이다.
지금부터 SOLID 다섯 가지 원칙을 간단한 예시 코드와 함께 정리해본다.
모든 걸 완벽하게 지키는 건 어렵다. 하지만 최대한 의식하고 적용하려고 노력하는 것, 그게 실력 향상의 길이라고 생각한다.

1. SRP - 단일 책임 원칙

하나의 클래스는 하나의 책임만 가져야 한다.

여러 기능을 하나의 클래스에 우겨 넣으면 나중에 수정할 때 다른 기능까지 깨질 가능성이 높다. 유지보수도 어렵고 테스트도 힘들어진다.
책임을 나누면 기능별로 독립적인 수정이 가능해지고, 테스트 코드도 작게 쪼갤 수 있다.

물론 "하나의 책임"이란 게 상황마다 다르다. 무조건 기능 하나만 있어야 한다는 게 아니라, 그 클래스가 담당하는 변화의 이유가 한 가지여야 한다는 의미로 이해하면 좋다.

2. OCP - 개방 폐쇄 원칙

확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

코드를 확장할 땐 새로운 기능을 추가하는 쪽으로 가야지, 기존 코드를 고치는 쪽으로 가면 유지보수가 힘들어진다.

예를 들어 새로운 결제 수단이 추가됐을 때, 기존 if 문을 계속 추가하는 방식은 OCP를 위반하는 구조다. 반면 새로운 클래스만 추가하면 되는 구조라면 OCP를 잘 지킨 거다.

확장성 있는 구조를 위해선 추상화, 즉 인터페이스나 추상 클래스를 잘 활용해야 한다.

3. LSP - 리스코프 치환 원칙

부모 타입을 사용하는 곳에 자식 타입을 넣어도 문제가 없어야 한다.

상속 구조를 만들 땐 항상 이 원칙을 염두에 둬야 한다. 부모 클래스에 정의된 규칙이 자식 클래스에서도 당연히 지켜질 거라고 기대하게 된다. 그런데 자식이 그 기대를 깨면 문제가 된다.

이 원칙을 지키지 않으면 다형성이 깨지고, 코드를 이해하거나 사용하는 데 혼란이 생긴다.
필요하다면 기능을 인터페이스로 쪼개서 각각 다른 타입으로 나누는 것도 방법이다.

4. ISP - 인터페이스 분리 원칙

클라이언트가 사용하지 않는 메서드에 의존하지 않게 해야 한다.

인터페이스가 너무 많은 기능을 담고 있으면, 어떤 구현체는 일부 기능만 필요함에도 불구하고 불필요한 메서드까지 구현해야 한다. 이건 명백한 낭비고, 유지보수 포인트가 늘어나는 원인이 된다.

필요한 기능만 쏙쏙 분리해서 최소한의 인터페이스를 제공하는 게 좋다.

5. DIP - 의존 역전 원칙

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

구체 클래스에 의존하는 구조는 테스트하기 어렵고 변경에 취약하다. 반면 추상화에 의존하면 구현체만 갈아끼우는 식으로 테스트나 확장이 가능하다.

DIP를 지키기 위해선 결국 의존성 주입이 필요하다. 직접 생성하지 말고 외부에서 주입받는 구조를 만드는 게 핵심이다.

설계 사상

SOLID, KISS, YAGNI, DRY, LoD 등.. 많은 설계 사상이 있다.
설계 사상은 개발자의 정신이고, 디자인 패턴은 이러한 설계 사상의 유산이다.

설계 사상은 절대적인 것은 아니나, 나같은 주니어 개발자들한테 성장의 밑거름이 될 것이다.

처음부터 SOLID 다 지키면서 개발하는 사람은 없다.

하지만 계속 의식하고 연습하다 보면, 어느 순간 자연스럽게 그렇게 설계하고 있는 자신을 보게 될 거다.

왜 안 되는지 궁금하면, 일단 한 번 안 지켜보고 개발해보면 된다. 그럼 안 지켜야 할 이유보다, 지켜야 할 이유가 먼저 보일 거다.

profile
생각은 깊게, 실행은 단순하게.

0개의 댓글