같은 컴포넌트?
이 사진들은 프로젝트를 진행하면서 사용하게 된 앱바입니다. 저는 이전에 프로젝트를 진행할 때 앱바를 제작할 일이 있으면 아래 예시 코드처럼 하나의 컴포넌트에 모든 역할을 부여하는 방식으로 제작했습니다.
const Appbar = (isLogoShow, isHomeShow) => {
return (
<div>
{isLogoShow && <Logo /> }
{isHomeShow && <Home /> }
</div>
)
}
이런 방식으로 제작하는 가장 큰 이유는 앱바가 거의 모든 페이지에서 보여주는 컴포넌트이기 때문에 최상위 레이아웃에 앱바를 포함시켜 모든 페이지를 한 번에 처리하기 위해서였던 거 같습니다.
하지만 이 방식으로 구현했을 경우 문제점이 너무 많았는데, 일단 이슈를 처리하기가 너무 힘들었습니다. 하나의 컴포넌트에서 여러 개의 경로, 하위 요소들을 가지고 있기 때문에 오류가 발생했을 때 무슨 요소를 고쳐야 할지 찾는 거부터가 힘들었습니다. 또 다른 이유는 추후 유지보수와 확장성이 어렵다는 것입니다. 초기에 기획한 대로만 컴포넌트를 구성해야 한다면 이 방식도 나쁘지 않은 방식이라고 생각합니다. 하지만 변경이 발생하거나 확장을 해야 하는 경우라면 상당히 난감해질 수 있습니다;; 하나의 컴포넌트에서 모든 경로를 처리한다고 하면 아래 코드처럼 경로를 지정하게 될 가능성이 높은데, 보기만 해도 읽기가 어렵다고 느껴집니다...;; 페이지가 늘어난다면 또 한 줄의 코드를 추가해야 합니다;;
너무 많은 분기가 생기게 되고 앱바를 사용하는 페이지가 많아지면 많아질수록 코드는 점점 읽기 어려워질 가능성이 높습니다.
location.pathname.includes('hoem') ||
location.search.includes('search') ||
location.pathname.includes('chat') ||
location.pathname.includes('profile')
새롭게 결정한 방식
이번에 새롭게 진행하는 프로젝트에서 공통 컴포넌트 설계를 맡아 진행하게 되었고, 기존에 구현하던 방식으로 앱바를 구현하는 것이 아닌 새로운 방식을 적용하여 구현하기로 결심했습니다.
구현하면서 세웠던 규칙들은 다음과 같습니다
1. 범용성이 높은 컴포넌트를 만들지 말자
첫 번째로 이전에 사용했던 방식의 가장 큰 문제점인 하나의 컴포넌트가 다양한 역할을 해서 코드 가독성이 점점 떨어지는 걸 개선하고자 하였습니다. 그렇기에 앱바를 제작하며 제일 먼저 한 것이 컴포넌트의 역할을 제한하는 것이었습니다. 위에 코드처럼 수많은 분기(if문과 같은)를 제거해 주는 작업을 해주었습니다.
제가 선택한 방식은 ReactNode를 이용하는 것이었는데, 일단 디자인에서 사용되는 공통 요소를 찾아 주었습니다. 이번 프로젝트에서 사용되는 공통 요소는 { leftContent, text, rightContent } 이렇게 3가지 입니다. 이 3가지 만을 이용하여 컴포넌트를 구성해 주었고, 다른 요소는 전부 제거하였습니다.
interface AppBarProps {
leftContent?: ReactNode;
text?: string;
rightContent?: ReactNode;
}
const AppBar = ({ leftContent, text, rightContent }: AppBarProps) => {
return (
<Container>
<Left>
<div>{leftContent}</div>
<span>{text}</span>
</Left>
<Right>
<Icon>{rightContent}</Icon>
</Right>
</Container>
);
};
2. 의존성을 최소화 하자
두 번째로 고려한 점은 의존성을 최소화하는 것입니다. 첫 번째 이유랑 이어지는 부분인데, { leftContent, text, rightContent }로 요소를 구성하였기에 Appbar 내부에서는 컨트롤할 수 있는 부분이 사라졌습니다. 이 방식으로 구현했던 이유는 앞서 말했듯이 역할을 제한하기 위해서이고, 이 방식을 사용하게 될 경우 자연스럽게 의존성을 하위 컴포넌트에 맡기게 되고, 앱바에서 하위 컴포넌트를 컨트롤하는 것이 아닌 하위 컴포넌트에서 앱바에 의존성을 주입하는 형태로 구성되게 됩니다. 이렇게 구성했을 경우 앱바에서 모든 기능을 구성했을 경우보다 오류가 발생할 확률이 줄어들 거라고 생각했습니다.
이런 식으로 하위 컴포넌트에서 의존성을 주입할 수 있습니다.
const Layout = ({ children }: { children: React.ReactNode }) => {
return (
<Container>
<AppBar leftContent={<Logo />} />
<Wrapper>{children}</Wrapper>
</Container>
);
};
하지만 이 방식으로 구현했을 경우 하위 컴포넌트에서 ReactNode를 넘겨주면서 하위 컴포넌트에서 임의대로 스타일이나 그 요소를 결정하게 될 경우 앱바의 일관성이 떨어질 수 있다 생각하였습니다. 그렇기에 추가적으로 컴포넌트를 제작해 주어 Appbar에 해당 컴포넌트를 추가해 주는 방식으로 일관성 있는 화면을 보여줄 수 있도록 하였습니다.
하위 컴포넌트에서 아래 코드처럼 원하는 컴포넌트를 이용해 Appbar를 구성하는 방식입니다. 이 방식을 사용했을 경우 추후 컴포넌트를 확장하거나 유지보수할 때 컴포넌트를 Appbar에 추가해주기만 하면 되기에 좋은 방식이라 생각했습니다.
interface AppBarProps {
leftContent?: ReactNode;
text?: string;
rightContent?: ReactNode;
}
const AppBar = ({ leftContent, text, rightContent }: AppBarProps) => {
return (
<Container>
<Left>
<div>{leftContent}</div>
<span>{text}</span>
</Left>
<Right>
<Icon>{rightContent}</Icon>
</Right>
</Container>
);
};
export default AppBar;
AppBar.Logo = Logo;
AppBar.ArrowLeft = ArrowLeft;
AppBar.Heart = Heart;
AppBar.GoHome = GoHome;
AppBar.CompleteButton = CompleteButton;
const Layout = ({ children }: { children: React.ReactNode }) => {
return (
<Container>
<AppBar leftContent={<AppBar.Logo />} />
<Wrapper>{children}</Wrapper>
</Container>
);
};
결론
이렇게 새로운 앱바를 제작해 보았습니다. 앱바를 제작하면서 다른 분들이 사용한 방식을 많이 참고하였고, 그 과정에서 컴포넌트 설계에 대해 다시 고민해볼 수 있는 시간이었습니다.
참고한 문서
'React' 카테고리의 다른 글
[Eslint] Eslint flat config 적용기 (0) | 2025.01.12 |
---|---|
[React] 라이브러리 없이 캘린더 컴포넌트 구현하기 (0) | 2025.01.11 |
[React] Kakao Map Api 사용하기 (5) - debounce를 활용한 Api 호출 최적화하기 (1) | 2024.09.15 |
[React] Kakao Map API 사용하기 (4) - customoverlay 클릭 이벤트 등록하기 (1) | 2024.08.30 |
[React] Kakao Map API 사용하기 (3) - customoverlay를 활용하여 마커 커스텀 하기 (1) | 2024.08.29 |