본문 바로가기
프론트엔드/React

리액트 React | 프로젝트 구조 설계 패턴

by YUNI Heo 2023. 7. 4.
반응형

 

 

✅ 리액트 설계를 하는 원칙

우리는 확장성있고 재사용성 있는 코드를 만들어야 한다
관심사에 따라서 코드를 분리하고 단일 책임을 가지는 컴포넌트를 만들어야 한다
외부에 제어를 위임시키는 것을 고려 해야한다

 

깨끗하고 성능 좋은, 유지 보수가 쉬운 리액트 컴포넌트 만드는 법 배워보기
모든 프로그래머의, 적어도 나에게는 꿈같은 일이다. 나한테는 훌륭한 프로그래머와 좋은 프로그래머를 가르는 자질이기도 하다. 언제나 배워야 하고, 개선해야 할 점이 있기 때문에, 절대로 완벽해질 수 없다는 사실이 재미있는 지점이다.

아래 모범 사례를 따라가다 보면 팀원뿐만 아니라 자기 자신도 도움받을 수 있을 것이다. 스타일 가이드를 통해 어떻게 코드를 짜는지에 대한 중요한 내용을 정의해 둔 개발팀을 본 적이 있다. 어떻게 생각하느냐고 묻는다면 훌륭한 아이디어라고 말할 것이다.

그중 일부는 아래와 같았다.

함수형 컴포넌트 사용하기 (arrow-function 같은)
인라인 스타일을 사용하지 않기
적절한 import 구조를 유지하기 (서드파티 import를 먼저 --> 내부에서 쓰는 import를 마지막으로)
커밋(commit) 전에 코드 형식 맞추기

 

💡 컴포넌트

  우리는 이제 리액트가 제공하는 자유로움 아래서 리액트 프로젝트를 잘 설계하는 원칙을 세워야 한다. 리액트를 잘 설계하기 위해서는 우선 컴포넌트에 대한 이해가 필요하다.

컴포넌트는 컴퓨터 소프트웨어에 있어서, 다시 사용할 수 있는 범용성을 위해 개발된 소프트웨어 구성 요소를 일컫는다. - wikipedia

  리액트는 무수한 컴포넌트로 이뤄져 있다. 컴포넌트를 잘 설계하는 것이 리액트 프로젝트를 잘 설계하는 부분에 큰 비중을 차지한다. 사전적 정의에 따르면 컴포넌트는 재사용성과 범용성을 위해서 만들어져야 한다. 위에서 말한 의존성이 짙고 목적성이 사라진 코드의 문제는 컴포넌트의 특징을 무시한 채 재사용성과 범용성이 부족한 채 작업 되었기 때문이라고 생각한다. 우리는 작업 일정이나 귀찮음, 혹은 능력부족으로 인해 그때 그때 일이 생길때마다 코드의 line만 보고 여기 들어가면 무슨 동작이 되겠네 하고 작업을 하기 일쑤다. 그렇게 되면 컴포넌트는 재사용성과 범용성이 떨어진채로 여러 기능이 섞인 처치 불가 코드가 된다. 처치 불가 코드를 어떻게 살릴 수 있을지 또 어떻게 지양 할수 있을지 생각해보자

 

💡 관심사를 분리하고 단일책임으로 설계하기

  컴포넌트가 재사용성과 범용성을 가지기 위해서는 관심사에 따라 딱 한가지의 역할만 수행시키기를 권장한다. (이를 객체지향에서는 단일 책임 원칙이라고 한다.) 리액트 컴포넌트는 단순하게 보면 props를 받아서 DOM 렌더를 시키는 JSX를 리턴하는 함수이다. 프로그래밍에 순수함수라는 개념이 있다.

컴퓨터 프로그래밍에서 순수함수는 다음을 따른다

동일한 인자에는 항상 같은 값을 리턴한다.
사이드 이펙트를 내지 않는다
  리액트 컴포넌트를 딱 한가지 역할만 수행시키기 위해 설계 하기 위해서는 순수함수의 특성과 같이 동일한 props를 받으면 같은 JSX 리턴하는 순수함수로 이뤄져야 한다.

  예를들어 이미지를 받아서 유저 프로필 이미지를 그리는 컴포넌트가 있다고 하자. 이미지를 받아서 그릴 뿐 만 아니라 이미지가 없을때 랜덤 이미지까지 생성하는 컴포넌트라면 단일 책임 원칙을 깬다. 랜덤 이미지 생성은 컴포넌트의 본래 역할과 다른 비즈니스 로직이므로 해당 컴포넌트와 분리되어 개발 되어야 한다. hooks를 이용하면 좀 더 편하게 비즈니스 로직을 외부로 분리 시킬 수 있다.

  잘 격리하고 역할을 잘 정의해서 컴포넌트를 만들면 코드가 너무 광범위한 역할을 수행하고 복잡해지는 걸 방지해 준다. 단일 책임을 가지는 순수 컴포넌트들은 테스트하기 좋고 가독성도 좋다.

 

💡 제어 위임

  제어를 외부에 위임 할수록 컴포넌트의 유연성과 재사용성이 높아진다. 흔히 쓰는 부트스트랩이나 antd를 생각해보자. 필요한 props만 받아서 설정대로 움직이고 제어까지 setState를 받아서 해당 컴포넌트 제어가 가능하다. 핵심 로직은 해당 컴포넌트를 import한 컴포넌트에 의해서 제어가 된다. 마찬가지로 우리가 만드는 컴포넌트도 제어를 위임에 맡길수록 비지니스 로직을 맡는 컴포넌트에 import 해서 재사용이 가능해진다. 재사용이 가능한 부분들은 따로 다 빼서 컴포넌트로 만들거나 hooks로직에 담으면 더 좋은 컴포넌트를 만들 수 있다. 반면, 제어를 위임하면 할수록 위임한 코드를 사용 하는 코드에 대한 이해 난이도가 높아 가고, 가독성이 떨어진다는 문제도 있다. 위임과 사용 용이성의 중심을 잘 잡는게 중요하다.

  이어서는 좀 더 구체적으로 리액트 프로젝트 구조 설계 패턴과 컴포넌트 설계 패턴의 예를 소개하겠다. 더 자세하게 알고 싶다면 참고의 링크에서 보면 된다.

 

✅ 프로젝트 구조 설계 패턴

  프로젝트 구조 설계에 대한 고민은 리액트 프로젝트의 시발점으로 어플리케이션 전반의 주춧돌이다. 다양한 관심사에 따라 나눌 수 있다. 리액트 공식문서는 옳은 방법은 없으며 크게 고민하지 말라고 한다. 자주 함께 변경 되는 파일 끼리 묶는걸 추천한다. 여러가지 패턴을 소개하겠다.

리액트 공식 문서의 권고와 같이 정해진 프로젝트 구조 설계는 없으며 위의 예제를 참고해서 원칙에 따라 설계해보면 좋겠다.

 

💡 파일의 기능이나 라우트에 따라서 분류하기

기능이나 경로로 폴더를 나누고 필요한 Js, Css, Test코드 모은다

common /
    Avatar.js;
    Avatar.css;
    APIUtils.js;
    APIUtils.test.js;
feed /
    index.js;
    Feed.js;
    Feed.css;
    FeedStory.js;
    FeedStory.test.js;
    FeedAPI.js;
profile /
    index.js;
    Profile.js;
    ProfileHeader.js;
    ProfileHeader.css;
    ProfileAPI.js;

 

💡 파일 유형에 따라 분리

더 나아가면 아토믹 디자인

api /
    APIUtils.js;
    APIUtils.test.js;
    ProfileAPI.js;
    UserAPI.js;
components /
    Avatar.js;
    Avatar.css;
    Feed.js;
    Feed.css;
    FeedStory.js;
    FeedStory.test.js;
    Profile.js;
    ProfileHeader.js;
    ProfileHeader.css;

 

💡 아토믹 디자인

페이지를 나눌 수 없을때 까지 쪼개서 재사용성을 극대화 한다

내용물( 비즈니스 로직 )과 껍데기( UI 컴포넌트 )를 분리하고 상태주입을 위한 컴포넌트를 이용해서 주입한다.

https://www.stevy.dev/react-design-guide/

 

💡 도메인 의존성, 기능, 재사용성, 비즈니스 로직 모두 고려해서 나눈 예

-src /
  ---domain / // 특정 도메인에 의존적인 컴포넌트
  -----User /
  -------Profile /
  -------Avatar /
  -----Message /
  -------MessageItem /
  -------MessageList /
  -----Payment /
  -------PaymentForm /
  -------PaymentWizard /
  -------services /
  ---------Currency /
  -----------index.js;
-----------test.js;
-----Error /
  -------ErrorMessage /
  -------ErrorBoundary /
  -------utils / // Error에서만 쓰이는 유틸은 따로 둘 수도 있다
  ---------ErrorTracking /
  -----------index.js;
-----------test.js;
---components / // 재사용이 가능한 컴포넌트들
  -----App /
  -----List /
  -----Input /
  -----Button /
  -----Checkbox /
  ---hooks /
  ---context /
  ---utils /
  -----Format /
  -------Date /
  ---------index.js;
---------test.js;

 

💡 관심사에 따른 역할에 따라 나눈 예

|── src
│   ├── application // 상태관리, 유틸, 상수
│   │   ├── common
│   │   ├── filters
│   │   ├── logger
│   │   ├── models
│   │   ├── persist
│   │   ├── plugins
│   │   ├── store
│   ├── infrastructure // axios, api handeler, 공통 컴포넌트
│   │   ├── api(services)
│   │   ├── components (common components)
│   ├── presentation // 비즈니스 로직 컴포넌트
│   │   ├── container
│   │   ├── component
├── index.js
반응형