컴포넌트 분리 기준과 필요성
🌐 컴포넌트 분리의 필요성
단일 책임 원칙(SRP) 구현
: 각 컴포넌트는 하나의 명확한 역할을 수행하여 코드의 이해도와 유지보수성을 향상시킨다.
재사용성 향상
: 공통 기능을 가진 컴포넌트를 분리하여 여러 페이지에서 재사용 가능하다.
가독성 증대
: 코드를 작고 독립적인 컴포넌트로 나누어 코드의 가독성과 이해도를 높인다.
테스트 용이성
: 컴포넌트 단위 테스트를 쉽게 수행하여 코드의 안정성을 높인다.
협업 개선
: 개발자들이 각 컴포넌트에 집중하여 개발하면서 협업의 효율성을 높인다.
사실 텍스트로만 보면 크게 와닿지 않을 수 있기 때문에 프로젝트에서 컴포넌트에서 공통적으로 쓰이는 부분을 공통 컴포넌트로 제작했던 경험을 가볍게 공유해보고자 한다.
공통 모달 만들기
디자이너 선생님들께 피그마로 전달받은 뷰에서 가져왔다.


모달 본문, 버튼 내용만 다른 모달이 5개의 뷰에서 쓰이고 있는 걸 확인할 수 있다.
이들을 공통 컴포넌트로 빼면 다른 컴포넌트에서 쓸 때 해당 모달 컴포넌트만 불러오면 되기 때문에 훨씬 간편해질 수 있다고 판단했다.
두 가지 모달에서 색이 칠해져 있는 버튼의 내용(예/아니오)이 달랐는데, 나 같은 경우 색이 칠해져있는 버튼의 내용에 초점을 두었고, 색이 칠해져 있는 버튼이 사용자가 누르도록 유도하는 버튼이라고 판단하여 컴포넌트명이 NegativeModal, PositiveModal로 컴포넌트를 나눠 공통 모달을 제작했다.
이 글을 쓰면서 생각해보니 모달 안의 버튼도 공통 컴포넌트로 나눌 수 있을 것 같다는 생각이..
공통 모달의 코드 중 NegativeModal, 즉 아니오를 유도하는 모달 컴포넌트는 아래와 같다.
우선 인자로 어떤 걸 받는지 먼저 확인해보자.
interface ModalContentPropTypes {
modalContent: string;
isModalOpen: boolean;
modalHandler: () => void;
closeModalHandler: () => void;
}modalContent: 모달의 본문 내용
isModalOpen : 모달의 현재 상태 (열림 true / 닫힘 false)
modalHandler : '예' 버튼을 눌렀을 때 실행하는 핸들러 함수
closeModalHandler : '아니오' 버튼을 눌렀을 때 모달을 닫는 핸들러 함수
내가 분리했던 모달의 경우 '아니오' 버튼을 눌렀을 때 모두 모달을 닫는 로직이었기 때문에 이렇게 구성하게 되었다.
인터페이스를 활용해서 인자들을 전달받으니 모달마다 로직과 내용이 달라도 재사용 할 수 있는 걸 확인할 수 있다.
// '아니오'를 유도하는 모달
export const NegativeModal = (props: ModalContentPropTypes) => {
const { modalContent, modalHandler, closeModalHandler, isModalOpen } = props;
/* 기타 로직들 생략 */
return (
<ModalBackgroundWrapper $isModalOpen={isModalOpen}>
<ModalWrapper ref={modalRef} $isModalOpen={isModalOpen}>
<ModalContentLayout>{modalContent}</ModalContentLayout>
<Spacing marginBottom="3.2" />
<ModalBtnLayout>
<NegativeButton onClick={modalHandler}>예</NegativeButton>
<PositiveButton onClick={closeModalHandler}>아니오</PositiveButton>
</ModalBtnLayout>
</ModalWrapper>
</ModalBackgroundWrapper>
);
};
다음과 같이 버튼의 경우 내용을 children으로 받아 구성했고, onClick을 통해 props로 받은 핸들러 함수를 넘겨주도록 구성했다.
경험한 바를 기반으로 이런 부분들에서 컴포넌트 분리의 중요성을 느낄 수 있었다.
컴포넌트를 분리한 뒤 컴포넌트가 하나의 역할만 하게 되어 코드의 가독성이 향상되었다 👍
공통 컴포넌트를 분리하면서 재사용성을 높일 수 있었다 👍
그렇다면 컴포넌트의 분리 기준은 어떤 게 있을까?
🌐 컴포넌트 분리 기준
단일 책임 원칙
각 컴포넌트는 하나의 명확한 역할을 수행해야 한다.
재사용 가능성
비슷한 기능을 하는 부분은 독립된 컴포넌트로 분리하여 재사용성을 높인다.
가독성
코드를 작고 독립적인 컴포넌트로 나누어 가독성을 높인다.
상태와 라이프사이클
컴포넌트가 관리하는 상태와 라이프사이클 메서드를 고려하여 분리한다.
UI 요소
서로 다른 UI 요소는 별도의 컴포넌트로 분리한다.
컴포넌트 분리 기준을 구체화하기 위해 응집도와 결합도라는 개념도 살펴보도록 하자.
응집도와 결합도 ?
응집도 (Cohesion)
💡 모듈에 포함된 내부 요소들이 하나의 책임/목적을 위해 연결되어 있는 연관된 정도
모듈이 하나의 목적을 위해 수행하는 요소들 간의 연관성 척도
모듈 내부의 기능적인 응집 정도
→ 높을 수록 좋음
응집도가 높을수록 필요한 데이터를 한 번에 파악할 수 있기 때문에 코드를 이해하기 쉬워진다.
응집도가 높을 수록 변경 대상과 범위가 명확해지기 때문에 코드를 수정하기 수월해진다.
이에 따라 코드의 테스트를 하기에도 쉬워진다.
결합도 (Coupling)
💡 다른 모듈과의 의존성 정도
모듈이 다른 모듈에 의존하는 정도의 척도
모듈과 모듈 간의 상호 결합 정도
→ 낮을 수록 좋음
결합도가 낮은 상황을 ‘느슨하게 연결되었다’라고 표현하기도 한다.
🧐 결국 응집도가 높고, 결합도가 낮도록 컴포넌트를 구성하는 것이 최적의 분리 방법이라 결론 내릴 수 있는데, 그렇게 구성하기 위해선 어떻게 해야할까?
인터페이스 기반 프로그래밍 - 결합도 낮추기
인터페이스를 통해 구현된 코드가 변경되더라도 인터페이스를 사용하는 코드에는 영향을 덜 미치게 하여 모듈의 결합도를 낮춘다.
컴포넌트 단위 테스트에서는 렌더링과 인터렉션만 다루고, 다른 로직들은 훅으로 빼낸다. - 응집도 높이기
참고한 링크들
https://ctrlcccv.github.io/code/2024-03-05-component-split/
https://medium.com/@jang.wangsu/설계-용어-응집도와-결합도-b5e2b7b210ff
https://curiousjinan.tistory.com/entry/effective-strategies-to-reduce-coupling-in-spring
