SOLID를 고민해보자 (1)

허진혁·2022년 10월 24일
0

고민해보자

목록 보기
4/12
post-thumbnail

고민해보자 두번째 시리즈는 SOLID 입니다.

아직 확실하진 않지만 두 편으로 나눌 예정입니다.
이번 페이지에 다룰 내용은 SRP/OCP 입니다!!

코드를 작성하려고 할 때, 들어본 단어 SOLID...

처음에는 이 원칙들이 왜 필요할까 생각을 해보았다. 코드를 작성할 때, 클래스 캡슐화하고, 대충 연결지어서 로직만 실행된다고 하면 되는것이 아니기에 무언가가 필요하다고 생각했다.

클린코드의 내용은 모르겠지만, 코드를 더 더 더 발전시켜야 되는 것은 알고있다. 그렇다면 객체지향을 배웠는데, 객체지향적으로 코드를 작성해야 될 것이 아닐까?

이렇게 생각을 시작하면 SOLID의 내용이 무엇인지는 몰라도 느낌이 온다.
인터페이스처럼 가이드라인이 되어 방향성을 알려준다는 것이 아닐까?

위키피디아의 정의를 보면

프로그래머가 시간이 지나도 유지 보수와 확장이 쉬운 시스템을 만들고자 할 때 이 원칙들을 함께 적용할 수 있다. SOLID 원칙들은 소프트웨어 작업에서 프로그래머가 소스 코드가 읽기 쉽고 확장하기 쉽게 될 때까지 소프트웨어 소스 코드를 리팩터링하여 코드 냄새를 제거하기 위해 적용할 수 있는 지침이다. 이 원칙들은 애자일 소프트웨어 개발과 적응적 소프트웨어 개발의 전반적 전략의 일부다.

우선 SOLID의 내용들이 무엇이 있는지부터 알아보자!!

SRP / OCP / LSP / ISP / DIP 이렇게 5가지의 원칙이 있다. 이 원칙들이 객체지향을 위한 가이드라인이라면, 서로 완전 독립적인 것이 아니라 어느정도 연결되어 있지 않을까? 생각해보았다.

이렇게 큰 틀을 두고 가니 두 가지 관점을 찾을 수 있었다.

SRP / OCP 를 통해 객체가 무분별하게 커지는 것을 막아주고,
LSP / ISP / DIP 를 통해 변화에 유용한 코드 작성이 가능해진다.

그렇다면, SRP부터 차근차근 알아가보자.

Single Responsibility Principle : 단일 책임 원칙

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

다양한 책임을 갖는다면 왜 객체지향적이지 않을까??

처음부터 완벽한 계획은 없듯이 상황에 맞게 더 좋은 방향성을 찾아나가야 한다. 그렇기에 요구사항은 계속 변할 것이다.

요구사항이 변하면 코드가 변하게 될텐데, 다양한 책임을 갖고 있다면 그 클래스의 코드를 바꾸게 될 것이고, 결국 클래스와 연관된 모든 코드를 수정하게 될 것이다.

만약 SRP 가이드라인에 따라 지켰다면, 요구사항 변경시 리팩토링할 코드가 줄어들 것이다.

그렇다면 "책임"이 의미하는 것이 무엇일까??
로버트 마틴은 책임을 "변경하려는 이유"로 정의했는데, 이부분이 와닿지 않았다.

우리가 흔히 하는 실수들이 있다. 어떠한 문제가 생긴다면 우리는 책임자를 찾는다. 그렇지만 이는 늘 불편한 결과를 맞이했던 것 같다.

진정으로 문제를 해결할 의지가 있다면, 문제의 근원을 찾아야 한다. 이러한 생각을 하게 되는 순간, 책임은 클래스에 국한되지 않는다는 것을 생각해보았다.

그런데 우리는 분명 아까 SRP를 정의할 때 "클래스는 단 하나의 책임만 갖는다"고 하지 않았나?

클래스도 하나의 종류에 포함되는 것이다. 메서드가 될수도 있고, 클래스가 될수도 있고, 패키지 등이 될 수도 있는 것이다.

다만 "문제가 발생했다면, 우리가 찾아야 할 곳을 "단일"하도록 만드라는 것이 아닐까 하는 조심스러운 정리를 해보려고 한다.

Open/Closed Principle : 개방-폐쇄 원칙

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

처음 이 문구를 보았을 때 이렇게 이상적이고 모순되는 말을 이해하기 힘들었다. 거의 20분동안 이문구만 계속 째려본 것 같다.

하지만 본질을 생각해보는 순간 조금 느낌이 왔다.

다양한 상황을 고려해서 유연하게 대처하는 것이 목적이겠구나!!

OCP는 추상화,상속 그리고 다형성으로 접근해보니 이해하기가 편해졌다.

추상화를 활용하려면 추상클래스와 인터페이스를 활용할 것이고, 이로인한 상속은 당연해지고, 상속이 존재하면 다형성이 가능해진다.

즉, OCP를 이해하기위해서 추상화, 상속, 다형성을 이해하고 있어야 한다.

추상화시킨 타입의 객체와 그 타입의 실제 구현이 담겨있는 객체를 바탕으로, 역할에 관한 코드는 변경되지 않지만, 구현할 코드를 추가 및 변경하여 OCP가 지켜지는 것이다.

이를 이해하자마자 나는 대표적인 두 가지 예시가 떠올랐다.
바로 JVM과 스프링의 ApplicationContext이다.

여기서 또 중요한 것을 발견했다. 개방과 폐쇄가 대척점에 있는 단어라서 두개의 접근법이 필요할것 같지만, 사실 하나의 개념만 고려하면 되는 것이다.

동전이 앞면이 있다면 뒷면도 있다고 생가한는 것처럼 말이다.

다시 돌아가서 JVM을 생각해보자.

JVM의 대표적인 설명 "운영체제에 독립적이다." 이 말은 JVM은 OCP원칙을 잘 지켰다는 것이다.

JVM은 어떠한 운영체제 에서도 동일하게 작동한다(open) = JVM은 운영체제가 변경되도 동일하게 작동한다.(close)

ApplicationContext(이하 스프링 컨테이너)는 다양한 설정정보를 받아들일 수 있도록 지원한다.

스프링 컨테이너는 여러가지 설정 정보를 받아들인다.(open) = 스프링 컨테이너는 설정 정보가 변경되도 받아들인다.(close)

(위 그림은 김영한님의 스프링 핵심원리 자료 참조한 것입니다)

위의 두가지 예시를 통해 우리는 추상화와 다형성을 기반으로 OCP가 지켜지는 것을 확인한 것을 기반으로,

OCP의 근본적인 목표는 다양한 상황을 생각해서 유연하게 설계하는 것으로 정리를 할 수 있었다.

profile
Don't ever say it's over if I'm breathing

0개의 댓글