리액트를 사용하고 redux, recoil과 같은 상태관리 라이브러리를 같이 사용하다보면 필연적으로 마주하는 디자인 패턴이 이 Presentational & Container 디자인 패턴이다. custom hook이 나오면서 이 디자인 패턴의 container가 custom hook으로 대체될 수 있어 이전과 같은 P&C Pattern이 사용되지는 않는다. 하지만 명확한 것은 데이터의 처리와 view를 분리시키려는 노력의 산물이 이 디자인 패턴이라는 것이다.
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 component
의custom hook
으로의 대체와 명확하게container
와presentational component
의 구분에 대한 불필요성이 대두되어 재사용성과 view와 로직의 구분이라는 대명제만 지킨 상태로 component를 구성합니다.
FE 개발 특히 View단을 개발하는 것은 크게 세가지로 구분할 수 있습니다.
1. 사용자의 상호작용 처리 UI 기능개발(JS
)
2. 데이터나 상호작용 결과를 시각화하는 렌더링 처리(마크업,CSS
)
3. 비즈니스 로직, UI 기능, 렌더링 처리의 통합 (React,Redux
)
회사의 규모가 커질 수록 1,3을 하는 개발영역과 2를 하는 UI개발 영역으로 나누어 협업을 하는 경우가 종종있습니다. 이 때 충돌을 방지하기 위해 Presentational and Container Pattern보다 더 구체적인 기준을 제시한 디자인 패턴으로 VAC 패턴이 있습니다.
로직과 UI의 분리는 분명 위의 Presentational and Container 패턴에서 목적으로 했던 내용이었습니다. VAC 패턴도 Container 컴포넌트에 로직을 위임하는 설계 방식을 따르기에 비슷한 패턴이라고 볼 수 있다. 하지만 근본 적인 차이는 컴포넌트가 View 로직을 가질 수 있는 지 여부입니다.
View Asset Component의 약자로 렌더링에 필요한 JSX와 스타일을 관리하는 컴포넌트를 의미합니다.
VAC 패턴은 View 컴포넌트에서 JSX 영역을 Props Object로 추상화하고, JSX를 VAC로 분리해서 개발하는 설계 방법입니다.
VAC의 특징
// 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 디자인 패턴
안녕하세요! 혹시 질문 좀 드려도 괜찮을까요?!