Jaeilit

리액트 컴포넌트 패턴에 관한... 본문

리액트를 배워보자

리액트 컴포넌트 패턴에 관한...

Jaeilit 2023. 8. 17. 19:44
728x90

프론트엔드 개발자로써 컴포넌트를 만들고 사용하는데 익숙합니다...

 

컴포넌트를 만드는 패턴에도 여러가지가 존재하고 만드는 것에 대해서도 알게모르게 규칙들이 존재합니다.

그리고 UI 컴포넌트에 대한 디자인 시스템이라는 개념도 등장하고 이 개념을 뒷받침 해줄 아토믹 패턴도 등장했습니다.

아토믹 디자인 패턴에 대한 장점과 단점 또한 명확하며 그 단점을 보완해줄 스토리북이라는 프레임워크도 등장했습니다.

 

이런것들을 생태계라고 표현한다면 제가 처음 리액트를 접했을 때 보다 엄청 비대해졌습니다.

 

물론 이런 개념적인 부분이 모든 프론트엔드 개발자분들에게 필수역량이 아니기에 무시하는 사람도 존재합니다.

 

단적으로 예를들어서,

페이지 단위로 파일을 작업하고 이것을 컴포넌트라고 부르며 모든 코드를 페이지 단위로 만들어버릴 수도 있습니다.

이러면 정말 간단하겠죠? 당연히 컴포넌트 트리가 존재하지 않으니 props 가 오고 갈 일이 없고 의존성 또한 볼 수 없을겁니다.

그에 따라 상태관리도 필요 없을지도 모릅니다. 또 컴포넌트를 나누는 본인 또는 팀의 기준이 없다면 관리 잘 된 코드가 아니기 때문에 유지보수와 생산성이 떨어질겁니다.

 

이렇게 작업했을 한다면 실제로 코드를 관리하는 프론트엔드 개발자가 아닌 다른 포지션의 개발자와 비개발자를 포함한 페이지만 보고 있는 "유저"는 보이는 페이지가 전부이기 때문에 성능이슈가 없다면 서론으로 말씀드린 생태계적 개념을 조금이라도 생각한 개발자와 초기 개발일 수록 속도 차이는 분명히 날 것입니다. 때로는 속도의 이유로 억울하겠지만 그들은 좋은평가를 받을수도 있겠지만 반대로 느렸기 때문에 안좋은 평가를 받을 수도 있을거라 생각합니다.

 

그럼에도 우리는 왜 더 좋은 컴포넌트를 만들기 위해 노력을 해야할까요?

컴포넌트 정의

 

리액트 공홈

mdn

웹 컴포넌트란 캡슐화하여 재사용이 가능한 커스텀 엘리먼트를 생성하고 웹 앱에서 활용 할 수 있도록 해주는 다양한 기술들의 모음

 

리액트 공홈

"스스로 상태를 관리하는 캡슐화 된 컴포넌트를 만드세요"

 

공통적으로 말하고 있는 캡슐화와 재사용에 대한 언급으로 캡슐화하여 재사용성이 가능한 컴포넌트를 만들어야한다는 생각이 저절로 듭니다. 캡슐화란 내부의 데이터와 그 데이터를 다루는 메서드를 외부로부터의 접근을 제어하고 보호하는 것을 의미합니다. 이미 리액트에서 컴포넌트는 캡슐화에 대한 역할을 충분히 하고 있습니다. 그 예로 내부 데이터인 state 에 외부에서 접근과 변경하지 못합니다.

 

컴포넌트 재사용성이란? 재사용성이 높은 컴포넌트는 생산성을 극대화 시켜주고, 중복된 코드를 줄여줍니다.

사실 재사용이란 프로그래밍에서 빠질수가 없습니다. 우리는 중복코드를 가장 싫어하기 때문이죠,

그렇기 때문에 재사용성의 포커스가 컴포넌트에 있을뿐 어디든 재사용이 가능한 코드를 만들어내야합니다.

 

 

 

가장 먼저 컴포넌트를 어떻게 나눌까하는 고민입니다.

 

컴포넌트를 나눈다는것은 관심사의 분리에 해당합니다.  관심사의 분리란 소프트 웨어 디자인의 원칙 중 하나로, 다양한 기능과 역할을 가진 요소들이 서로 독립적으로 분리되어야한다는 개념입니다. 

 

관심사의 분리와 함께 단일책임원칙에 따라 나누는것이 좋습니다. 단일책임원칙이란 하나의 책임만 가져야한다는 원칙입니다. 이것 또한 OOP의 개념 중 하나입니다. SOLID 5가지 원칙이라고도 하며 SRP 단일책임 원칙은 5개의 원칙 중 S 에 해당합니다.

 

import React, { useState } from 'react';

const InputField = ({ label, value, onChange }) => {
  const handleInputChange = (event) => {
    const newValue = event.target.value;
    onChange(newValue);
  };

  return (
    <div>
      <label>{label}: </label>
      <input type="text" value={value} onChange={handleInputChange} />
    </div>
  );
};

export default InputField;

간단한 예시로 input field 에 대해 다룰 수 있습니다. 해당 예시는 input 에 대한 책임만 지게 됩니다. 이렇게 작은 단위의 컴포넌트가 만들어진다면 우리는 유지보수와 재사용성이 높고 또 테스트 코드 작성도 간단하게 할 수 있다는 큰 장점이 생깁니다.

 

관심사의 분리와 SRP 원칙을 활용한 컴포넌트 패턴들이 있습니다.

Presentatioanl - Container 패턴은 UI 와 로직에 대한 관심사 분리를 하였고, 로직을 담당하는 Container 컴포넌트와 마크업만 담당하는 Presentation 컴포넌트로 나뉘어서 관리하는 패턴입니다. MVC 패턴 중 Control 과 View 부분으로 보일 수도 있겠네요,

// PresentationComponent.js

import React from 'react';

const PresentationComponent = ({ data }) => {
  return (
    <div>
      <h2>Presentation Component</h2>
      <ul>
        {data.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
};

export default PresentationComponent;


// ContainerComponent.js

import React, { useState } from 'react';
import PresentationComponent from './PresentationComponent';

const ContainerComponent = () => {
  const [data, setData] = useState(['Item 1', 'Item 2', 'Item 3']);

  return <PresentationComponent data={data} />;
};

export default ContainerComponent;

 

 

이외에도 여러 패턴들이 존재하는데 아래 링크에 5가지의 리액트 패턴을 소개하고 있습니다.

 

https://javascript.plainenglish.io/5-advanced-react-patterns-a6b7624267a6

 

5 Advanced React Patterns

An overview of 5 modern advanced React patterns, including integration codes, pros and cons, and concrete usage within public libraries.

javascript.plainenglish.io

 

 

패턴들을 공부해보면 어쨋거나 저쨋거나 단일책임에 따라 UI 와 로직을 나누면 되는거 아니야? 라고 생각이 들겁니다. 네 맞아요,

리액트 컴포넌트 패턴들의 공통점은 UI 와 비지니스 로직을 분리하여 재사용성과 유지보수에 용이한 컴포넌트를 만든다는 목적에 있습니다.

 

그럼 관심사의 분리에 따라 우리는 UI 만 생각하는 컴포넌트를 예를 들어보겠습니다.

위 예시에서 input field 컴포넌트를 어디에 가져다 쓰던 내가 원하는 styles 이 적용 된 input field 가 생겨야합니다.

 

UI 컴포넌트 만들기

 

UI 컴포넌트를 유연하게 만드는 방법에는 3가지 정도 떠오릅니다.

 

1. props로 style 상속하기

2. 헤드리스 컴포넌트

3. 컴파운드 컴포넌트

 

1. style 상속하기

import React from 'react';
import classnames from 'classnames';
import './styles.css'; // 스타일 파일을 임포트합니다.

const ComponentWithInheritedStyles = ({ isHighlighted }) => {
  // 클래스 이름을 조건부로 결정합니다.
  const componentClasses = classnames('base-component', {
    'highlighted': isHighlighted,
  });

  return (
    <div className={componentClasses}>
      <p>This is a component with inherited styles.</p>
    </div>
  );
};

export default ComponentWithInheritedStyles;

 

이 경우에는 부모 컴포넌트에서 정의 해놓은 className 에 따른 styles 을 하위 컴포넌트의 props 로 넘겨서 스타일을 입히는 방법입니다. 컴포넌트나 페이지마다 원하는 바로바로 스타일을 정의해서 쓸 수 있다는 점이 큰 장점입니다. 그리고 css 스타일 방식에 따라 의도치 않게 다른 파일과 중첩되거나 그로 인해 override 될 위험도 있어보입니다.

 

2. 헤드리스 컴포넌트

헤드리스 란 머리가 없다는 겁니다. 헤드리스 컴포넌트란 로직과 시각적 표현을 분리한건데요, 기능만 있고 스타일만 없다는 뜻으로 아무것도 렌더링 하지 않기 때문 머리가 없다는 표현을 사용합니다. 로직만 살아있기 때문에 우리는 어떤 스타일도 가져다 사용할 수 있습니다.

Count 라는 예제로 만들었습니다. 헤드리스 컴포넌트 내에서는 태그를 일체사용하지 않고 사용하는 컴포넌트에서 자유자재로 스타일을 입힐 수 있습니다.

 

아래는 헤드리스 UI 들의 링크입니다.

https://www.merrickchristensen.com/articles/headless-user-interface-components/

 

Merrick Christensen

Using Webflow with Netlify Configure Netlify to send particular routes to Webflow so that you can selectively serve pages that are designed and hosted on Webflow. 2 Minute Read » JSON Lisp Learn about how programming languages work as we design & implemen

www.merrickchristensen.com

https://tanstack.com/table/v8

 

TanStack Table | React Table, Solid Table, Svelte Table, Vue Table

Headless UI for building powerful tables & datagrids with TS/JS, React, Solid, Svelte and Vue

tanstack.com

3. 컴파운드 컴포넌트(합성 컴포넌트)

https://fe-developers.kakaoent.com/2022/220731-composition-component/

 

합성 컴포넌트로 재사용성 극대화하기 | 카카오엔터테인먼트 FE 기술블로그

방경민(Kai) 사용자들에게 보이는 부분을 개발한다는 데서 프론트엔드 개발자의 매력을 듬뿍 느끼고 있습니다.

fe-developers.kakaoent.com

합성 컴포넌트란 select tag 와 option tag 와 같이 두 태그가 하나의 UI 를 만들어내듯 태그들을 합성하여 만든 컴포넌트를 말합니다.

장점으로는 UI 를 유연하게 대처 할수 있다는 점과, 기존 컴포넌트에 요구사항들이 생길때 좀 더 효과적으로 처리 할수 있다는 장점도 있구요 상황에 따라 기존 컴포넌트와 다르게 합성 컴포넌트들이 props 들을 나눠 받기 때문에 가독성과 props 드릴링 또한 피할수 있습니다.

 

합성컴포넌트를 잘 사용하기 위해서는 React 의 Children 와 cloneElement 와도 친해져야합니다.

https://ko.legacy.reactjs.org/docs/react-api.html#reactchildren

 

React 최상위 API – React

A JavaScript library for building user interfaces

ko.legacy.reactjs.org

React.Children 은 부모 컴포넌트의 자식요소들을 이야기하는데요

cloneElement 는 말 그대로 복사한 JSX 입니다. 컴파운드 컴포넌트의 메인합성컴포넌트에서 공통적으로 아래에 props 를 내려줄때 사용 할 수 있습니다.

 

정리,,

 

 

728x90

'리액트를 배워보자' 카테고리의 다른 글

React Suspense + React code spliting  (0) 2023.02.21
리액트 프로젝트 설계  (0) 2023.01.01
Theme.ts  (0) 2022.09.11
SPA vs MPA  (0) 2021.09.19
리액트 TodoList 만들기 Day1 프론트 단 구성하기  (0) 2021.09.10