포스트

SOLID 원칙을 실무적으로 어떻게 이해해야 하는가

SOLID는 객체지향 설계의 다섯 가지 원칙을 묶은 이름이다. 면접에서는 종종 정의를 외우듯 말하게 되지만, 실무에서는 “변경이 들어왔을 때 코드가 어디서 깨지는가”를 줄이기 위한 기준으로 보는 편이 훨씬 유용하다.

SOLID가 필요한 이유

기능은 계속 바뀌고, 요구사항은 뒤늦게 들어오며, 처음 설계한 구조가 끝까지 유지되는 경우는 거의 없다. SOLID는 이런 변화 속에서 코드를 덜 깨지게 만들기 위한 설계 감각이다.

즉, SOLID는 예쁜 이론이 아니라 다음 질문에 답하려는 원칙이다.

  • 책임이 어디까지인가
  • 변경이 들어올 때 어디를 수정해야 하는가
  • 구현 세부가 상위 로직에 얼마나 새고 있는가
  • 서로 다른 역할이 너무 강하게 묶여 있지는 않은가

SRP: 단일 책임 원칙

하나의 클래스는 하나의 변경 이유를 가져야 한다는 원칙이다.

예를 들어 주문 서비스가:

  • 주문 생성
  • 가격 계산
  • 알림 전송
  • 로그 포맷 구성

까지 모두 맡고 있다면, 서로 다른 이유로 계속 수정될 수밖에 없다.

SRP의 핵심은 “메서드가 한 개여야 한다”가 아니라, 변경 축이 섞이지 않아야 한다는 점이다.

OCP: 개방-폐쇄 원칙

확장에는 열려 있고 수정에는 닫혀 있어야 한다는 원칙이다.

현실적으로는 “기존 코드를 계속 뜯어고치지 않고 새 동작을 붙일 수 있는가”에 가깝다.

예를 들어 결제 수단이 늘어날 때마다 if-else를 계속 추가하는 구조보다, 인터페이스를 통해 구현을 추가할 수 있는 구조가 OCP에 더 가깝다.

LSP: 리스코프 치환 원칙

자식 타입은 부모 타입을 대체할 수 있어야 한다는 원칙이다.

즉, 부모 타입을 기대하는 코드에 자식 객체를 넣었을 때 동작 의미가 깨지면 안 된다.

대표적인 문제는 이런 경우다.

  • 부모는 “이 메서드는 항상 성공한다”는 계약인데 자식은 예외를 남발한다
  • 부모는 “읽기/쓰기 가능”한데 자식은 일부 동작을 막는다

상속 문법이 가능하다고 해서 설계적으로 치환 가능한 것은 아니다.

ISP: 인터페이스 분리 원칙

클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다는 원칙이다.

거대한 인터페이스 하나에 모든 기능을 몰아넣으면, 구현체는 필요 없는 메서드까지 억지로 구현해야 한다. 결국 빈 메서드, 예외 던지기, 의미 없는 구현이 생기기 쉽다.

인터페이스는 크게 만드는 것보다, 사용자 관점의 역할 단위로 나누는 것이 유지보수에 유리하다.

DIP: 의존관계 역전 원칙

상위 모듈이 하위 구현 세부에 직접 의존하지 말고, 둘 다 추상화에 의존해야 한다는 원칙이다.

예를 들어 서비스가 특정 DB 구현체나 특정 외부 API 클라이언트에 직접 묶여 있으면, 교체와 테스트가 어렵다. 반대로 인터페이스에 의존하면 구현 변경의 충격이 줄어든다.

스프링의 DI가 자주 언급되는 이유도 이 지점을 실용적으로 풀어주기 때문이다.

SOLID를 오해하기 쉬운 지점

클래스 수를 늘리는 것이 목표는 아니다

SOLID를 잘못 적용하면 클래스와 인터페이스만 과하게 늘어나고 구조는 더 복잡해질 수 있다. 중요한 것은 추상화 개수보다 변경 비용을 줄이는가다.

모든 곳에 다 적용할 필요는 없다

단순한 CRUD나 수명이 짧은 코드에 과도한 추상화를 넣으면 오히려 읽기 어려워진다. 변경 가능성이 큰 곳, 정책이 자주 달라지는 곳, 외부 연동이 있는 곳에서 더 큰 효과가 난다.

실무에서 SOLID를 보는 기준

코드를 볼 때 다음 질문을 던지면 충분히 실용적이다.

  • 이 클래스는 왜 바뀌는가
  • 새 기능이 들어오면 기존 분기문을 계속 수정해야 하는가
  • 상위 로직이 하위 구현 세부를 너무 많이 알고 있지는 않은가
  • 한 인터페이스가 너무 많은 역할을 떠안고 있지는 않은가
  • 상속 구조가 계약을 깨고 있지는 않은가

SOLID는 원칙을 외우는 문제가 아니라, 변경이 들어왔을 때 코드가 얼마나 덜 깨지는지 평가하는 기준이다.

정리

  • SRP는 책임 분리를 본다
  • OCP는 수정 없이 확장 가능한 구조를 본다
  • LSP는 상속 관계의 계약 보존을 본다
  • ISP는 역할 단위 인터페이스 분리를 본다
  • DIP는 구현이 아니라 추상화에 의존하는 구조를 본다

결국 SOLID의 핵심은 객체지향 자체보다, 변경에 강한 구조를 만들기 위한 판단 기준으로 이해하는 데 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

댓글

아직 댓글이 없습니다