어떤 React 디자인 패턴이 적절한 것일까 ?
Table of contents
들어가면서 🙋🏻♂️
요즘 저는 과기부에서 주관하는 "소프트웨어 마에스트로 13기" 활동과 "IT 연합 동아리 YAPP" 활동을 병행하며 살고 있습니다.
이 중 YAPP 동아리 활동에서 한창 개발 단계인 과정 속에서 발생한 이슈에 대해서 고민해보고 팀원 들과 결론을 내린 이야기를 풀어보기 위해서 해당 글을 작성하게 되었습니다.
React 디자인 패턴
은 현재(2022.07 기준)도 일반화 된 틀이 있는 시점은 아닌 것 같고, 그만큼 다양한 디자인 패턴들이 검색을 해보면 다양하게 언급되는 것을 알 수 있을 것입니다.
그 중, YAPP 프로젝트에서 저희 프론트엔드 팀원들 사이에서 언급된 React 디자인 패턴 2가지
에 대한 이슈에 대해 살펴보겠습니다.
문제의 상황 🤔
저의 동아리 팀원 중 프론트엔드 개발자는 세 명이 존재합니다. 각자 본인이 공부하거나 현업에서 적용해오던 React 컴포넌트 디자인 패턴 기준이 있었습니다.
먼저, 제가 프로젝트에 적용하고 있던 컴포넌트 디자인 패턴에 가장 가까운 것은 Container - Presenter 패턴
이었습니다.
그로 인한 하나의 컴포넌트에 대한 폴더 구조 예시를 살펴보면 다음과 같습니다.
ComponentA
- index.tsx // ComponentA`s Container 🔍
- ComponentAView.tsx // ComponentA`s Presenter 🔍
- ComponentA.styled.ts // ComponentA`s Styled Component (by.emotion)
- ComponentA.type.ts // ComponentA`s Types
// ComponentA/index.tsx
...
import ComponentAView from './ComponentAView';
const ComponentA = () => {
// 데이터 or 작업들
return <ComponentAView {...props 로 내려줄 데이터 및 작업들} />;
};
export default ComponentA;
// ComponentA/ComponentAView.tsx
const ComponentAView = (props: propsType) => {
return ( ... ); // View 는 Container 로 부터 내려받은 데이터 or 작업 바인딩
};
export default ComponentAView;
해당 방식을 통해서 추구하고자 한 궁극적인 목표는 로직과 단순한 뷰를 최대한 분리해서 관리(= 관심사의 분리, IoC)
를 실천하고 싶다는 것이었습니다.
프로젝트를 시작한 초기에는 모르겠지만, 점차 프로젝트 규모가 커진다는 것을 전제했을 때 하나의 컴포넌트 안에 로직과 뷰가 함께 존재할 수록 컴포넌트가 비대해지고 그만큼 유지보수하기 힘들 수 있다고 생각했기 때문입니다.
...
이와 비슷한 관점에서 다른 팀원이 추구하고 있던 방식에 가장 가까운 것은 Custom Hook 패턴
이었습니다.
그로인한 하나의 컴포넌트에 대한 폴더 구조 예시를 살펴보면 다음과 같습니다.
ComponentA
- index.tsx // ComponentA 🔍
- ComponentA.styled.ts // ComponentA`s Styled Component (by.emotion)
- ComponentA.type.ts // ComponentA`s Types
...
hooks/useMyCustomHook.tsx
import { useMyCustomHook } from "hooks/useMyCustomHook"
const ComponentA = () => {
const { ... } = useMyCustomHook(...); // 커스텀 훅을 통해서 데이터 or 작업을 받음
return ( ... ); // 커스텀 훅을 통해 얻어낸 데이터 or 작업을 바인딩
};
export default ComponentA;
앞선 Container - Presenter 패턴
처럼 로직을 담당하는 Container 컴포넌트가 뷰를 담당하는 Presenter 컴포넌트를 감싸는 형태가 아닌, 하나의 컴포넌트 형태이면서 복잡한 로직들은 Custom Hook
을 통해 단순히 호출해서 필요한 데이터와 작업들을 받고 뷰에 바인딩해서 렌더링하는 방식입니다.
...
일단, 두 방식이 프로젝트를 진행하는 데 당장 크게 문제를 일으키는 이슈는 아니긴 합니다.
중요한 것은 어찌되었든 팀끼리 하나의 방식을 통일
하는 것이 당장에 이슈였습니다.
나의 생각 정리 💆🏻♂️
나의 생각 정리 이후 팀원에게 공유
팀원들의 답변
마치며...
해당 이슈가 오프라인에서 이야기에서 나올 당시에는 위에 첨부한 팀원들에게 공유한 저의 생각 정리만큼 정돈된 의견을 잘 전달하지 못했다고 생각들었습니다. 그래서 위와 같이 좀 더 관련된 내용을 조사하고 정리해서 공유를 하게 되었고, 이와 같은 사례를 이 글에 공유하게 되었습니다.
결과적으로는 Custom Hook 패턴
방식으로 진행하는 것으로 컨벤션을 통일하기로 했고 대략적인 구조는 다음과 같습니다.
ComponentA
- index.tsx // ComponentA 🔍
- ComponentA.styled.ts // ComponentA`s Styled Component (by.emotion)
- ComponentA.type.ts // ComponentA`s Types
- useCustomHook1.tsx // ComponentA`s 에만 사용되는 Custom Hook (여러 개일 수 있음)
- ...
...
hooks/
- 여러 컴포넌트들이 공유가능 Custom Hook 들은 hooks 디렉터리에서 관리
덕분에 이번 기회를 통해서 VAC(View Asset Component) 패턴
이라는 것을 새로 알게 되었고, 당장 진행 중인 프로젝트에는 적용하진 못하고 있지만 이후 리팩터링 과정에서 또는 새로운 프로젝트를 진행하면서 FE 와 UI 를 좀 더 체계적으로 분리해서 관리하고 싶을 때
시도해보고 싶습니다.
이와 별개로 이번 이슈를 통해 팀원들과 이야기하며 제가 공감하게 된 내용은 다음과 같습니다.
💡 다양한 디자인 패턴이 있지만 정답은 없습니다. 단지 그 때 우리의 상황에 가장 알맞은 방식을 추구하는 것이 최선입니다.
cf. 깃헙 커밋 메시지 컨벤션, 코드 포맷팅 등과 함께 이러한 컴포넌트 디자인 패턴
또한 프로젝트 시작에 앞서 컨벤션을 통일하는 것이 가장 바람직하다고 생각이 들었습니다. (프로젝트 시작 전에 결정되면 가장 좋고, 빠르면 빠를수록 좋은 것 같습니다 😂)
감사합니다.
참고 자료