에어비앤비 클론코딩을 하면서 인원수를 선택하는 모달, 숙소타입을 선택하는 모달 등 유사한 모달창을 만들어야 했다. 코드의 재활용을 고려하는 므찐 개발자가 되고 싶었다. 그래서 하나의 모달 컴포넌트 프레임 안에 다른 내용을 담을 수 있어야 했다.
첫번째로 찾은 방법은 composition이다. 컴포넌트안에 컴포넌트를 담는 방법으로 react에서는 compostion합성의 방법이라고 한다. 프레임 컴포넌트 태그 안에 내가 전달하고자 하는 내용 태그들을 적으면, 프레임 컴포넌트 태그안에 {prㅐps.children}으로 전달된다.
이 방법은 props를 통해서 componenet를 전달함으로서 컴포넌트의 재사용을 지원한다.
const Modal = props => {
return (
<>
<div>
<span onClick={closeModal}>x</span>
<div>{props.children}</div>
</div>
</>
);
};
const Contents = props => {
return (
<>
<Modal color="blue">
<h1 className="Dialog-title">Welcome</h1>
<p className="Dialog-message">Thank you for visiting our spacecraft!</p>
</Modal>
</>
);
};
하지만 다른 방법으로 모달을 재활용 할 수 있는지가 궁금했고, hoc라는 개념을 찾았다. HOC는 Highg order component 라는 개념으로 컴포넌트를 인자로 넘겨서 컴포넌트를 리턴 받는 개념이라고 할 수 있다. 그러면서 컴포넌트에 캡슐화된 상태나 동작을 공유할 수 있다.
컴포넌트 -HOC(캡슐화)-컴포넌트
const Modal = contentComponent => {
return props => {
return (
<>
<div ref={closeid} className={isOpen ? "show" : "hide"}>
popup header <span onClick={closeModal}>x</span>
<contentComponent {...props} data={data} />
<div>btn1 btn2</div>
</div>
</>
);
};
};
뭐 일단 만들수는 있었다. 하지만 HOC를 굳이 모달을 만드는 데 사용해야 할까?
HOC는 주로 cross cutting concern을 해결하기 위해서 사용된다. 그럼 cross cutting concern은 무엇일까?
concern은 기능에 따라 분리된 시스템의 단위 즉 기능의 단위라고 할 수 있다. 그리고 cross-cutting이란 가로로 자른다는 의미로 주로 기능들이 세로로 흐를 때 가로로 다른 기능들에 영향을 미치는 것들이라고 할 수 있다. 대표적으로 로깅, 보안, 트랜젝션, 로딩 등에 사용이 된다.
결론적으로 반복적으로 사용되는 기능의 단위를 cross cutting concern이라고 하는 것으로 이해했다.
내가 만들고자 하는 모달창의 경우 반복되는 기능이기는 하지만 전체적인 기능의 반복이라고 보기는 어렵고 props로 전달되는 방법으로도 충분히 해결이 가능하다고 결론을 내렸다.
+Render props
기쁜 마음으로 컴포넌트의 조합은 composition을 이용하고 반복되는 로직의 재사용을 위해서는 hoc나 render props를 사용하면 되겠다고 생각했지만,
곧 새로운 이슈를 발견했다. react 문서의 자주묻는 질문에
Do Hooks replace render props and higher-order components? 라는 질문이 있었다. 공식문서의 답은 hooks는 대부분의 경우 renderprops 와 hoc를 대체할 수 있다고 한다. 그럼 문제점이 무엇인지도 알았고 해결책도 무엇인지 알았지만 동시에 두가지 질문이 생겼다.
일단 첫번째 질문의 답은 주변의 도움과 hooks가 왜 생겼는지에 대한 이유에서 찾았다. class component개발에서 react life cycle에 종속적이지 않으면서 중복된 코드를 분리하기 위해서 hoc로 중복되는 코드를 분리했다. 그러다보니 HOC가 반환하는 중복되는 컴포넌트 레이어들이 쌓이는 문제가 발생했다.
하지만 react hooks 가 나오면서 라이프사이클이 단순화되었거, 공통의 코드 분리가 필요한 경우에는 custom hook을 사용하면 해결 할 수 있었다. custom hooks를 사용하면 useState와 같이 사용자가 원하는 hooks를 use[Custom]의 형태로 만들어서 코드를 분리 할 수 있다. custom hooks를 이용하면 컴포넌트 중첩이 되지 않아서 hoc에서 발생했던 컴포넌트 중첩의 문제를 해결 할 수 있다.
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
function FriendStatus(props) {
const isOnline = useFriendStatus(props.friend.id);
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
와우네.. 커스텀 훅..
두번째 질문인 render props와 hoc는 이제 더이상 사용하지 않는걸까? 그건 아니다 공식문서에서는 아직 hoc에 대한 내용을 다루고 있다 hoc로만 해결할 수 있는 문제가 있기 때문인데. 가상 스크롤러를 만들 때 랜더해야하는 아이템들을 props로 전달해야하거나 공통의 로직이 component를 가지는 경우는 hoc를 사용하는 것이 더 적합하다고한다. 이 부분은 아직 예시를 만들어 본 부분이 아니어서 더 찾아봐야 할 것 같다.
이번에 모달을 만드는 방법을 중심으로 react를 공부하다보니 여러 질문들이 같이 생겨나게 되었다.
이런 궁금증들을 하나씩 풀어나가는 것도 재미있을 것 같다.