React VAC Pattern
다음주부터 본격적으로 시작되는 FE 개발에 앞서 이전의 FE 코드를 작성하면서 느꼈던 갈증을 해소하기 위해 VAC 라는 패턴을 알게 되어 이를 바로 적용하고자 내용을 정리하려고 합니다.
이전의 회사 프로젝트를 진행해본 결과 비교적 많은 수정 요청이 랜더링 처리된 마크업 부분에서 있었습니다. 그래서 FE 로직과 마크업 처리를 분리 하는 VAC 패턴과 같은 것이 필요했었습니다.
VAC (View Asset Component) 는 JSX 영역을 Props Object 로 추상화해 JSX와 View 로직을 분리해서 개발하는 컴포넌트 입니다.
그래서 다음과 같은 특징을 가지게 됩니다.
- 반복이나 조건부 노출, 스타일 제어와 같은 랜더링과 관련된 처리만을 수행합니다.
- 오직 props 를 통해서만 제어되며 스스로의 상태를 관리하거나 변경하지 않는 stateless 컴포넌트 입니다.
- 이벤트에 함수를 바인당할 때 어떠한 추가 처리도 하지 않습니다.
구현
yarn create react-app sample
예제가 될 spin_box.js 입니다.
import { useState } from 'react';
const SpinBox = () => {
const [value, setValue] = useState(0);
return (
<div>
<button onClick={() => setValue(value - 1)}>-</button>
<span>{value}</span>
<button onClick={() => setValue(value + 1)}>+</button>
</div>
)
};
export default SpinBox;
Props Object 부분을 정의합니다.
props object 는 JSX 에 사용할 상태정보나 이벤트 핸들러가 됩니다.
spin_box.js
import { useState } from 'react';
import SpinBoxView from "./spinbox_view";
const SpinBox = () => {
const [value, setValue] = useState(0);
const props = {
value,
onDecrease: () => setValue(value - 1),
onIncrease: () => setValue(value + 1),
}
return <SpinBoxView {...props} />
};
export default SpinBox;
JSX 영역을 VAC 로 만듭니다.
spin_box_view.js
const SpinBoxView = ({ value, onIncrease, onDecrease }) => (
<div>
<button onClick={onDecrease}>-</button>
<span>{value}</span>
<button onClick={onIncrease}>+</button>
</div>
);
export default SpinBoxView;
다음은 disable 상태를 처리할 경우의 예제 입니다.
const SpinBox = () => {
const [value, setValue] = useState(0);
return (
<div>
<button disabled={value < 1} onClick={() => setValue(value - 1)}>-</button>
<span>{value}</span>
<button disabled={value > 9} onClick={() => setValue(value + 1)}>+</button>
</div>
)
};
export default SpinBox;
이 경우에는 props object 의 네이밍을 랜더링에 직관적인 형태로 사용하는게 좋습니다.
import { useState } from 'react';
import SpinBoxView from "./spinbox_view";
const SpinBox = () => {
const [value, setValue] = useState(0);
const props = {
value,
disabledDecrease: value < 1,
disabledIncrease: value > 9,
onDecrease: () => setValue(value - 1),
onIncrease: () => setValue(value + 1),
}
return <SpinBoxView {...props} />
};
export default SpinBox;
const SpinBoxView = ({ value, disabledDecrease, disabledIncrease, onIncrease, onDecrease }) => (
<div>
<button disabled={disabledDecrease} onClick={onDecrease}>-</button>
<span>{value}</span>
<button disabled={disabledIncrease} onClick={onIncrease}>+</button>
</div>
);
export default SpinBoxView;
패턴이 있긴 하지만 패턴도 상황에 따라서 쓰이는게 적합하기도 하고 아닐때도 있어 잘 선택하고 사용하면서 필요에 따라서는 패턴의 목적을 무시하지 않는 범위에서 약간의 수정이 있어도 괜찮다고 생각합니다.