본문 바로가기
개발

SOLID 원칙에 대하여

by ISA(류) 2022. 5. 4.

SOLID: 단일 책임, 개방 폐쇄, 리스 코브 치환, 인터페이스 분리, 의존성 역전 원칙을 두 문자어 법칙으로 정리한 용어.
객체 지향 설계에 대한 원칙이라고 하지만 사실 모던 프로그래밍 전반에 널리 적용되는 클린 코드 원칙이라고 볼 수 있다.

해당 원칙을 한마디로 간단히 정리하면 하나의 책임 단위로 코드를 잘 분리해서 잘 추상화 시키고 추후 변경이 적게 설계해라이다.

단일 책임 원칙의 경우 하나의 객체가 하나의 책임을 가지도록 설계하는 원칙이다. 그 책임에는 명확한 범위가 없다. 예를 들어서 식사라는 프로그램을 만들려고 하면 식사라는 행위 자체가 하나의 책임이 될 수 있고 그 식사를 사람과 메뉴, 식기, 예절, etc 등의 세부적인 책임으로 분리할 수 있다.

이걸 어떻게 분리하는지는 판단에 따라 다르다. 하지만 보통 추상화 단계에 맞게 책임을 부여한다. 즉 식사라는 전체 책임을 가진 추상화 레이어에서는 식사 자체가 하나의 책임이고 그 하위 레이어에서는 사람과 메뉴, 행위가 각각 하나의 책임 일 수 있고 사람에는 철수, 영희, 바둑이등이 각각 하나의 책임으로 분류될 수 있다. 그럼 식사라는 책임을 가진 레이어에서는 그 하위의 레이어들을 불러와서 식사라는 책임을 수행하고, 사람 등은 그 하위의 레이어에서 데이터를 가져와서 그 책임을 수행한다. 더 이상 유의미하게 책임을 분리할 필요가 없을 때까지 말이다. 그런데 그걸 뭘 기준으로 판단할까?

보통은 최대한 수정이 캡슐화되도록 적절하게 나눈다. 식사라는 책임을 가진 프로그램의 구성 요소로 존재하고 생각할 수 있는 많은 것들을. 즉, 목적에 따라서 어느 구성 요소가 달라질 수 있는지에 따라서 책임을 분리한다. 식사에 참여하는 사람이 달라질 수 있으니 사람을 따로 분리하고, 메뉴가 달라질 수 있으니 메뉴를 분리하고 그를 통해서 식사를 구성하는 방식이다. 그를 통해서 메뉴와 사람 등 식사를 구성하는 요소를 수정하는 데 있어서 다른 부분을 고려할 필요가 없어진다.(기본적으로 서로 간의 영역에서 서로의 영역을 모른다는 전제를 가지니까)

개방 폐쇄 원칙에 의하면 소프트웨어는 확장에는 열려 있고 변경에는 닫혀있어야 한다. 먼저 식사라는 프로그램을 단일 책임원칙에 따라서 적절히 만들었다. 식사에 참여하는 사람을 수정할 때나 메뉴를 수정할 때 그것이 식사라는 프로그램의 다른 부분에는 아무런 영향을 미치지 않는다. 하지만 해당 프로그램의 목적이 단순히 식사만을 다루는 게 아니라 사람 간의 만남을 다루는 것이 본래 목적이라면 어떻게 될까? 식사라는 책임에 맞춰진 기존의 코드는 수정되어야 할 것이다.

대부분의 경우 만남에서 취식행위가 포함되지만 사람 간의 만남에서 항상 식사를 하는 것은 아니기에 또 그게 식사 약속인지 단순히 만나서 노는 것인지 면접을 보는지에 따라서 해당 프로그램을 구성하는 요소가 변화할 것이다. 다른 활동에서는 필요 없는 것들이 존재하기 때문이다. 이런 문제가 예상될 경우 개방 폐쇄 원칙에 따라서 각 활동 간의 변화할 것과 변하지 않을 것들을 구분 짓고 그를 인터페이스 즉 공통 코드를 만든다.

그리고 그를 상속받아서 각 활동을 정의 내린다. 식사라는 프로그램을 결국 사람의 만남의 한 종류이기에 사람의 만남의 한 종류로 다루어지면서 다른 만남을 추가할 때마다 식사라는 부분을 수정할 필요가 없어진다. 곰곰이 생각해보면 OCP도 결국 본질적으로 단일 책임 원칙과 큰 차이가 없다는 사실을 알 수 있다. 그냥 식사라는 책임이 만남이라는 책임으로 확장되었을 뿐이다.

리스 코브 치환의 원칙에 의하면 상속받은 타입은 기반 타입의 정의를 충실히 구현해야 한다. 즉, 만남의 종류라는 기반 타입을 상속받은 식사나 면접 등의 타입을 서로 교체하였을 때 프로그램이 정상적으로 동작해야 한다는 것이다. 이는 리스 코프의 행동적 하위 성이라는 개념은 가변 객체의 치환성이라는 개념을 정의 내리는데 그를 프로그래밍 환경에 맡게 적용한 원칙이다.

공통 정의나 코드를 사용하는 코드를 구현하였을 때 문제가 발생한다면 공통부분의 범위를 잘못 설정하였거나 사실 그 코드가 서로 관계가 없는 코드일 확률이 높다. 그럴 경우 해당 코드에서 억지로 공통부분을 따로 빼내어서 정의 내리려고 하지 말라는 의미이다. 즉, 공통 코드가 필요한 상황이 아니라면 그렇게 짤 필요가 없다는 의미이다.

이 또한 책임에 따라서 다르게 해석되는데 보통 OCP와 LSP를 동시에 지키는 것이 힘든 경우가 많으니 주의 깊게 신경 써야 할 부분이다.

인터페이스 분리의 원칙에 의하면 상속받은 인터페이스의 모든 부분을 구현해야 한다. 그렇지 않다면 차라리 여러 인터페이스를 만들어서 상속받는 게 낫다. 단일 책임과 리스 코브 치환 원칙과 유사한데 굳이 구분 짓자면 클래스와 인터페이스의 차이 정도가 있다. 물론 객체 지향이 아닌 개발에서 유의미한 부분을 찾자면 리스코브 치환의 원칙처럼 공통부분이 아닌 곳을 억지로 공통으로 묶지 말고 따로 만들자 정도가 되겠다.

의존성 역전의 원칙에 의하면 하위 레벨의 모듈에서 일어난 변경이 상위 레벨의 모듈의 변경을 요구하는 위계 관계를 끊어야 한다. 당연한 말이지만 하위 레벨의 모듈이 변경되었을 때 그를 호출하는 상위 레벨의 모듈도 영향을 받는다.
이를 방지하기 위해서 직접적으로 하위 레벨의 모듈을 호출(참조) 하지 않고 추상화된 레이어를 하나 추가해서 그 레이어를 호출한다. 그것이 인터페이스가 될 수 도 있겠고, 또는 wrap 한 함수가 될 수 도 있을 것이다.
이는 보통 다른 원칙을 지키려고 하면 자연스럽게 지켜지는 원칙이기도 하다.

본래 SOLID 원칙은 자연스럽게 개발에서 적용하고 있는 편이지만 해당 부분을 타인에게 설명하는데 있어서 조금 복잡하고 양이 많아서 귀찮은 점을 느꼈다. 이 글은 그를 쉽게 설명하기 위한 요약 정도 될 것 같다. SOLID는 프로그래밍 패러다임에 상관없이 결국 적절한 코드를 잘 짜는 방법이라고 정의 내릴 수 있다.

반응형

'개발' 카테고리의 다른 글

ChatGPT에 대한 감상  (0) 2023.01.21
기술부채에 대하여.  (0) 2022.05.11
클린코드와 표준  (0) 2022.04.28
JWT 간단정리  (0) 2022.01.27
개발 블로그  (0) 2021.06.25