Summary
이 글은 CodeGym의 “SOLID: 자바 클래스 설계의 다섯 가지 기본 원칙” 아티클에 대한 리뷰이다. 원문은 객체지향 설계에서 중요한 SOLID 원칙(단일 책임, 개방-폐쇄, 리스코프 치환, 인터페이스 분리, 의존성 역전)을 설명하고 자바 코드 예제를 통해 실제 적용 방법을 보여준다. 특히 단일 책임 원칙과 의존성 역전 원칙이 실제 코드에서 어떻게 구현되는지 상세한 예시를 통해 명확하게 설명한 점이 인상적이었다. 이 리뷰에서는 각 원칙의 핵심 내용과 함께 실무에서의 적용 경험, 그리고 원칙을 적용할 때 발생할 수 있는 오버엔지니어링의 위험성에 대한 생각을 담았다.
Info
이 글은 항해99 1일 1아티클 챌린지 참여 글이다.
- 챌린지 페이지 바로가기
- 챌린지 소개 페이지
- 원본 아티클: SOLID: 자바 클래스 설계의 다섯 가지 기본 원칙
- 관련 리뷰: 프론트엔드와 SOLID 원칙
아티클 내용
들어가며
이 아티클은 로버트 마틴이 제안하고 마이클 페더스가 약어화한 객체지향 설계의 다섯 가지 기본 원칙인 SOLID에 대해 설명한다. 아티클은 Java 코드 예제를 통해 각 원칙의 의미와 실제 적용 방법을 보여주며, 특히 OrderProcessor 클래스를 사용한 일관된 예시를 통해 원칙들이 어떻게 실제 코드에 적용되는지 설명한다.
Note
어제 리뷰한 카카오엔터테인먼트의 스티브님 아티클에서도 SRP에 대해 정확히 같은 관점으로 접근했다. 그곳에서는 “SRP를 ‘단일 책임’이 아닌 ‘변경의 이유가 하나’라는 관점에서 해석”해야 한다고 강조했다. 두 아티클이 서로 다른 환경(Java와 React)을 다루고 있음에도 이 핵심 개념에서는 일치하는 모습을 보이는 점이 인상적이다.
인상 깊었던 한 줄
“클래스를 변경해야 하는 이유가 한 가지 이상이어서는 안 된다.”
이 문구는 단일 책임 원칙(SRP)을 설명하는 부분에서 나온다. 아티클에서는 이를 “클래스를 변경해야 하는 이유가 한 가지 이상이어서는 안 된다”고 간결하게 정의했다. 이 정의가 특히 인상적인 이유는 많은 개발자들이 단일 책임 원칙을 “클래스는 한 가지 일만 해야 한다”는 식으로 오해하기 쉬운데, 아티클은 그 본질이 “변경의 이유”에 있다는 점을 명확히 했기 때문이다.
이 문장이 중요한 이유는 책임이라는 개념을 명확히 하기 때문이다. 코드를 작성할 때 기능이 무엇인지가 아니라, 변경이 발생할 수 있는 이유에 집중하면 더 응집력 있는 클래스를 설계할 수 있다. 예를 들어, 주문 처리, 데이터베이스 저장, 이메일 전송이라는 세 가지 다른 책임이 한 클래스에 있을 때 발생하는 문제를 명확히 보여준 예시가 이해하기 쉬웠다.
흥미롭게도 어제 리뷰한 카카오엔터테인먼트의 스티브님 아티클에서도 SRP에 대해 정확히 같은 관점으로 접근했다. 그곳에서는 “SRP를 ‘단일 책임’이 아닌 ‘변경의 이유가 하나’라는 관점에서 해석”해야 한다고 강조했다. 두 아티클이 서로 다른 환경(Java와 React)을 다루고 있음에도 이 핵심 개념에서는 일치하는 모습을 보이는 점이 인상적이다.
SOLID 원칙 개요와 설명
아티클에서는 SOLID 원칙을 다음과 같이 설명한다:
- 단일 책임 원칙(SRP): 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다.
- 개방-폐쇄 원칙(OCP): 소프트웨어 엔티티는 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.
- 리스코프 치환 원칙(LSP): 하위 타입은 상위 타입을 대체할 수 있어야 한다.
- 인터페이스 분리 원칙(ISP): 클라이언트는 사용하지 않는 인터페이스에 의존하지 않아야 한다.
- 의존성 역전 원칙(DIP): 고수준 모듈은 저수준 모듈에 의존하지 않아야 한다. 둘 다 추상화에 의존해야 한다.
특히 단일 책임 원칙(SRP)과 의존성 역전 원칙(DIP)에 관한 예시가 상세하게 설명되어 있어 이해하기 쉬웠다. OrderProcessor 클래스가 처음에는 주문 처리, 데이터베이스 저장, 이메일 전송이라는 세 가지 책임을 가지고 있다가, 이를 세 개의 별도 클래스로 분리하는 과정을 보여준다.
의존성 역전 원칙에서는 더 나아가 인터페이스를 통한 추상화를 보여주며, OrderProcessor가 구체적인 구현체가 아닌 추상화된 인터페이스에 의존하도록 개선하는 과정을 명확히 보여준다.
프론트엔드 아티클에서는 DIP를 구현하기 위해 React의 Context API와 의존성 주입 패턴을 활용하는 방법을 제시했지만, 여기서는 Java의 인터페이스를 활용한 전통적인 방식으로 접근하고 있다. 두 접근법은 표면적으로는 다르지만 근본적인 원칙은 동일하다 - 구체적인 구현보다 추상화에 의존하는 것이다.
개인적인 공감 포인트
-
단일 책임 원칙의 실제 적용: SRP를 적용한 경험이 많은데, 특히 초기에는 “한 가지 일만 하는 클래스”를 만들려고 클래스를 과도하게 작게 나누는 실수를 했었다. 아티클에서 설명한 “변경의 이유”라는 관점으로 SRP를 이해하게 되면서, 더 적절한 수준의 추상화가 가능해졌다.
-
의존성 역전의 유연성: DIP를 적용하여 테스트 용이성과 유연성을 높인 경험이 있다. 특히 의존성 주입을 통해 동일한 코드가 다양한 환경(개발, 테스트, 프로덕션)에서 다른 구현체와 함께 작동할 수 있게 한 점이 큰 장점이었다. 아티클의 OrderProcessor 예제가 이런 장점을 잘 보여준다.
-
SOLID와 오버엔지니어링: SOLID 원칙을 너무 교조적으로 적용하면 오히려 불필요하게 복잡한 코드가 될 수 있다는 점도 경험했다. 아티클에는 명시적으로 언급되지 않았지만, SOLID 원칙도 상황과 맥락에 맞게 적용하는 균형이 중요하다고 생각한다. 이 점은 프론트엔드 아티클에서 스티브님이 특별히 강조했던 부분이다. 그는 “원칙으로서의 SOLID”라는 관점에서 접근하고 교조적인 적용을 경계해야 한다고 설명했다.
자바와 객체지향 설계에 대한 생각
객체지향 설계에서 SOLID 원칙은 견고하고 유지보수 가능한 소프트웨어를 만드는 데 중요한 지침이다. 하지만 이런 원칙을 적용할 때는 코드의 목적과 상황을 고려해야 한다.
대규모 엔터프라이즈 애플리케이션에서는 SOLID 원칙의 엄격한 적용이 장기적으로 유지보수성을 높이지만, 작은 프로토타입이나 짧은 수명의 프로젝트에서는 유연하게 적용하는 것이 효율적일 수 있다.
자바는 객체지향 언어로서 SOLID 원칙을 적용하기에 적합한 언어이다. 특히 인터페이스, 추상 클래스, 다형성과 같은 기능은 SOLID 원칙을 구현하는 데 큰 도움이 된다. 스프링 프레임워크는 이러한 SOLID 원칙을 잘 반영한 대표적인 예라고 할 수 있다.
반면, 프론트엔드 환경에서는 함수형 패러다임과 선언적 UI 개발 방식이 자주 사용되므로 SOLID를 적용하는 방식이 다소 다를 수 있다. 어제 리뷰한 프론트엔드 아티클에서는 React의 컴포넌트, 훅, 컨텍스트 API 등을 활용해 SOLID 원칙을 구현하는 방법을 제시했다.
SOLID 원칙 적용의 균형점
SOLID 원칙은 코드의 품질을 향상시키는 중요한 지침이지만, 이를 적용할 때는 다음과 같은 균형점을 고려해야 한다:
-
복잡성과 명확성: 너무 많은 클래스와 인터페이스는 코드 이해를 오히려 어렵게 만들 수 있다. 적절한 수준의 추상화가 중요하다.
-
팀의 숙련도: 팀의 모든 구성원이 SOLID 원칙을 이해하고 있는지가 중요하다. 원칙을 잘 이해하지 못한 상태에서 적용하면 오히려 혼란을 가중시킬 수 있다.
-
프로젝트의 규모와 수명: 대규모 장기 프로젝트에서는 SOLID 원칙의 엄격한 적용이 더 중요하다. 작은 프로젝트에서는 상황에 맞게 유연하게 적용할 필요가 있다.
아티클에서 제시한 OrderProcessor 예제는 좋은 시작점이지만, 실제 복잡한 비즈니스 로직에서는 더 많은 고려사항이 필요할 수 있다.
핵심 요약
-
변경의 이유로 책임 정의하기: 단일 책임 원칙은 “클래스는 한 가지 일만 해야 한다”가 아니라 “클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다”는 것이다. 이 관점으로 접근하면 더 적절한 수준의 추상화가 가능하다.
-
인터페이스를 통한 추상화: 의존성 역전 원칙에서 가장 중요한 것은 구체적인 구현보다 추상화에 의존하는 것이다. 이를 통해 코드의 유연성과 테스트 용이성을 높일 수 있다.
-
실용적인 적용: SOLID 원칙은 교조적으로 적용하기보다 프로젝트의 상황과 요구사항에 맞게 실용적으로 적용해야 한다. 때로는 원칙의 완벽한 적용보다 코드의 명확성이 더 중요할 수 있다.
결론
CodeGym의 “SOLID: 자바 클래스 설계의 다섯 가지 기본 원칙” 아티클은 객체지향 설계의 핵심 원칙을 이해하기 쉬운 예제와 함께 설명하고 있다. 특히 단일 책임 원칙과 의존성 역전 원칙에 대한 설명이 실제 코드 예제와 함께 제공되어 이해하기 쉬웠다.
SOLID 원칙은 견고하고 유지보수 가능한 소프트웨어를 만드는 데 중요한 지침이지만, 무조건적인 적용보다는 프로젝트의 상황과 요구사항에 맞게 균형 있게 적용하는 것이 중요하다. 이 아티클은 SOLID 원칙의 기본을 이해하는 데 좋은 출발점이 되지만, 실제 적용에서는 더 많은 고려사항과 균형점을 찾아야 한다.
어제 리뷰한 프론트엔드 맥락의 SOLID 원칙과 오늘의 Java 맥락의 SOLID 원칙을 함께 보면, 같은 원칙이 다른 환경에서 어떻게 해석되고 적용되는지 이해하는 데 더욱 도움이 된다. 프론트엔드에서는 컴포넌트와 훅을 통해, 백엔드에서는 클래스와 인터페이스를 통해 구현되지만, 근본적인 원칙과 목표는 동일하다 - 유지보수 가능하고 확장 가능한 코드를 작성하는 것이다.
객체지향 설계의 기본 원칙인 SOLID를 이해하고 적용하는 것은 모든 자바 개발자에게 중요한 역량이며, 이 아티클은 그 시작점으로서 가치가 있다. 원칙의 본질을 이해하고 상황에 맞게 적용하는 지혜가 더해진다면, 더 나은 소프트웨어를 만들 수 있을 것이다.