아키텍처: 목적과 형식
소프트웨어 아키텍처를 개선할 기회가 생겼다. 그래서 아키텍처의 지향점을 설정하고 동료들과 같이 기존 아키텍처의 과거 유산들을 검토했다. 여러 가지를 많이 논의했고 ‘신뢰성’과 ‘유지보수성’이라는 2가지 품질 속성을 정의했다. 이 중 유지보수성에 맞도록 애플리케이션 아키텍처는 클린 아키텍처 사상을 기반으로 한 아키텍처로 구성했다.
해당 아키텍처를 적용하면서 몇 가지 알게 된 것들이 있다. 그 중 많이 접하게 된 부분이 ‘오해’다. 인터넷이든 사내든 클린 아키텍처가 많은 것을 변화시킬 만능 수단처럼 생각하거나 또는 부질없다고 생각하는 것이다. 그리고 특정 용어 또는 특정 구조(패키지 또는 트리 구조)대로 사용하지 않는다고 클린 아키텍처가 아니라고 하는 주장이다.
만능 수단처럼 생각하거나 부질없다고 생각하는 대부분의 이유는 ‘형식’을 중점으로 살펴보기 때문이다. 형식 관련된 부분을 이야기하기 위해 헥사고날 아키텍처(Hexagonal Architecture)를 예시로 들겠다. 형식 위주로 해당 아키텍처를 바라볼 경우 다음의 예시는 헥사고날 아키텍처라고 말한다. 그리고 그 구조에 맞추어 작성하는 것이 헥사고날 아키텍처에 따라 구현하는 것이라고 생각한다. 그 형식대로 수행해야 목적 달성이 된다고 생각한다.
/
├── adapter
└── application
├── domain
└── port
하지만 이 형식만 맹목적으로 따르다 보면 무언가 불편하고, 속도도 느리고, 무의미한 작업 같고, 정말 이 활동들이 목적 달성에 도움이 되는지 등 여러 가지를 생각하게 된다. 그러다 결국 N-Layer 아키텍처(N-Tier 또는 Multitier 아키텍처) 보다 불편하고, 좋지 않다는 생각과 함께 부질없다고 나름의 결론을 지어버린다. 목적과 의미를 생각하지 않고 형식만을 따지니, 하고 있는 활동들이 무용하다고 생각하는 것이다.
이렇게 형식만을 따져서 생기는 소프트웨어 개발 측면의 대표적인 부정적 사례들은 다음과 같다.
- 디자인 패턴 중독: 각 디자인 패턴이 주는 이점들이 있고 그 목적에 맞게 사용해야 한다. 하지만 실제 문제 해결에 적합하지 않거나 불필요하지만 디자인 패턴을 적용할 수 있는 형식이기 때문에 적용하고 그에 따라 코드가 불필요하게 복잡해져 유지보수를 어렵게 만든다.
- 코드 스타일 강박: 코드 스타일을 통일하면 가독성을 높여 유지보수성을 향상시킨다. 다만 코드 스타일이나 린트(lint) 규칙에 지나치게 집착해서 실제 구현된 결과물을 판단하지 않고, 이 때문에 실제 애플리케이션의 품질이나 개발 속도 개선이 도움을 주지 않는다.
- 도구 및 프레임워크 남용: 새로운 도구나 프레임워크를 도입하면 생산성과 품질을 높일 기회가 될 수 있다. 하지만 단순히 그 프레임워크를 도입하고 사용하는 그 형식에만 집착해 실질적으로 불필요한 도구 또는 프레임워크임에도 불구하고 도입해서 애플리케이션을 복잡하게 만들고 의존성도 강하게 만들어 유지보수성을 떨어트리게 된다. 예를 든다면 non-blocking으로 제공할 서비스가 아닌데 리액티브(reactive) 프레임워크의 형식이 멋져 보이고 트렌디하다고 생각해 적용하는 것이다.
- 언어의 특정 기능 남용: 위 ‘도구 및 프레임워크 남용’과 동일하다. 대표적인 사례로 람다(lambda)가 있다. 람다를 쓰지 않아도 충분한 가독성을 확보하고 더 나은 성능을 보일 때가 있는 데 람다를 사용함으로써 더 개선된 활동을 하고 있다고 생각하는 것이다.
- 아키텍처 원칙 남용: DRY, KISS 등 여러 가지 아키텍처 원칙이 있고 이것 또한 목적에 맞게 사용해야 한다. 하지만 그 목적을 이해하지 않고 형식에 치중해 유지보수를 어렵게 만든다. 예로 DRY의 경우 ‘중복된 코드를 만들지 말라’, ‘코드를 재사용하라’는 것을 보고 ‘중복된 코드와 코드 재사용’ 형식에만 치중해 행동하는 경우다. 대다수의 경우 냄새(smell)로서 진짜 중복 코드인 경우가 많지만 서로 다른 비즈니스 목적 달성을 위해 똑같아 보이는 코드가 유지되는 경우가 있다. 이 경우는 비즈니스 기능의 의존성을 만들지 않기 위함인데 그 본질을 보지 않고 ‘중복된 코드’ 형식만 보고 중복 코드를 제거하고 불필요한 의존성을 만드는 경우다.
- 클린 코드 강박: 이것도 무척 많이 보이는 사례다. 책에서 보이는 그 형식만을 보고 따라 하는 것이다. 덕분에 클린 코드 반대 밈도 많이 생겨나는 것을 보았다. 대표적인 사례로 함수를 작게 만들고 한 가지 일만 하라는 것을 곧이곧대로 따라 하는 것이다.
이렇듯 본질을 외면하고 형식만 보는 것은 다음과 같다. ‘성공한 대다수의 사람이 아침 일찍 일어나 운동을 하고 찬물 샤워를 한다’는 것을 보고 아침 일찍 일어나 운동을 하고 샤워를 하는 것이 성공의 핵심이라고 보고 이것만 따라 하는 것이다. 실제 성공한 사람들의 본질적인 원천은 다 다른데, 눈으로 보면서 쉽게 따라 할 수 있고 따라 함으로써 동질감을 쉽게 느낄 수 있기에 근본적인 것을 보지 않고 형식적인 것만 취하는 것이다.
“본질을 돌아보고 목적을 생각하라” 내가 하고자 하는 말이다. 형식을 목적으로 보지 말고, 목적이 먼저 있고 그 목적에 형식이 부합하는지를 봐야 한다. 클린 아키텍처도 목적에 따라 선택하면 된다. 소프트웨어 아키텍처의 목적이 ‘초기에 사용자에게 빠르게 딜리버리’라면 N-Layer 아키텍처를 선택하는 게 더 도움이 될 수 있다. 다만, 불완전한 레이어 경계가 되지 않도록 아키텍처 거버넌스를 잘 구축해야 한다. 소프트웨어 아키텍처 목적이 ‘유지보수성을 기반으로 한 지연 없는 딜리버리’라면 클린 아키텍처를 선택하는 게 더 도움이 될 수 있다. N-Layer 아키텍처보단 더 쉽게 아키텍처 거버넌스 특히, 피트니스 함수를 구축할 수 있다.
형식은 중요하지 않을 수도 있고 중요할 수도 있다. 이 말은 형식이 어떤 것을 달성해 주는 가장 근본적인 동력이 되지는 않지만, 그 달성을 위해 지속적으로 도움을 주는 역할을 한다는 말이다. N-Layer 아키텍처와 클린 아키텍처가 제공하는 기본적인 품질 수준이 있다. 각 아키텍처는 적어도 그 품질 수준을 제공해 줄 수 있다. 이 부분은 현실적으로 꽤 중요하다. 현실적인 조직은 이상적인 개발자들로만 구성되어 있지 않다. 이상적인 개발자들로 구성되어 있다면 어떤 형식이든 상관없이 딜리버리도 빠르고 유지보수성도 매우 좋은 코드를 개발할 것이다. 하지만 이는 현실적으로 불가능하다. 그런 사람들로 구성할 수 없다. 조직이 잘하는 사람들로 구성되어 있어도 개인사로 인해 몇몇 구성원들이 빠져나가고, 이후 실력이 다른 구성원들로 채워질 수 있다. 코드는 영속적인데 사람들은 계속 교체된다. 이런 환경에서 어떤 형식이 구축되어 있고 그 형식을 지속적으로 모니터링 할 수 있는 환경이 구축되어 있다면 최소한 그 수준의 품질은 제공해 준다. 형식은 이런 부분으로 접근해야 한다.
모든 것을 해결해 주는 아키텍처는 없다. 목적에 맞게 선택해야 한다. 대부분의 경우 거의 표준으로 잡혀 있는 아키텍처 자체에는 문제가 없다. 장단점이 명확하게 기술되어 있는데 그것을 무시하고 목적에 부합하지 않고 사용하는 것이 문제다.
아키텍처들의 근본적인 목적이 무엇인지를 생각해 봐야 한다. N-Layer 아키텍처, 클린 아키텍처, 헥사고날 아키텍처, 오니언 아키텍처, 바이퍼 패턴, MVVM 패턴 등등 근본적으로 공통적인 부분은 계층의 분리를 통한 관심사의 분리다. 이 목적을 달성하는 게 주목적이지 각 아키텍처에서 보여준 형식을 따라 하는 것이 주목적은 아니라는 것이다.
목적에 부합하게, 그리고 그 형식을 일부 변경해서 본인 조직의 개발 문화에 맞추어 사용하고 있는지를 먼저 되돌아보자. “형식에 매몰되지 말고, 본질을 돌아보고 목적을 생각하라.”