이번에는 children prop을 활용하여 React 프로젝트에서 공통 모달 컴포넌트를 분리하고 각각의 컴포넌트를 합성해보자.
기존에는 서로 다른 두 개의 모달 컴포넌트, LoginModal
과 SignupModal
가 각각 모달에 대한 로직 및 스타일을 내부에 정의하였다.
LoginModal
SignupModal
하지만 위의 이미지를 보면 LoginModal
과 SignupModal
은 중복되는 모달에 관련된 로직과 스타일이 많은 것을 볼 수 있다. 따라서BaseModal
로 공통된 컴포넌트를 만는뒤 BaseModal
컴포넌트를 기반으로하여, LoginModal
과 SignupModal
컴포넌트를 합성하기로 하였다.
이 과정에서 필요한 것이 바로 children
prop이다. children prop은 React에서 일반적으로 사용되는 방법으로, 공통 컴포넌트를 분리하고 재사용하기 위해 사용된다. BaseModal
과 같은 공통 컴포넌트에서 children
prop을 사용하면, 이 컴포넌트를 사용하는 곳에서 필요한 내용을 추가하거나 변경할 수 있다.
이제 프로젝트에 적용한 사례를 살펴 보자.
BaseModal
컴포넌트 생성먼저, 공통 모달 컴포넌트인 BaseModal
을 생성한다. 이 컴포넌트는 공통된 모달의 레이아웃과 기본 기능을 가지며, 다른 모달 컴포넌트들이 이를 확장하여 사용할 수 있다.
// src/components/helpers/BaseModal.jsx
import React, { useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { palette } from '../../styles/globalColor';
// Styled-components를 사용한 스타일 정의 ...
const BaseModal = ({ isVisible, onClose, title, children }) => {
const modalRef = useRef(null);
const handleModalOutsideClick = event => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
onClose();
}
};
useEffect(() => {
if (isVisible) {
document.addEventListener('mousedown', handleModalOutsideClick);
} else {
document.removeEventListener('mousedown', handleModalOutsideClick);
}
return () => {
document.removeEventListener('mousedown', handleModalOutsideClick);
};
}, [isVisible]);
if (!isVisible) {
return null;
}
return (
<ModalContainer ref={modalRef}>
<ModalTop>
<CloseBtn onClick={onClose} ... ></CloseBtn>
<h2>{title}</h2>
</ModalTop>
{children}
</ModalContainer>
);
};
BaseModal.propTypes = {
isVisible: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
children: PropTypes.node,
};
export default BaseModal;
BaseModal
의 children
prop을 사용하여 SignupModal
의 내용을 전달한다.// src/components/SignupModal/SignupModal.jsx
// import 내역들 ..
const SignupModal = () => {
const isSignupModalVisible = useSelector(state => state.isSignupModalVisible);
const dispatch = useDispatch();
const handleHideSignupModal = () => {
dispatch(hideSignupModal());
};
return (
<BaseModal
isVisible={isSignupModalVisible}
onClose={handleHideSignupModal}
title='회원가입 완료하기'
>
기존 SignupModalContent 컴포넌트 내역들 ..
</BaseModal>
);
};
export default SignupModal;
LoginModal
컴포넌트에서 BaseModal
사용하기// src/components/LoginModal/LoginModalStyle.jsx
// import 내역들 ..
const LoginModal = () => {
const isLoginModalVisible = useSelector(state => state.isLoginModalVisible);
const dispatch = useDispatch();
const handleHideLoginModal = () => {
dispatch(hideLoginModal());
};
return (
<BaseModal
isVisible={isLoginModalVisible}
onClose={handleHideLoginModal}
title='로그인 또는 회원가입'
>
기존 LoginModalContent 컴포넌트 ...
</BaseModal>
);
};
export default LoginModal;
BaseModal
이라는 공통 컴포넌트를 생성하여 모달의 레이아웃과 공통 기능을 추상화했다.children
prop을 사용하여 BaseModal
에 필요한 내용을 전달하여, 각각의 모달 컴포넌트에서 공통 레이아웃과 기능을 재사용할 수 있도록 했다.