Components Composition

wgnator·2022년 8월 13일
0

최근 익히게 된 리액트 코드 리팩토링의 개념 중 하나인 Component Composition 에 대하여 정리해 본다.

일반적으로 components composition(컴포넌트 합성) 이란 컴포넌트를 작은 단위로 분리하고 재조합 하여 재사용성을 높이는 것을 말한다. 예를 들어, 한 앱 내에서 여러번 사용되는 비슷한 모양의 버튼을 하나의 재사용 가능한 컴포넌트로 만들어 필요한 곳에서 불러 쓸 수 있다:

import styled from "styled-components";
import { theme } from "../styles/theme";

export default function Button<P>(props: PropsWithChildren<P>) {
  return <Container {...props} />;
}

const Container = styled.button`
  background-color: ${theme.buttonBackgroundColor};
  color: white;
  width: 10rem;
  height: 2rem;
  border: none;
  border-radius: 6px;
  cursor: pointer;
`;

위의 경우, 단순히 하나의 styled component로 다시 보내주는 형태이기 때문에 props 전체를 그대로 전달하였지만, 만약 두개 이상의 태그 등으로 중첩되어 있다면 children 이 들어가야 할 자리에 {props.children} 을 명시해주면 된다.

이번에 이 패턴을 가지고 여러가지로 시험을 해보면서 깨달은 것은 컴포넌트 합성은 단순히 특정 UI 모습만 담은 작은 단위의 컴포넌트에만 쓸 수 있는 것이 아니라는 점이다. 부모가 관리하는 상태값과 연관된 비지니스 로직의 기능도 주고 싶을 경우, props로 상태값이나 setState를 넘겨줄 수 있지만, 반대로 자식에게 응답을 얻고 싶을때는 콜백 함수를 넘겨줌으로써 사용할 수 있다.

다음은 내가 만들었던 confirmation 모달창에 위의 버튼 컴포넌트를 적용한 사례이다. 사용자의 컨펌을 받아야 하는 모달이 여러 곳에서 존재할 경우, 안의 알림 내용은 children 으로 받고, 버튼이 눌렸을 때 확인/취소에 따라 콜백으로 받아온 함수에 true/false를 전달해준다.

import styled from "styled-components";
import Button from "../components/Button";
import { theme } from "../styles/theme";

type ConfirmationModalType = {
  callback: (isConfirmed: boolean) => void;
};
export default function ConfirmationModal({ callback, children }: ConfirmationModalType) {
  return (
    <Container>
      <h3>{children}</h3>
      <ButtonWrapper>
        <YesButton onClick={() => callback(true)}>Yes</YesButton>
        <NoButton onClick={() => callback(false)}>No</NoButton>
      </ButtonWrapper>
    </Container>
  );
}

const Container = styled.div`
  ...
`;

const ButtonWrapper = styled.div`
  ...
`;

const YesButton = styled(Button)`
  ...
`;

const NoButton = styled(Button)`
  ...
`;

부모 컴포넌트에서는 다음과 같은 방법으로 콜백을 전달한다:

		...
      {deleteRequestedID && (
        <ConfirmationModal
          callback={(isConfirmed) => {
            if (isConfirmed && deleteRequestedID) deleteSchedule(deleteRequestedID);
            setDeleteRequestedID(null);
          }}
        >
          Are you sure to delete this schedule?
        </ConfirmationModal>
      )}
      ...

이런식으로 단순한 UI 로직만 분리하는 용도 뿐 아니라 로직을 수반한 더 큰 단위의 컴포넌트도 분리하여 재사용/재조합 할 수 있다.

(참고: 리액트 공식문서 - https://reactjs.org/docs/composition-vs-inheritance.html)

profile
A journey in frontend world

0개의 댓글