React,  Next,  Development,  Article
Atomic Design 기반의 React 아키텍처
리팩토링을 통해 얻은 교훈
2023. 5. 2.2023. 5. 3.

리팩토링의 다짐

2023년을 시작하면서 세운 개발의 큰 방향은 리팩토링과 아키텍쳐 개선이었습니다. 기존의 서비스들은 1차적으로 서비스에 성공하였고, 그 다음을 개발할 때까지 시간도 생겼기 때문이죠. 하지만 가장 큰 이유는 개발 효율의 저하였기에 리팩토링을 무조건 하겠다고 다짐했습니다.

그러던 중 Naver DEVIEW 2023에 참가하였고, 발표중 아토믹 디자인 패턴에 관한 이야기를 듣게 되었습니다. 여러 발표자 분들이 중복적으로 말씀하셨기도 하고, 간단 명료한 구조가 제 마음에 쏙 들었기 때문에 해당 패턴을 바탕으로 아키텍처를 개선하기로 결정했습니다.

image.png 원자론을 주장한 존 돌턴(1766). 본 게시글과 아무런 연관이 없다.

아토믹 디자인 패턴

image.png

아토믹 디자인 패턴은, 모듈을 원자부터 페이지 단위까지 구분하여서 조립하는 식으로 개발을 진행하는 간단한 디자인 패턴을 이야기합니다.

아토믹 디자인 패턴은 다음과 같은 요소로 이루어집니다.

  1. Atom: 더이상 분해할 수 없는 기본 컴포넌트 입니다.
  2. Molecule: atom을 결합하여 만듭니다. 고유한 특성을 가지며, 한 가지의 일만 해야합니다.
  3. Organism: atom, molecule, organism을 결합하여 만듭니다. 명확한 영역과 특정 컨텍스트를 가집니다.
  4. Template: organism, molecule로 구성할 수 있습니다. 레이아웃에 배치하고 구조를 잡는 와이어 프레임입니다.
  5. Page: 실제 콘텐츠를 담고 있는 페이지입니다. Template이 클래스라면 Page는 인스턴스로 생각할 수 있습니다.

명쾌하네요! 그럼 이렇게만 만들면 재사용성도 높고, SRP(단일 책임 원칙, Single Responsibility Principle)도 지키는 좋은 아키텍처가 나오겠죠?

하지만 여러 개발자 분들의 이야기를 들어보면 공통적으로 “생각보다 어렵다”라고 말하십니다. 애초에 아토믹 디자인 패턴은 디자이너를 위해 만든 시스템이기도 하고, 사람마다 각 레벨을 나누는 기준이 주관적이기 때문에 기준을 정하는게 쉽지 않았다고 합니다. 따라서 저는 진행중인 프로젝트들에 맞게 구조를 변경시켰습니다.

변형 아토믹 디자인 패턴

아키텍처를 결정하기 전에, 기존의 문제점을 한 번 살펴보죠.

기존의 문제

1. 폴더 구조

현재 아키텍처는 page-component의 아주 간단한 구조입니다. 하지만 어떤 컴포넌트는 page를 기준으로, 어떤 컴포넌트는 기능을 기준으로 분류되었습니다.

처음에는 컴포넌트에 파일 개수가 많지 않았기 때문에 문제가 되지 않았습니다. 하지만 시간이 흐르고, 코드가 쌓이니 파일들 간의 관계를 기억하기 힘들어졌고, 코드를 찾는 건 물론이며, 디버깅 또한 어려워졌습니다.

image(1).png

image(2).png

좌측은 Consult와 “가장 많이 관련된 컴포넌트” 들이, 우측은 mentor와 “가장 관련된 컴포넌트”들이 모여있습니다. mentor 페이지의 consult 컴포넌트, consult 페이지의 mentor 컴포넌트의 위치는 그때그때 달라지겠죠.

2. 비대한 컴포넌트들

컴포넌트를 페이지 바로 밑 계층의 단위로 보았기 때문에, 기본적으로 컴포넌트들이 매우 거대합니다. 또한 이런 맥락에서 컴포넌트를 위한 컴포넌트를 만들게 되면, “어차피 여기서만 사용할건데” 라는 생각으로 특정 컨텍스트에 종속되는 경우가 많았습니다. 결국 재사용 가능한 컴포넌트는 존재하지 않게 되며, 각각의 컴포넌트들이 분리 기능에만 그치게 되었습니다.

3. TypeScript의 부재

말해서 뭐할까요. props로 넘어온 데이터를 검증하는 코드들이 정말 차곡차곡 쌓여서 정작 보고싶은 로직을 가려서, 가독성이 떨어졌습니다.

변형된 아토믹 디자인 패턴: 컨텍스트의 관점

위와 같은 문제가 발생한 근본적인 이유를 “컨텍스트의 미분리” 로 보았습니다. 컨텍스트가 페이지에서 모든 컴포넌트까지 이어졌기 때문에 결합도가 매우 높아지고, 재사용이 불가능해 집니다. 또한 어떤 컴포넌트는 페이지를, 어떤 컴포넌트는 기능을 기준으로 컨텍스트를 설정하였기 때문에, 분리되어야 할 컴포넌트들도 충돌이 일어나고, 이를 분리하기 위한 주관적인 판단이 강하게 필요합니다.

따라서 페이지와 컴포넌트가 많은 프로젝트들이 아니기 때문에, 컨텍스트를 기준으로 아토믹 디자인을 적용하였고, 다음과 같은 아키텍쳐를 만들었습니다.

  1. component: 기능에 종속적인 컴포넌트들입니다. 기존 아토믹 모델의 atom과 molecule을 합친 형태와 유사합니다. 컨텍스트를 가지지 않으며, 동일한 입력에 동일한 출력을 반환하는 정적 컴포넌트입니다. MUI의 컴포넌트들과 유사하게 사용할 수 있습니다. 반복적인 사용이 가능해야하며, props를 통해 수정이 가능해야합니다.(ex: Custom design을 적용한 input)
  2. organism: 컨텍스트에 종속적인 컴포넌트들입니다. 기존 아토믹 모델의 organism과 거의 동일합니다. component와 organism을 사용할 수 있습니다. 컨텍스트를 지니고 있지만, 해당 컨텍스트(도메인)에서 재사용이 가능합니다. (ex: 회원가입의 단계를 나타내는 헤더)
  3. page: 각 url경로와 동일하게 (Next.js와 동일한 규칙) 폴더 구조로 존재하는 파일들입니다. 대부분 data와 state를 이곳에서 관리합니다. 해당 페이지에서만 사용되는 재사용이 불가능한 요소들은 page 파일 내부에서 선언될 수 있습니다.
  4. service: 반복적으로 사용되는 로직을 배치합니다. 페이지와 동일한 폴더 구조를 가지되, 여러 파일에서 사용한다면, 해당 파일들의 상단 경로에 위치시킵니다. 도메인이 존재하지 않는다면 common 폴더 하위에 기능별로 분류합니다. (ex: /service/common/date/getKoreanDate.ts)
  5. apis: api의 url을 바탕으로 1-depth로 분리해서 배치합니다. 한 파일에 여러 api가 있을 수 있습니다.
  6. interface: 사용되는 모든 Interface는 해당 폴더에서 관리합니다. 도메인별로 1-depth로 분리해서 관리합니다. 한 파일에 여러 interface가 있을 수 있습니다.

image.png 변경된 폴더 구조

image.png Button이라는 기능에만 충실한 component들

그래서?

위와 같은 구조를 설정하고 리팩토링을 진행해보니 다음과 같은 이점을 느낄 수 있었습니다.

component가 더 이상 컨텍스트에 종속되지 않기 때문에, component에 있는 요소들은 걱정없이 “사용”할 수 있습니다. component에 파일이 작성된다는 것은 코드 전체의 재사용성이 늘어난다는 것과 동일하게 여길 수 있습니다.

그리고 organism은 컨텍스트를 포함해도 되며, component와 분리됨으로써, 재사용성의 수준이 낮기 때문에, 생성에 대한 부담감이 많이 줄어들었습니다. 그럼에도 불구하고, 페이지와 동일한 폴더 구조로 배치되기 때문에, 원하는 코드를 쉽게 찾아갈 수 있습니다.

page들 또한 url과 동일한 경로를 가지기 때문에 더 이상 router를 확인하지 않아도 됩니다. 그리고 재사용이 불가능한 요소들은 page 파일 내부에 배치할 수 있게 함으로써 가독성을 더 높이면서, 재사용 가능한 파일들과 분리하였기 때문에 다른 폴더 구조를 어지럽히지 않게 됐습니다.

그리고 각각의 요소들은 interface를 사용하며 props를 전달하기 때문에, 더 이상 전달 요소의 검증 과정이 대폭 줄어들었습니다.

무엇보다 기분이 좋습니다

정리가 되지 않았을 때는 기능을 개발하더라도, 단발적인 기능으로만 사용되었기 때문에 추가적인 개발 자체를 회피하는 경향이 있었습니다. 하지만 재사용성이 높은 코드를 작성하고 사용해보니, 오랜만에 개발의 기쁨을 느낀것 같네요.

image(5).png

결론과 후기

중요한건 “재사용성”이었고, 이를 구현하기 위해서는 “컨텍스트(맥락)”을 잘 파악하는게 중요하다는 걸 느꼈습니다. (컨텍스트는 코에걸면 코걸이 귀에걸면 귀걸이 느낌의 용어이긴 합니다만…😅)

그리고 리팩토링을 해보며 확실히 느꼈습니다. 만약 과거의 저는 현재와 같은 구조를 주었더라도, 기존의 코드와 크게 다르지 않는 코딩을 했을 것 같다는걸요.

왜냐하면 이 구조를 구현하기 위해서는

  1. MUI의 컴포넌트를 충분히 커스텀 할 수 있는 능력
  2. type과 interface를 알맞게 생성, 변형하며, Library의 type들도 사용할 수 있는 능력
  3. 컴포넌트의 필수적인 요소만을 통한 추상화 능력

이 3가지 능력이 필요한데, 저는 이번 리팩토링을 통해서 배우게 됐기 때문이죠.

그래서 기존의 코드와 구조가 너무 별로지만, 저를 크게 자책하지는 않으려고 합니다. 이런 과정을 거쳤기에 리팩토링을 하고, 알맞은 능력을 배웠다고 생각합니다.

이번에 얻게된 경험들은 앞으로의 개발 속도가 N배는 올릴 수 있음을 느꼈습니다. 앞으로 조금 더 노력해서, 지금 정도의 프로젝트 규모는, "1주일이면 충분하지~"라며 뚝딱 만들어내는 개발자라는 자그마한 목표가 생겼습니다.

image(6).png

Reference

카카오 FE 기술블로그-아토믹 디자인을 활용한 디자인 시스템 도입기