front-end/FrontEnd

[FrontEnd] 좀 더 재사용성이 높은 컴포넌트 - 1

hojung 2022. 9. 4.
728x90
반응형

1. 좀 더 재사용성이 높은 컴포넌트에 관해

현재 스타트업 프로젝트에서 nextJs를 이용해 프론트엔드를 개발하고 있다. 

마크업을 진행하던 중 항상 수 많은 버튼들과 컴포넌트들을 그 때 그 때마다 새로이 만드는 것은 아주 비효율적인 방법이라고 생각했다. 

또한 항상 프로그래밍에서 함수를 만들고 클래스를 만드는 등의 작업을 하는 것은 반복적인 작업을 줄이기 위함임을 학부에서 배운 것을 통해 알고 있었다. 따라서 좀 더 재사용성이 높은 컴포넌트에 대한 고민을 하기 시작했다. 

많은 기술 블로그등을 찾아보고 팀원들끼리 의견을 나누던 중 같은 팀원 한 분이 나에게 아주 좋은 글을 추천해주셨다. 

해당 글은 카카오 엔터테인먼트 기술 블로그에 존재하던 글이었는데 다음과 같다. 

https://fe-developers.kakaoent.com/2022/220731-composition-component/

 

합성 컴포넌트로 재사용성 극대화하기

카카오엔터테인먼트 FE 기술블로그

fe-developers.kakaoent.com

위 글은 합성 컴포넌트에 대한 얘기였다. 일반적인 경우 우리는 props를 이용해서 컴포넌트에 다양성을 불어넣을 수 있지만 위 방법은 props를 계속해서 추가가 되어야하고 해당 컴포넌트를 만들 때 항상 props에 대한 명시를 해주어야한다는 불편한 점이 있었다. 또한 props로 받은 경우 만약 안에 태그들이나 컴포넌트 배치 순서가 변경된다면 대응할 수 없다는 문제점이 있었다. 

 

그래서 위의 글에서 사용하기로 한 개념이 합성 컴포넌트이다. 자바스크립트의 Object.assign함수를 통해 메인컴포넌트와 서브 컴포넌트를 묶어준 후 각기 다른 경우에 유연하게 대응할 수 있도록 만들었다. 

 

2. 무엇을 하려하는가?

나는 nextJs에서 아코디언 컴포넌트를 만들어야 했다. 생긴 모습은 다음과 같다. 

하지만 안에 들어갈 내용이 다른 경우가 많았다. 이 아코디언 컴포넌트는 여러 컴포넌트를 포함할 수 있어야했고 아무 컴포넌트가 들어가 있지 않은 경우도 신경을 써야했다. 

아무 참여자도 없는 경우

반면 위와 같이 다른 컴포넌트가 들어갈 경우에는 배치가 변했다. 맨 위의 아코디언에서는 한 줄에 하나의 컴포넌트만이 배치 되었지만 바로 위의 경우에는 한 줄의 두 개의 컴포넌트가 배치되어야했기 때문이다. 

따라서 나는 AccordianMain컴포넌트를 다음과 같이 설계했다. 

1. AccordianMain

const AccordianMain = ({ title, totalPeople, applicants, children }: Props) => {
	const [hide, setHide] = useState<string>('');
	const handleButtonClick = (event: any) => {
		event.stopPropagation();
		if (hide === '' || hide === 'open') {
			setHide('close');
		} else {
			setHide('open');
		}
	};
	const buttonText = hide === '' ? '펼침' : hide === 'open' ? '숨기기' : '펼침';
	return (
		<S.AccordianMainContainer>
			<S.AccordianHeaderContainer hide={hide}>
				<S.AccordianHeaderTitle>{title}</S.AccordianHeaderTitle>
				<S.AccordianHeaderPeopleNumber>{`${applicants}/${totalPeople}`}</S.AccordianHeaderPeopleNumber>
				<S.AccordianHeaderHideButton onClick={handleButtonClick} hide={hide}>
					{buttonText}
				</S.AccordianHeaderHideButton>
				<S.ArrowIcon width={8} height={4} hide={hide} />
			</S.AccordianHeaderContainer>

			<S.AccordianContentsWrapper hide={hide}>
				<Accordian.Contents>{children}</Accordian.Contents>
			</S.AccordianContentsWrapper>
		</S.AccordianMainContainer>
	);
};

export default AccordianMain;

hide라는 state를 지니면서 Accordian.Contents라는 곳에 children을 줌으로써 다양한 children이 들어갈 수 있게 하였다. 

 

2. AccordianContent

import { ReactNode } from 'react'
import * as S from "./styled";
type Props = {
    children: ReactNode;
}

const AccordianContents = ({ children }: Props) => {

    return (
        <S.AccordianContentsContainer>
            {
                children
            }
        </S.AccordianContentsContainer>
    );
}

export default AccordianContents;

AccordianContent의 경우 ReactNode 타입의 children을 받는다. ReactNode타입은 ReactElement타입을 포함하므로 함수형 컴포넌트뿐만 아니라 클래스형 컴포넌트 또한 들어갈 수 있게 하였다. 

 

3. Accordian

import AccordianContents from './AccordianContents';
import AccordianMain from './AccordianMain';

const Accordian = Object.assign(AccordianMain, {
    Contents: AccordianContents,
});

export default Accordian;

그 후 Accordian/index.tsx파일에서는 맨 위에서 소개했던 합성 컴포넌트를 구현한 방법과 같이 Object.assign함수를 이용해서 Contents를 넣어주었다. 추 후 버튼과 같은 컴포넌트들이 추가 되어도 button에 해당하는 컴포넌트를 만든 후 Object.assign함수에 추가해주면 된다. 

 

다음과 같이 단순히 UI의 구현을 넘어 그 뒤를 생각하는 공부가 필요한 거 같다. 프론트엔드는 아주 빠르게 변하며 변화에 빠르게 대응해야한다는 숙명을 가지고 있다. 이에 따라 대비해두는 프로그래밍 습관을 갖는 것이 아주 중요한 거 같다. 

728x90
반응형

댓글