이번글은  깔끔하면서 체계적인 모듈화 및 코드를 작성할 수있도록 리액트 클린 아키텍처를 작성해보겠다.

 

아키텍처의 중요성은 로버트 c 마틴의 클린 아키텍처에서 서술하기를 기능은 구조에 의지한다는 것이다. 이 점을 이해하기위해서는 코딩이라는 행위에 담겨져 있는 내재적 관점을 살펴 봐야 한다. 우리가 회사에 들어가서 하는 행위는 코딩이며 목정적성을 생각하면 다음과 같다.

 

우리는 코드를 이해하고 읽으며 수정/ 추가 유지보수 하는 일을 많이 하게된다. 따라서 이런 행위를 우리는 제일 먼저 프로젝트의 구조를 파악하고 아키텍처와도 밀접한 관계성을 갖는다. 예를 들어 지저분한 방에서 물건을 찾을 때 와 정돈된 방에서 물건을 찾는 속도는 차이가 나듯이 프로젝트의 구조에서도 정돈된 구조는 개발자의 속도에도 영향을 끼친다.

 

회사의 입장에서는 클린한 코드로 최소한의 인력으로 최대의 이익을 창출해야 하며 이는 소프트웨어 아키텍처의 목표인 필요한 시스템을 만들고 유지보수하는데 투입되는 인력을 최소화 하는것과 상응하게 된다.

 

클린 아키텍처란?

로버트 가 제안한 방법으로 관심사를 확실히 분리하여 효율적으로 재사용이 높은 컴포넌트를 구현 할 수 있게끔 만들수 있는 아키텍처 이다. 최소한의 노력으로 최대의 효율을 얻게 해주는 것이다.

 

 

클린 아키텍처의 기본 이론

  • 체계적으로 계층이 분리되어야한다.
  • 각 계층은 서로 맡은 역할을 충실히 수행한다.
  • 계층간의 의존성 관계가 잘 이루어져야한다.

핵심 개념

  • 안쪽의 원(비즈니스 로직) 바깥의 원(UI나 data source)에 대해 전혀 알지 못한다.
  • 안쪽 영역으로 갈수록 추상화와 정책의 수준이 높아지며, 반대로 갈수록 구체적인 세부사항으로 구성된다.

클린아키텍처 구조의 이해

 

이 글에서는 Next.js 프로젝트를 대상으로 위의 규칙을 준수하여 구성해 보겠다.
Next.js의 많은 예제들의 수준과 비슷하게 한다면, 아래와 같은 계층을 가진 구조의 모양이 나올 수 있다.

Core

입력과 출력으로부터 거리가 멀기 때문에 고수준이라고 볼 수 있는 계층이다.
앞서 말한 원칙에서 세부적인 도메인 로직이 아닌, 웹 애플리케이션 로직과 추상화된 코드들이 들어간다.
예를 들면 아래와 같은 성격의 코드들이 들어갈 수 있다.

  • 애플리케이션이 외부와의 통신을 위해 필요한 구현체
  • 도메인과 연관이 없는 고수준의 유틸리티
  • 재사용가능한 컴포넌트
    • ex) Button, Input, Select
  • 중요도가 높은 외부 모듈(라이브러리)의 어댑터

Core 계층에 있는 코드들은 절대 Core 원의 외부에 있는 계층(lib/components, pages)을 참조하면 안 된다.

Components / Lib

이 계층은 도메인에 종속적이며, Core 계층의 코드들을 참조할 수 있고, 아래 예시와 같은 코드들이 들어간다.
( 주문이라는 도메인이 속한 프로젝트라고 가정. )

 

- src
     - lib
         - order
             - constatns
             - hooks
             - mutations
             - queries
             - utils
             - ....
 
 - src
     - components
         - order
             - ItemList.tsx
             - Price.tsx
             - Payment.tsx
             - ...

 

  • Lib
    • 도메인에 관련되어 있는 로직들.
    • components 계층과 pages 계층에서 참조되어진다.
  • Components
    • 도메인에 관련되어 있는 컴포넌트.
    • 도메인에 국한되어 재사용성은 낮다.
    • pages 계층에서 참조되어진다.

Pages

도메인에 의존도가 높은 계층. 입력/출력과 밀접해있으며, 가장 저수준이라고 볼 수 있다.
Next.js의 기본 설정 값인 파일 시스템 기반으로 라우팅 처리를 하며, Components Lib 계층의 코드들을 참조하여 구성할 수 있다.

- src
   - pages
     - api
       - order
         - [...slug].tsx  // src/lib/order/.. 참조
         - ...
     - order
       - [id].tsx // src/components/order/.. 참조
       - _middleware.tsx // src/lib/order/.. 참조
       - ...

 

Dependency diagram

위의 구조와 규칙을 가지고 간략히 의존성 그래프로 표현하면 아래와 같은 모양이 될 것이다.

의존성은 모두 단방향으로만 흘러가고, 역으로 참조해서는 안 된다.

예를 들어, core 계층에 API 통신을 위한 구현체가 있다고 가정해 보자.
이 구현체는 UI의 형태 혹은 상태, 세부적인 도메인 로직를 알아서는 안되며, 알아도 좋을 게 없다.
마찬가지로 도메인에 종속적인 코드들 또한, 이 구현체가 비동기 통신을 위하여 어떤 객체를 사용하여 구현이 되었는지, 어떻게 API 서버와 통신을 하는지 전혀 알 필요가 없다.

이러한 관심사의 분리로 인해 각 모듈은 여러 책임에서 벗어나기 쉽고, 테스트하기도 더 쉬워지며, 유지 보수 비용도 줄어들 것이다.

 

아래는 내가 직접 응용한 구조이다. 

 

 

core -> common에 api 를 담은 폴더와 타입들을 지정해둔 각 서비스 마다 dto 폴더를 생성하여 정의하였고

useCasese -> 각 서비스 아래에 폴더이른과매칭되는 ts 파일로 어플리케이션 비즈니스 로직을 수행하는 영역으로 api 통신에 있어 중간다리하는 역할 하는 부분으로 구분을 지었다. 

controller -> 부분은 pages 폴더 추후에 추가할 custom hook 폴더를 추가할 예정이다.

 

결론

실무에서 업무를 하다보면 직관적이지 못한 프로젝트 구조들로 인해 많은 시간을 낭비하였다. 좋은코드구조는 남들이보기에 한눈에 들어오는 구조이며 누구나 이해할 수 있는 영역이라고 생각한다.그런 구조를 생각하면서 구조를 짜고 코드를 생성 한다면 개발자의 시간 등이 많이 아껴지리라고 본다.

 

 

깃허브 https://github.com/asdf132645/reactToyProject

 

+ Recent posts