프론트엔드 개발자인 단테는 아키텍처에 대한 지식이 부족하여
만들면서 배우는 클린 아키텍처
를 읽기로 결심했다.
SPA를 사용하다보면 클린코드나 라이브러리 사용에 능숙해지는데 집중하여 소프트웨어 아키텍처에 대해 잘 신경쓰지 못하는 경우가 많습니다.
특히 비전공자 출신 프론트엔드 개발자분들이라면 소프트웨어 엔지니어링, 아키텍처와 같은 단어에 익숙하지 않은 환경에 많이 노출되었었다고 생각합니다.
저는 엔지니어라는 직업을 주어진 자원을 활용해 효과적으로 문제를 해결하는 사람
으로 정의하고 있습니다.
방탈출 게임을 많이 해본 사람이 더욱 빠르게 비슷한 컨셉의 게임에 적응하는 것 처럼, 소프트웨어 엔지니어는 익숙하지 않지만 기본적이고 중요한 용어들을 의도적으로 듣고, 적용해보려 노력해야 한다고 생각합니다. 그러한 노력들이 모여 훗날 훌륭한 엔지니어링이란 결과로 도출될 것이기 때문입니다.
서론이 길었습니다만, 여전히 계층형 아키텍처라는 단어는 프론트엔드 개발자에게 너무나 생소하기만합니다.
저보다 훨씬 유명한 사람이 거론했었던 주제라고 한다면 좀 더 동기부여가 살 수 있을까요? joduldu님의 블로그에서도 계층형 아키텍처를 다룬 글이 있어 소개합니다. 제 글 말고 다른 글에서는 어떻게 설명했는지 참고하고 싶다면 이 글을 참고해주세요.
저는 대학교 재학시절 객체지향 프로그래밍이라는 수업을 수강한 적이 있습니다. 한 학기동안 객체지향 프로그래밍을 적용한 프로그램을 만드는 수업이었는데 실상은 디자인 패턴을 몇 개나 적용했는지에 따라 학점이 달라지는 이상한 수업이었습니다.
당시에는 패턴 적용에 집중하느라 내가 작성하는 모든 코드가 얼마나 유지보수가 좋은 코드인지에 대해 전혀 관심을 기울이지 않았었는데요, 데이터베이스에 접근하거나 UI를 그리거나 비즈니스 로직을 작성할 때 아무 생각도 하지 않고 한데 섞어 작성했었습니다.
이렇게 작성을 하게 되면 특정 요구사항의 변경이 일어났을 때 대응하기가 매우 어려워집니다.
따라서 전통적인 어플리케이션을 작성할 때 각각의 역할을 다른 레이어로 나뉘어 한 레이어가 다른 레이어에 의존하는 형태를 띄게끔 설계를 했습니다. 이걸 계층형 아키텍쳐라고 합니다.
software-architecture-patterns
의 저자 엉클 밥에 의하면
계층형 아키텍쳐(이하 레이어 아키텍처
)에는 필수로 존재해야 할 레이어나 최소 요구 레이어수는 존재하지 않지만 보통 아래와 같은 3계층 혹은 4계층으로 나누어 집니다.
Although the layered architecture pattern does not specify the number and types of layers that must exist in the pattern, most layered architectures consist of four standard layers: presentation, business, persistence, and database
각 계층이 하는 역할에 대해 알아보겠습니다.
웹 계층은 presentation layer
라고 불리기도 합니다. 유저 인터페이스나 브라우저 커뮤니케이션 로직을 다룹니다.
웹 계층은 사용자의 요청을 받아 도메인 혹은 비즈니스 계층에 있는 서비스로 요청을 보내는 역할을 합니다.
도메인 계층 / 비즈니스 계층은 특정 요청에 대한 특정 서비스의 주요 로직을 수행하고 도메인 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층의 컴포넌트를 호출합니다.
영속성은 영어로 Persistence라고 부릅니다. 이 단어를 이해하려면 엔티티에 대해 먼저 알아야합니다. 엔티티는 데이터의 집합, 저장되고 관리되어야 하는 데이터를 의미합니다. 데이터베이스에서는 레코드를 의미합니다. 영속성은 엔티티를 영구 저장하는 환경을 의미합니다. 쉽게 말하자면 SQL, HSQL등을 사용하는 컴포넌트들이 있는 레이어에 따라 비즈니스 레이어에 속하기도 합니다.
In some cases, the business layer and persistence layer are combined into a single business layer, particularly when the persistence logic (e.g., SQL or HSQL) is embedded within the business layer components
출처 https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.html
Each layer in the architecture forms an abstraction around the work that needs to be done to satisfy a particular business request. - 엉클 밥
계층형 아키텍처의 장점은 각 계층의 관심사가 다른 계층과 철저하게 분리되어 있다는 것입니다.
각 계층별로 역할이 분명하게 나뉘어져 있기 때문에 다른 계층이 해야 할 역할에 대한 정보를 가지고 있을 필요가 없습니다.
예를 들어 웹 계층은 고객 정보를 어떻게 가져오는지에 대해 알고 있을 필요가 없습니다.
위와 같은 개념을 소프트웨어 공학에서는
관심사의 분리 seperation concern
라고 합니다.
관심사의 분리가 충족되면 각 계층의 역할을 수행할 수 있는데 집중하여 도메인 로직을 개발할 수 있고 테스트 로직을 작성할 수 있기 떄문에 유지보수에도 용이합니다.
계층형 아키텍처에서 각 계층이closed
되었다는 말은 한 계층에 전달된 Request는 바로 밑에 있는 계층을 꼭 거쳐야만 타켓 계층에 도달할 수 있다는 의미입니다.
각 계층간 요청이 순차적으로 진행되는게 보장되면 한 계층의 변경은 다른 계층에 영향을 미치지 않는다는 점이 보장됩니다.
계층간 요청이 순차적으로 진행되지 않는다면 영속성 계층에서의 SQL 구문의 변경이 두 계층 이상 거리에 있는 presentation layer, business layer의 컴포넌트들 간에 영향을 미쳐 각 컴포넌트 간 강한 결합성을 야기 시킵니다.
이는 조그마한 변경에도 큰 유지보수 비용을 치러야 하는 아키텍처가 되게 합니다.
좋습니다. 우리는 seperation of concerns와 isolation layer를 통해 변경에 용이한 아키텍처를 만들었습니다.
그런데 어플리케이션이 복잡해짐에 따라 계층이 5개 이상으로 늘어나게 되는 경우가 있습니다.
예를 들어 비즈니스 레이어의 각 컴포넌트들이 공통적으로 사용하는 컴포넌트들을 service layer라는 계층에 분리하여 만들고 싶을 수 있습니다.
하지만 이 경우 비즈니스 레이어는 영속성 레이어를 통하기 위해서는 공통 컴포넌트를 사용하지 않아도 꼭 서비스 레이어르 거쳐야 한다는 문제가 있습니다.
따라서 이를 해결하기 위해 service layer를 앞서 봤던 closed가 아닌 opened 상태로 만들 수 있습니다.
잘 만들어진 계층형 아키텍처는 변경에 용이하지만 코드에 나쁜 습관들이 스며들기 쉽게 만들고 시간이 지남에 따라 유지보수 난이도가 올라갈 수 있게 하는 허점들을 노출한다는 문제가 있습니다.
위 그림에서 보았듯이 계층형 아키텍처의 제일 토대에는 영속성이 있고 이는 곧 설계의 토대가 데이터베이스에 있다고 볼 수 있습니다.
좋은 객체지향 설계를 위해서는 각 객체가 수행해야 할 역할과 행동들을 중심으로 객체간 관계를 모델링 합니다. 그리고 모델링한 행동들에 따라 어플리케이션의 상태가 변경됩니다.
하지만 계층형 아키텍처에서 의존성의 방향이 아래 계층을 향하고 최하단에는 데이터베이스 관련 레이어가 존재하기 때문에 데이터베이스의 구조를 먼저 생각하고 이를 토대로 도메인 로직을 구현합니다.
하지만 비즈니스 관점에서는 데이터베이스 주도 설계는 옳지 않은 방법입니다. 도메인 로직을 구성하고 로직을 제대로 이해했는지 먼저 확인하지 않고 영속성 계층과 웹 계층을 만들기 떄문입니다.
데이터 베이스 계층을 중심으로 아키텍처가 만들어지는 이유는 ORM을 사용하기 때문입니다.
현대의 많은 어플리케이션에서는 ORM 프레임워크를 사용합니다.
ORM에 의해 관리되는 엔티티들을 일반적으로 영속성 계층에 주로 두는데 아래 계층으로의 방향으로만 접근 가능하기 떄문에 도메인 계층에서는 엔티티에 접근할 수 있으며, 서비스가 영속성 모델을 비즈니스 모델처럼 사용하게 됩니다.
도메인 레이어의 서비스 컴포넌트는 도메인 로직뿐만 아니라 즉시로딩/지연로딩, 데이터베이스 트랜잭션, 캐시 플러시등 영속성 계층에서 담당해야 할 역할들을 수행하게 됩니다.
이는 영속성 계층과 도메인 계층 사이에 강한 결합을 생기게 하여 변경에 용이하지 않은 아키텍처를 만들게 합니다.
객체와 객체간의 강한 결합성이 생긴다면 한 객체가 한 객체에 의존하는 형태를 띄게 되고 이것은 좋은 변경에 용이한 설계라고 할 수 없습니다. 이 내용은 객체지향의 오해와 사실
, 오브젝트
에도 나와있는 내용입니다.
영속성 모델을 도메인 모델 처럼 사용하기 때문에 영속성 코드가 도메인 코드에 녹아들어가 둘 중 하나만 바꾸는 것이 어려워집니다. 계층형 아키텍처를 사용하는 본래의 의도와 정반대의 특징을 띄게 됩니다.
어느 실험에서 평균이상의 경제력을 가진 주민들이 거주하는 동네와 빈곤층이 거주하는 동네에 표지판을 땐 자동차를 24시간동안 가만히 놔두고 관찰해보았습니다.
빈곤층이 거주하는 동네에서는 불과 몇 시간이 되지 않아 자동차가 박살나고 대부분의 부품이 도난당했다고 합니다.
반면 평균 이상의 동네에서는 별 다른점을 관찰하지 못하여 실험자들이 자동차의 창문 중 하나를 꺠보았다고 합니다. 그러자 빈곤층 동네에 설치해두었던 자동차와 마찬가지로 24시간이 지나지 않아 온갖 부품들이 도난당하는 것을 발견할 수 있었다고 합니다.
계층형 아키텍처에서 존재하는 유일한 제약조건은 하위 계층이 상위 계층에 접근하지 못한다는 것입니다.
만약 하위에서 상위 계층을 접근하고 싶다면 컴포넌트를 한 계층 아래로 내려버리면 됩니다. 이러한 접근 방법을 지름길
이라고 합니다.
프로젝트가 고도화되고 마감이 다가올 수록, 몇 번의 지름길을 선택하는 것이 반복될 수록 또 다른 지름길을 선택하는 것에 대한 심리적 부담감은 훨씬 낮아지게 됩니다.
이러한 심리적 효과를 심리학에서는 깨진 창문 이론
이라고 합니다.
도메인 계층에만 존재하던 컴포넌트를 영속성 계층으로 내리는 작업이 반복되면 결국 아래와 같이 영속성 계층은 비대해집니다.
이는 하나의 계층이 여러 역할을 수행한다는 점에서 관심사의 분리가 제대로 이뤄지는 계층이라고 볼 수 없습니다.
앞서 보았듯이 계층형 아키텍처에는 open 레이어가 존재할 수 있습니다.
그리고 실제 프로젝트를 수행하며 간단한 요구사항에도 open layer를 추가하고 싶은 유혹을 느끼는 순간들이 발생하게 됩니다.
엔티티의 필드를 하나만 조작하면 된다는 간단한 요구사항에 의해
도메인 계층을 open 레이어로 만들고
도메인 로직을 웹 계층에서 구현하면 어떻게 될까요?
계층형 아키텍처는 웹 계층이 영속성 계층을 사용하는 것을 막지 않습니다.
그렇게 되면 강한 결합이 웹 계층과 영속성 계층 사이에도 생기게 됩니다.
이러한 부분이 문제가 되는 이유는 영속성 계층을 사용하기 위해 웹 계층에도 도메인 로직이 생기기 때문입니다. 웹 계층에도 도메인 로직이 하는 책임이 추가되었습니다.
책임이 분산되니까 테스트 코드를 작성하는데 더 신경쓸게 많아집니다.
좋은 아키텍처는 기능을 추가하거나 변경할 때 변경할 적절한 위치를 찾는 일에도 도움을 주어야 합니다.
이 관점에서 계층형 아키텍처가 좋은 선택이 되지 못하는 이유는 다음과 같습니다.
앞서 봤듯이 데이터 베이스 기반의 설계로 인해 비즈니스 규칙과 영속성 계층간의 결합성이 높아져 어느 곳을 수정해야 하는 지에 대한 통일성이 낮아집니다.
계층형 아키텍처에서 지름길을 선택하는 일이 빈번해져 한 계층의 너비가 비대해진다면 한 계층에서만 여러 개의 유즈케이스를 담당하게 됩니다. 넓은 서비스는 영속성 계층에 많은 의존성을 갖게 되고 작업해야 할 유즈 케이스를 책임지는 서비스를 찾는 것도 어려워집니다.
비대해진 도메인 레이어
도메인 로직이 여기저기 퍼져 있어서 어디 코드를 수정할 지 파악하기 어렵기 때문에 일단 프로젝트 작업 시간이 길어지고, 얼마나 많은 리소스를 배정해야 일정에 맞춰 개발 할 수 있을지 예측하는데 어려워집니다. 두명이 추가된다고 작업 속도가 두배로 늘지 않거든요. 기존 프로젝트를 이해하고 변경하기 어렵기 때문입니다.
게다가 계층형 아키텍처를 개발할 때 각 레이어를 한 사람씩 맡아서 동시에 작업할 수 없습니다. 상위 계층을 작업하기 위해서는 하위 계층의 작업이 선행되어야 합니다.
따라서 특정 기능을 동시 작업하는 것은 매우 어려운 일
입니다.
오늘은 계층형 설계의 문제점에 대해 이야기해보았습니다.
앞으로 아키텍처에 더욱 많은 관심을 가지고 프론트엔드 개발자의 관점에서 쉽게 글을 풀어나가려고 노력하겠습니다.
감사합니다.
오 굉장히 재미있는 주제네요 .. 감사히 잘 읽었습니다 !