Intro
웹 페이지를 개발하다보면 여러 모달을 만들고 관리해야 할 일이 많다.
특히 리액트에서 모달의 상태를 state로 관리하다보면 코드도 지저분해지고 비슷한 코드가 여기 저기 흩어져있기 마련이 된다.
해결법을 탐색하던 중 우연히 한 영상을 보게 되었고 그 아이디어를 차용해 내 나름대로 다시 구현해보았다.
설계
기본적인 아이디어는 아래 영상에서 가져왔다.
https://youtu.be/gMzYOE0TV0g?si=0SKd5mKO1-yP1gHc
요점은 ModalController에서 stack 구조로 모달을 관리하는 것, 그리고 Context API를 활용해 프로젝트 내부 어디서나 컨트롤러에 접근할 수 있게 하는 것이다.
이를 간단히 도식으로 나타내면 다음과 같다.
1. 컴포넌트 단에서는 push/pop을 통해 모달을 추가/삭제한다.
2. push/pop 요청 시 ModalController 내부 stack에 모달 정보를 추가/삭제하여 업데이트한다.
3. 업데이트가 완료되면 rerender를 진행한다.
영상에서의 flush 패턴, 비동기를 이용한 모달의 resolve, reject 처리 등 세부 사항은 채택하지 않았다.
그리고 영상처럼 Context API를 사용하는 버전과 더불어 zustand를 이용한 버전도 추가로 구현하였다.
구현
0. 공통
우선 stack에 저장할 모달 정보 객체 타입은 다음과 같이 정의했다.
id: 각 모달을 구분하기 위한 uuid
title: 모달의 제목
content: 모달 내부에 표시할 컨텐츠
accept, cancel: 모달의 확인/취소 버튼 클릭 시 실행할 콜백
1. Context API version
ModalController
먼저, 가장 기본이 되는 ModalController부터 살펴보자.
각 모달의 정보는 modalInfos 배열에 stack 형태로 저장된다.
ModalController 생성 시 리렌더를 위한 state setter를 파라미터로 받아 triggerRerender() method를 만든다. 이는 push, pop 내부에서 리렌더링을 일으키기 위해 사용된다.
Context API
ModalController를 전역적으로 사용하기 위한 Context API 관련 코드이다.
전형적인 Context API를 사용하는 패턴이며 ModalProvider에서 리렌더를 위한 flag setter를 ModalController 생성자에게 넘겨준다.
Modal Rendering
모달을 렌더링하기 위한 컴포넌트이다.
modalInfo(stack) 정보를 가져와 순서대로 렌더링해주어 중첩된 모든 모달을 렌더링할 수 있도록 해주었다.
createPortal을 사용했는데, 이는 HTML DOM의 최상단 레벨에 모달을 띄워 다른 컴포넌트로부터 영향을 받지 않게 하기 위함이다.
createPortal의 기준이 될 노드는 useEffect 내부에서 생성해주었다.
Sample code
구현 내용의 사용 예시이다.
ModalProvider의 자식으로 SampleComponent를 생성하고 내부에 두 종류의 모달을 추가하였다.
동작 확인을 위해 각 모달의 accept는 alert를 띄우도록 하였다.
결과
2. zustand version
상태관리 라이브러리를 이용하면 Context API보다 더 간편하게 구현할 수 있다.
먼저 ModalController와 Context API 관련 코드는 store 하나로 축약 가능하다.
ModalContainer와 Sample code는 store를 사용하는 부분을 제외하면 Context API 버전과 거의 동일하다.
결과 역시 Context API 버전과 동일하다.
Source code
https://github.com/dev-wann/react-boilerplate/tree/main/src/modal
'Web development > React' 카테고리의 다른 글
여러 component끼리 state를 공유해야 할 때 (0) | 2023.09.24 |
---|