강형욱 선생님은 강아지에게 "강조되고 반복되는 소리"가 강아지를 불안하게 만든다고 하셨다. 똑같이 개발자에게도 반복되는 코드는 불안하게 만든다. 프로젝트를 진행하던 중 나를 불안하게 만드는 코드가 생겨 어떻게 해결했는지 기록하려 한다.
{item.menues.map((menue => {
if(menue.path) {
return (
<Link key={menue.key} href={menue.path} legacyBehavior>
<div className={`nav-link ${router.pathname === menue.path && 'active'}`}>
<div>
<span className='nav_icon'>{menue.icon && menue.icon}</span>
</div>
<div>
<span>{menue.label}</span>
</div>
<div>
{menue.children && (<UpOutlined />)}
</div>
</div>
</Link>
)
} else {
return (
<div key={menue.key} className={`nav-link ${router.pathname === menue.path && 'active'}`}>
<div>
<span className='nav_icon'>{menue.icon && menue.icon}</span>
</div>
<div>
<span>{menue.label}</span>
</div>
<div>
{menue.children && (<UpOutlined />)}
</div>
</div>
)
}
}))}
위 코드를 보면 menu.path 값이 있으면 Link 컴포넌트를 감싸고 없으면 그대로 렌더하는 로직을 작성하였다. 중복되는 태그를 없애기 위해 처음으로 생각한 벙법은 아래와 같다.
const renderLinkContent = (path, icon, label, children) => {
return (
<div className={`nav-link ${router.pathname === path && 'active'}`}>
<div>
<span className='nav_icon'>{icon && icon}</span>
</div>
<div>
<span>{label}</span>
</div>
<div>
{children && (<UpOutlined />)}
</div>
</div>
)
}
return (
<div>
{props.data.map((item, index) => (
<Fragment key={index}>
<div className='nav-category'>
<span>{item.category}</span>
</div>
{item.menues.map((menue => (menue.path ? (
<Link key={menue.key} href={menue.path} legacyBehavior>
{renderLinkContent(menue.path, menue.icon, menue.label, menue.children)}
</Link>) : (renderLinkContent(menue.path, menue.icon, menue.label, menue.children))
)))}
<div>
<hr></hr>
</div>
</Fragment>
))}
</div>
)
중복되는 소스는 제거가 되었지만,,, 몇 가지 불편한 생각은 떠나지 않았다. 첫 번째 renderLinkContent 함수를 굳이 만들 바에 차라리 컴포넌트로 분리하면 되지 않을까? 그리고 분리를 하는 게 맞나? 하는 생각이 먼저 들었고 두 번째는 코드가 아름답지 않고 지저분하게 보였다. 그래서 좀 더 괜찮은 방법이 없을까 고민하고 인터넷 검색을 하던 중 괜찮은 글을 보게 되어 적용하였다.
Conditional wrapping in React 글 링크
해당 글에서는 재사용 가능하게 ConditionalWrapper 컴포넌트를 만들고 Wrapper 컴포넌트에 비교 값과 래핑 되었을 때의 컴포넌트를 props로 넘겨준다.
export default function CondinalWrapper({condition, wrapper, children}) {
return condition ? wrapper(children) : children
}
위 코드를 적용하면 아래와 같이 재사용할 수 있는 깔끔하고 아름다운 코드로 바꿀 수 있다.
{item.menues.map((menue => (
<CondinalWrapper
condition={menue.path}
wrapper={children => <Link key={menue.key} href={menue.path} legacyBehavior>{children}</Link>}
>
<div className={`nav-link ${router.pathname === menue.path && 'active'}`}>
<div>
<span className='nav_icon'>{menue.icon && menue.icon}</span>
</div>
<div>
<span>{menue.label}</span>
</div>
<div>
{menue.children && (<UpOutlined />)}
</div>
</div>
</CondinalWrapper>
)))}
기존의 문제점이던 중복된 코드를 제거하였고 어디서든 재사용 가능한 방식이라는 점에서 좋았다.