[React] 디자인패턴 (Presentational-Container, VAC패턴)

Mayton·2023년 1월 30일
15

React

목록 보기
10/10
post-custom-banner

🙋🏻‍♂️ Presentational and Container Pattern

리액트를 사용하고 redux, recoil과 같은 상태관리 라이브러리를 같이 사용하다보면 필연적으로 마주하는 디자인 패턴이 이 Presentational & Container 디자인 패턴이다. custom hook이 나오면서 이 디자인 패턴의 container가 custom hook으로 대체될 수 있어 이전과 같은 P&C Pattern이 사용되지는 않는다. 하지만 명확한 것은 데이터의 처리와 view를 분리시키려는 노력의 산물이 이 디자인 패턴이라는 것이다.

👉🏻 Presentational and Container Pattern 이란?

  • 로직을 위한 컴포넌트(데이터를 위한), UI를 위한 View 컴포넌트로 분리된 패턴입니다. 이에 따라 앱의 기능과 ui에 대한 구분이 쉬워집니다.
  • container 내에 데이터를 props를 통해 각기 다른 컴포넌트에 전달할 수 있습니다.
  • view 로직의 분리를 통해 컴포넌트의 재사용성을 극대화 시킵니다.
  • 동일한 마크업, 컨테이너 레이아웃 (header, footer)는 반복해서 작성하지 않아도 this.props.children 구현 할 수 있습니다.

👉🏻 Presentational component

  • UI와 관련된 component
  • state를 직접 조작하지 않으며 container component에서 받은 props를 통해 로직을 구현합니다.
  • 그에 따라 useState, useCallback, dispatch등 state관련된 훅이 하나도 없습니다.
  • 상태를 거의 가지지 않으며, 상태를 가진다면 데이터에 관한것이 아닌 ui 상태에 관한 것입니다.
    종종, this.props.children을 통해 컴포넌트가 렌더됩니다.

👉🏻 Container component

  • 어떻게 동작하는지, 어떤 로직을 수행하는지에 관련있습니다.
  • markup을 사용하지 않습니다. 스타일을 사용하지 않습니다.
  • 데이터와 데이터 조작에 관한 함수를 만들고 present component에 제공합니다.

gist의 예제

// CommentListContainer.js
import React from "react";
import CommentList from "./CommentList";

class CommentListContainer extends React.Component {
  constructor() {
    super();
    this.state = { comments: [] };
  }

  componentDidMount() {
    fetch("/my-comments.json")
      .then(res => res.json())
      .then(comments => this.setState({ comments }));
  }

  render() {
    return <CommentList comments={this.state.comments} />;
  }
}

데이터를 fetch 한뒤 presentataional component에 props로 넘겨준다.

// CommentListPresenter.js
import React from "react";

const Commentlist = comments => (
  <ul>
    {comments.map(({ body, author }) => (
      <li>
        {body}-{author}
      </li>
    ))}
  </ul>
);

container component에서 데이터를 받아 UI를 구성한다.

단 최근에는 container componentcustom hook으로의 대체와 명확하게 containerpresentational component의 구분에 대한 불필요성이 대두되어 재사용성과 view와 로직의 구분이라는 대명제만 지킨 상태로 component를 구성합니다.

🙋🏻‍♂️ VAC 패턴

FE 개발 특히 View단을 개발하는 것은 크게 세가지로 구분할 수 있습니다.
1. 사용자의 상호작용 처리 UI 기능개발(JS)
2. 데이터나 상호작용 결과를 시각화하는 렌더링 처리(마크업, CSS)
3. 비즈니스 로직, UI 기능, 렌더링 처리의 통합 (React, Redux)
회사의 규모가 커질 수록 1,3을 하는 개발영역과 2를 하는 UI개발 영역으로 나누어 협업을 하는 경우가 종종있습니다. 이 때 충돌을 방지하기 위해 Presentational and Container Pattern보다 더 구체적인 기준을 제시한 디자인 패턴으로 VAC 패턴이 있습니다.

👉🏻 Presentational and Container VS VAC Pattern

로직과 UI의 분리는 분명 위의 Presentational and Container 패턴에서 목적으로 했던 내용이었습니다. VAC 패턴도 Container 컴포넌트에 로직을 위임하는 설계 방식을 따르기에 비슷한 패턴이라고 볼 수 있다. 하지만 근본 적인 차이는 컴포넌트가 View 로직을 가질 수 있는 지 여부입니다.

👉🏻 VAC Pattern 이란?

View Asset Component의 약자로 렌더링에 필요한 JSX와 스타일을 관리하는 컴포넌트를 의미합니다.
VAC 패턴은 View 컴포넌트에서 JSX 영역을 Props Object로 추상화하고, JSX를 VAC로 분리해서 개발하는 설계 방법입니다.

VAC의 특징

  • 반복이나 조건부 노출, 스타일 제어와 같은 렌더링과 관련된 처리만을 수행합니다.
  • 오직 props를 통해서만 제어되며 스스로의 상태를 관리하거나 변경하지 않는 stateless 컴포넌트입니다.
  • 이벤트에 함수를 바인딩할 때 어떠한 추가 처리도 하지 않습니다.

구현예제

// VAC
const SpinBoxView = ({ value, onIncrease, onDecrease }) => (
  <div>
    <button onClick={onDecrease}>-</button>
    <span>{value}</span>
    <button onClick={onIncrease}>+</button>
  </div>
);
// View Component
const SpinBox = () => {
  const [value, setValue] = useState(0);

  const props = {
    value,
    onDecrease: () => setValue(value - 1),
    onIncrease: () => setValue(value + 1),
  };

  // JSX를 VAC로 교체
  return <SpinBoxView {...props} />;
};

👉🏻 잘못된 예시

View 컴포넌트의 기능이나 상테 제어에 VAC가 관여해서는 안됩니다.

// VAC
const SpinBoxView = ({ value, step, handleClick }) => (
  <div>
    <button onClick={() => handleClick(value - step)}>-</button>
    <span>{value}</span>
    <button onClick={() => handleClick(value + step)}>+</button>
  </div>
);
// View Component
const SpinBox = () => {
  const [value, setValue] = useState(0);

  const props = {
    value,
    step: 1,
    handleClick: (n) => setValue(n),
  };

  // VAC에서 value를 제어하는 행위에 관여
  return <SpinBoxView {...props} />;
};

VAC 패턴을 통해 UI 영역 담당자와 로직 담당자가 서로 수정사항이 있을 때 서로의 것에 관여하지 않고, 수정할 수 있게 됩니다.

참조
The Atomic Workflow
아토믹 디자인을 활용한 디자인 시스템 도입기
React Hooks: Compound Components
February 18th, 2019 — 5 min read

React로 사고하기
합성 컴포넌트로 재사용성 극대화하기
프론트엔드에서 MV* 아키텍쳐란 무엇인가요?
React에서 View의 렌더링 관심사 분리를 위한 VAC 패턴 소개
Presentational and Container Components
[리액트] VAC 패턴 적용 후기 및 장단점
리액트 디자인패턴 : View Asset Component (VAC 패턴)
React VAC Pattern - View 로직과 JSX의 의존성을 최소화 하자!
presentational & container 디자인 패턴

profile
개발 취준생
post-custom-banner

1개의 댓글

comment-user-thumbnail
2023년 3월 7일

안녕하세요! 혹시 질문 좀 드려도 괜찮을까요?!

답글 달기