클린아키텍처 6부: 세부사항

Jihyun·2021년 12월 27일
0

이 글은 한달 한권 챌린지의 두번째 책, 클린아키텍처를 읽고 정리한 내용이다.


30장. 데이터베이스는 세부사항이다

어플리케이션 내부 데이터의 구조는 시스템 아키텍처에서 중요하다. 하지만 데이터베이스는 데이터 모델이 아니다.

데이터베이스는 데이터에 접근할 방법을 제공하는 유틸리티다. 아키텍처 관점에서 보면 이러한 유틸리티는 저수준의 세부사항(메커니즘)일 뿐이라서 아키텍처와는 관련이 없다.


관계형 데이터베이스

관계형 테이블은 특정한 형식의 데이터에 접근하는 경우에는 편리하지만, 데이터를 테이블에 행 단위로 배치한다는 자체는 아키텍처적으로 볼 때 전혀 중요하지 않다. 애플리케이션의 유스케이스는 이러한 방식을 알아서는 안 되며 관여해서도 안 된다. 데이터가 테이블 구조를 가진다는 사실은 오직 아키텍처의 외부 원에 위치한 최하위 수준의 유틸리티 함수만 알아야 한다.

많은 데이터 접근 프레임워크가 테이블과 행이 객체 형태로 시스템 여기 저기에서 돌아다니게 허용하는데, 아키텍처적으로 잘못된 설계다. 이렇게 하면 유스케이스, 업무 규칙, 심지어는 내조차도 관계형 데이터 구조에 결합 되어 버린다.

데이터베이스 시스템은 왜 이렇게 널리 사용되는가?

한마디로 답하자면, 바로 ‘디스크’ 때문이었다. 디스크는 느리다. 프로그래머는 디스크 때문에 피해갈 수 없는 시간 지연이라는 짐을 완화하기 위해 해결책(색인, 캐시, 쿼리 계획 최적화)이 필요해졌다. 그리고 데이터를 표현하는 일종의 표준적인 방식도 필요했는데, 작업 중인 대상이 어떤 데이터인지 알 수 있어야 했기 때문이다. 간단히 말해서 데이터 접근 및 관리 시스템이 필요했다. 시간이 지나면서 이러한 시스템은 두 가지 유형으로 분리되었다.

하나는 파일 시스템 이었고, 다른 하나는 관계형 데이터베이스 관리 시스템(RDBMS)이었다.

두 시스템은 데이터를 디스크에 체계화해서, 각 시스템에 특화된 방식으로 접근해야 할 때 가능한 한 효율적으로 데이터를 저장하고 검색할 수 있도록 한다. 각 시스템은 데이터를 색인하고 배치하는 고유한 전략을 활용 한다.

디스크가 없다면 어떻게 될까?

디스크는 이제는 소멸 중인 부품이다. 디스크는 RAM으로 대체되고 있다.

디스크가 모두 사라지고 모든 데이터가 RAM에 저장된다면 데이터를 어떻게 체계화할 것인가? 이 데이터들을 다양한 데이터 구조로 체계화할 것이며, 데이터에 접근할 때 는 포인터나 참조를 사용할 것이다. 이것이 프로그래머가 하는 일이기 때문 이다.

사실 이 문제를 곰곰이 생각해 보면, 당신은 이미 이렇게 일하고 있다는 사실을 알아챌 것이다. 데이터가 데이터베이스나 파일 시스템에 있더라도, RAM으로 읽은 후에는 다루기 편리한 형태로 그 구조를 변경한다. 데이터를 파일이나 테이블 형태로 그대로 두는 경우는 거의 없다.

결론

데이터베이스는 그저 메커니즘에 불과하며, 디스크 표면과 RAM 사이에서 데이터를 옮길 때 사용할 뿐이다. 아키텍처 관점에서 본다면 디스크에 데이터가 있기만 한다면, 데이터가 어떤 형태인지는 절대로 신경 써서는 안 된다. 디스크 자체가 존재한다는 사실조차도 인식해서는 안 된다.



31장. 웹은 세부사항이다

I960년대 이래로 우리 업계는 일련의 반복되는 진동을 겪어왔고, 현재 웹은 그저 이러한 진동의 맨 끝에 있을 뿐이다. 이 진동은 모든 연산 능력을 중앙 서버에 두는 방식과 모든 연산 능력을 단말에 두는 방식 사이에서 끊임없이 움직여 왔다. 웹이 유명세를 탄 이래 지난 십여 년 사이에도 우리는 이러한 진동을 수차례 목격했다.

연산 능력을 중앙에 집중하는 방식과 분산하는 방식 사이에서 우리는 끊임없이 움직인다. 그리고 내 생각에 이러한 진동은 한동안 계 속될 것이다. IT 역사 전체로 시야를 넓히면 웹은 아무것도 바꾸지 않았다. 웹은 우리가 발버둥치면서 생기는 여느 수많은 진동 중 하나에 불과하다.

GUI는 세부사항이다. 웹은 GUI다. 따라서 웹은 세부사항이다. 그리고 아키텍트라면 이러한 세부사항을 핵심 업무 로직에서 분리된 경계 바깥에 두어야 한다.



32장. 프레임워크는 세부사항이다

프레임워크는 상당한 인기를 끌고 있다. 무료인데다 강력하며 유용한 프레임워크가 많다. 하지만 아무리 해도 프레임워크는 아키텍처가 될 수 없다.


프레임워크를 사용해야 할 경우 우리는 프레임워크 제작자가 제공하는 문서를 읽는다. 이 문서에서 프레임워크 제작자와 다른 사용자는 우리가 만들 소프트웨어와 프레임워크를 어떻게 통합할 수 있을지 조언한다. 대개의 경우 이들은 프레임워크를 중심에 두고 아키텍처는 그 바깥을 감싸야 한다고 말한다. 프레임워크 제작자는 가능하면 애플리케이션이 프레임워크에 공고하게 결합될 것을 강하게 역설한다. 그래서 프레임워크와 절대로 분리할 수 없는 방식으로 쓰는 사용자가 많아지는 일은 자신의 프레임워크가 효과적임을 검중하는 최고의 방법이다. 이 관계는 일방적이다. 모든 위험과 부담은 오롯이 당신이 감수할 뿐, 제작자가 감수하는 건 아무것도 없다.

위험 요인

  • 프레임워크의 아키텍처는 그다지 깔끔하지 않은 경우가 많다. 프레임워크는 의존성 규칙을 위반하는 경향이 있다.

  • 프레임워크는 애플리케이션의 초기 기능을 만드는 데는 도움이 될 것이 다. 하지만 제품이 성숙해지면서 프레임워크가 제공하는 기능과 틀을 벗어나게 될 것이다.

  • 프레임워크는 당신에게 도움되지 않는 방향으로 진화할 수도 있다. 도움도 되지 않는 신규 버전으로 업그레이드하느라 다른 일을 못할 수도 있다. 심지어 사용 중이던 기능이 사라지거나 반영하기 힘든 형태로 변경될 수 도 있다.

  • 새롭고 더 나은 프레임워크가 등장해서 갈아타고 싶을 수도 있다.


해결책

해결책은 무엇인가? 프레임워크를 시용할 수는 있다. 다만 프레임워크와 결합해서는 안 된다. 프레임워크는 아키텍처의 바깥쪽 원에 속하는 세부 사항으로 취급하라. 프레임워크가 핵심 코드 안으로 들어오지 못하게 하라. 대신 핵심 코드에 플러그인할 수 있는 컴포넌트에 프레임워크를 통합하고, 의존성 규칙을 준수 하라.

예를 들어 스프링(Spring)은 훌륭한 의존성 주입 프레임워크다. 그러나 업무 객체는 절대로 스프링에 대해 알아서는 안된다. 업무 객체보다는 메인(Main) 컴포넌트에서 스프링을 사용해서 의존성을 주입하는 편이 낫다. 메인은 아키텍처 내에서 가장 지저분한, 최저 수준의 컴포넌트이기 때문에 스프링을 알아도 상관 없다.

C++를 사용하고 있다면 STL과 결합해야 할 가능성이 높다. 자바를 사용한다면 표준 라이브러리와 반드시 결합해야 한다. 이러한 관계는 정상이다. 하지만 선택적이어야 한다. 애플리케이션이 프레임워크와 결합하고자 한다면 애플리케이션의 남은 생애 동안 그 프레임워크와 항상 함께 해야 한다는 사실을 반드시 명심해야 한다.



34장. 빠져 있는 장

지금까지 읽은 모든 조언은 더 나은 소프트웨어를 설계하는 데 확실히 도움이 될 것이다. 이러한 소프트웨어는 올바르게 정의된 경계, 명확한 책임, 그리고 통제된 의존성을 가진 클래스와 컴포넌트로 구성될 것이다. 하지만 악마는 항상 디테일(구현 세부사항)에 있는 법이며, 이점을 심사숙고하지 않는다면 마지막 고비에 걸려 넘어지기 십상일 것이다.


계층 기반 패키지

가장 단순한 첫 번째 설계 방식은 전통적인 수평 계층형 아키텍처다. 기술적인 관점에서 해당 코드가 하는 일에 기반해 그 코드를 분할한다. 흔히 우리는 이 방식을 ‘계층 기반 패키지’라고 부른다.

이 전형적인 계층형 아키텍처에는 웹, 업무 규칙, 영속성 코드를 위해 계층이 각각 하나씩 존재한다. 다시 말해 코드는 계층이라는 얇은 수평 조각으로 나뉘며, 각 계층은 유사한 종류의 것들을 묶는 도구로 사용된다.

마틴 파울러(Martin Fowler)는 처음 시작하기에는 계층형 아키텍처가 적합하다고 얘기했다. 다수의 정보에서도 계층형 아키텍처를 만드는 길로 인도한다. 이 아키텍처는 엄청난 복잡함을 겪지 않고도 무언가를 작동시켜주는 아주 빠른 방법이다. 문제는, 마틴이 지적했듯이 소프트웨어가 커지고 복잡해지기 시작하면 머지 않아 모든 코드를 담기엔 부족하다는 사실을 깨닫고, 더 잘게 모듈화해야 할지를 고민하게 될 것이다.

계층형 아키텍처는 업무 도메인에 대해 무것도 말해주지 않는다는 문제도 있다. 전혀 다른 업무 도메인이라도 코드를 계층형 아키텍처로 만들어서 나란히 놓고 보면, 웹, 서비스, 리포지토리로 구성된 모습이 비슷하게 보일 것이다.

기능 기반 패키지

이는 서로 연관된 기능, 도메인 개념에 기반하여 수직의 얇은 조각으로 코드를 나누는 방식이다.

인터페이스와 클래스가 단 하나의 패키지에 속하게 되어 코드의 상위 수준 구조가 업무 도메인에 대해 무언가를 알려주게 된다. 드디어 우리는 이 코드 베이스가 웹, 서비스, 리포지토리가 아니라 액션과 관련한 무언가를 한다는 걸 볼 수 있다. 소프트웨어 개발팀이 수평적 계층화(계층 기반 패키지)의 문제를 깨닫고, 수직적 계층화(기능 기반 패키지)로 전환하는 걸 자주 목격했다.

포트와 어댑터

포트와 어댑터(Ports and Adapters) 혹은 육각형 아키텍처(Hexagonal Architecture), 경계/ 컨트롤러/엔티티(BCE) 등의 방식으로 접근하는 이유는 업무/도메인에 초점을 둔 코드가 프레임워크나 데이터베이스 같은 기술적인 세부구현과 독립적이며 분리된 아키텍처를 만들기 위해서다.

코드 베이스는 ‘내부’(도메인)와 ‘외부’(인프라)로 구성됨을 혼히 볼 수 있다. ‘내부’ 영역은 도메인 개념을 모두 포함하는 반면, ‘외부’ 영역은 외부 세계(예를 들면 UI, 데이터베이스, 서드파티 통합)와의 상호작용을 포함한다. 여기서 주요 규칙은 바로 ‘외부가 ‘내부’에 의존하며, 절대 그 반대로는 안 된다는 점이다.

컴포넌트 기반 패키지

‘컴포넌트 기반 패키지’는 지금까지 우리가 본 모든 것들을 혼합한 것으로, 큰 단위의 단일 컴포넌트와 관련된 모든 책임을 하나의 자바 패키지로 묶는 데 주안점을 둔다. 이 접근법은 서비스 중심적인 시각으로 소프트웨어 시스템을 바라보며, 마이크로서비스 아키텍처가 가진 시각과도 동일하다. 포트와 어댑터에서 웹을 그저 또 다른 전달 메커니즘으로 취급하는 것과 마찬가지로 컴포넌트 기반 패키지에서도 사용자 인터페이스를 큰 단위의 컴포넌트로부터 분리해서 유지한다.

본질적으로 이 접근법에서는 ‘업무 로직’과 영속성 관련 코드를 하나로 묶는데, 이 묶음을 ‘컴포넌트’라고 정의하자. 이 정의는 소프트웨어 시스템의 정적 구조를 컨테이너, 컴포넌트, 클래스(또는 코드)의 측면에서 계층적으로 생각하는 간단한 방법이다. 이 방법론에서 소프트웨어 시스템은 하나 이상의 컨테이너(예를 들어 웹 애플리케이션, 모바일 앱, 독립형 애플리케이션, 데이터베이스, 파일시스템 등)로 구성되며, 각 컨테이너는 하나 이상의 컴포넌트를 포함한다. 또한 각 컴포넌트는 하나 이상의 클래스(또는 코드)로 구현된다. 이때 각 컴포넌트가 개별 jar 파일로 분리될지 여부는 직교적인 관심사다.

결론:빠져 있는 조언

이 장은 최적의 설계를 꾀했더라도, 구현 전략에 얽힌 복잡함을 고려하지 않으면 설계가 순식간에 망가질 수도 있다는 사실을 강조하는 데 그 목적이 있다. 설계를 어떻게 해야만 원하는 코드 구조로 매핑할 수 있을지, 그 코드를 어떻게 조직화할지, 런타임과 컴파일타임에 어떤 결합 분리 모드를 적용할지를 고민하라. 가능하다면 선택사항을 열어두되, 실용주의적으로 행하라. 그리고 팀의 규모, 기술 수준, 해결책의 복잡성을 일정과 예산이라는 제약과 동시에 고려하라. 또한 선택된 아키텍처 스타일을 강제하는 데 컴파일러의 도움을 받을 수 있을지를 고민하며, 데이터 모델과 같은 다른 영역에 결합되지 않도록 주의하라. 구현 세부사항에는 항상 문제가 있는 법이다.

0개의 댓글