카테고리 없음

왜 내 코드만 유지보수가 힘들까? SOLID 원칙에서 답을 찾다 _ 1편 [Clean architecture]

ksc036 2025. 9. 2. 00:29

개발자의 기본은 하나의 함수가 하나의 일만 하도록 깔끔하게 짜는 것이라고 생각한다.
하지만 함수 하나하나를 아무리 잘 만들어도, 그것들이 어떻게 묶이고 배치되느냐에 따라 전혀 다른 결과가 나온다.

 

배치가 잘못되면, 잘 만든 함수조차도 서로 발목을 잡으며 문제를 일으킨다.
그래서 함수의 품질만큼이나 함수들을 어떻게 조직하고 구성할 것인가가 중요하다.

 

SOLID원칙은 함수와 데이터 구조를 클래스로 배치하는 방법, 그리고 이들 클래스를 서로 결합하는 방법을 설명해준다.

 

 이를 잘 사용하면 

  • 변경에 유연하다.
  • 이해하기 쉽다.
  • 많은 소프트웨어 시스템에 사용 될 수 있는 컴포넌트의 기반이 된다.

이러한 장점들을 얻을 수 있다.

 

그럼 지금부터 하나씩 알아보자.

 


SRP : 단일 책임 원칙

단일 모듈의 변경의 이유가 하나, 오직 하나뿐이어야 한다.

 

저자는 SRP가 이름 때문에 가장 잘 전달되지 못한 원칙이라고 생각 한다. 

단일 책임 원칙은 하나의 일만 해야 하는 게 아니라 변경의 이유가 오직 하나여야만 한다고 한다.

 

즉, SRP를 지킨 클래스라 하면 하나의 책임 아래에는 여러 가지 일을 하는 집합이라고 생각하면 된다.

 

이 원칙이 잘 지켜지지 않으면 나타나는 두 가지 징후들이 있다. 

 

1. 우발적 중복

중복에는 두 가지가 있다. 정말로 같은 로직을 반복하는 진짜 중복우발적 중복이다. 

 

이 둘을 구분하는 방법은 미래를 생각해보면 된다.

앞으로도 계속 같은 로직으로 유지되어야 하는 것들이라고 예상된다면 이것은 진짜 중복일 확률이 높다.

 

지금은 같지만 이후에는 결국에 갈라져야 할 것 이라 예상 된다면 이것은 우발적 중복일 확률이 높다.

 

코드에 이런 우발적 중복이 나타난다면 SRP를 위반했는지 의심해보면 좋다. 

왜냐하면 우발적으로 중복된 코드가 보인다는 것은 앞으로 달라져야 하지만 같은 클래스로 묶였을 확률이 있다는 것을 의미한다.

 

즉, 다른 이유로 인해 변경되어야 하는 코드가 같은 클래스에 존재할 가능성이 있다는 의미이다.

 

2. 병합

소스코드 충돌을 의미한다.

 

서로 다른 이유로 변경을 하고 있는 두 개발자의 코드가 충돌한다는 것은, 변경의 이유가 두 개일 수도 있다는 것을 단적으로 보여주는 예이다.

 

이런 이상 징후가 나타날 때마다 클래스가 SRP를 위반한 것은 아닌가 의심을 해보면 좋다.

 

SRP 해결책

해결책은 간단하다. 붙어있는 코드, 함수, 데이터를 따로 클래스로 분리해주면 된다. 그리고 이때 각각의 클래스는 서로의 존재를 모르게 만들어 주면된다.

 


OCP : 개방 폐쇄 원칙

 

소프트웨어 개체는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

 

이게 무슨 말일까? SRP의 정의보다는 추상적으로 서술되어 있어서 뜻이 한 번에 와닿지 않는다.

 

이는 OCP의 목적을 보면 조금 더 쉽게 와닿을 수 있다.

 

OCP의 목적

OCP의 목표는 "시스템을 확장하기 쉬운 동시에 변경으로 인해 시스템이 받는 영향을 최소화 하는데 있다."  라고 적혀있다.

 

그렇다면 이는 어떻게 달성할 수 있을까?

 

이를 설명하기 전에 인터페이스에 대한 이야기를 먼저 하려고한다.

 

인터페이스가 무엇인가?

 

C타입 충전기를 예시로 들어보겠다. 만약 내 핸드폰이 C타입으로 충전 가능하다고 하자.

이때 우리는 어떤 회사에서 제작되었는지와 상관없이 C타입 충전기라고 한다면 이것을 사용하면 휴대폰을 충전할 수 있다고 생각하게 된다.

 

이게 인터페이스이다. 사용하고자 하는 쪽( = 휴대폰을 충전하려는 입장)은 구현 세부 사항을(어떤 회사가 어떤 설계도로 만들었는지) 전혀 모르고도 C타입이라면 사용할 수 있다고 생각할 수 있게 된다.

 

이때 충전하고자 하는 휴대폰 쪽은 C타입 인터페이스에 의존한다고 한다. 

C타입을 충전기를 만든 회사는 C타입 인터페이스를 구현한다고 한다.

 

즉, 휴대폰은 C타입이라는 인터페이스에 의존하기 때문에, C타입을 제작한 회사(구현과) 상관 없이 우리가 원하는 동작을(충전을) 보장받을 수 있는 것이다.

 

코드로 가져오기

이 원리를 코드로 가져와 보자. 우리의 코드는 "충전하려고하는 휴대폰입장"과 "충전을 구현하는 회사" 둘 다 포함하고 있다.

 

이때 휴대폰을 충전하려고 하는 쪽 코드 (= 구현을 사용하는 쪽) 에서 인터페이스에 의존하게 만들어 두면, 구현부(=구체적인 충전 로직)는 쉽게 교체할 수 있다.

 

예를들어, 우리 코드의 요구사항이 저속충전에서 고속충전으로 바뀌었다고 해보자.

이때 C타입 인터페이스 계약만 잘 지킨다면 우리는 구현부만 저속충전내용에서 고속충전 내용으로 바꿀 수 있게된다. 

 

기존의 코드를 지우는 것이 아니라 단순히 고속 충전 구현을 추가만 하고 인터페이스의 구현부만 변경하는 식으로 기능 변경이 가능하다. 

 

즉, 인터페이스 사용처와 구현체는 “인터페이스라는 계약”에 의해 묶이게 된다.

 

 예시로 인터페이스를 들었지만 이렇게 상속, 인터페이스 등을 통해서 새 기능을 확장할 때 기존 코드를 건드리지 않을 수 있으면 ocp를 만족한다고 한다.

 

 

쓰다보니 내용이 길어져서 2편에서 계속...

 

 


[Clean architecture] 왜 내 코드만 유지보수가 힘들까? SOLID 원칙에서 답을 찾다 _ 2편

https://ksc036.tistory.com/3