(2) - useModal, 여러 개의 Modal을 선언적으로 관리하는 방법론
이 글에서는 여러 개의 Modal을 Stateful하고, 선언적으로 사용할 수 있는 방법론을 제시해요.
토스 오더 프로젝트는 여러 개의 Modal을 적극적으로 사용하였어요.
토스 오더 - 가장 쉬운 테이블 오더 (1) - 직관적인 UI/UX를 어떻게 만들었나요?
- 사용자의 인식과 의식을 고려했어요.
페이지의 이동이 발생하거나, Layout이 크게 변하면 사용자는 페이지에 대해 처음부터 다시 파악해야 해요. 이 문제를 해결하기 위해 Page Depth를 없애고, Modal을 적극적으로 활용했어요.
사용자의 이목을 끌기 위해 새로운 window 창이 나타나서, 사용자의 행동을 요구하거나 중요한 데이터를 표시해요.
브라우저의 요소로 동작하는 Alert 또는 Popup과 다른 점은 브라우저의 viewport 안에 나타나는 DOM 요소라는 점이에요.
즉, UI의 일부에요.
Chakra UI의 Modal 사용법은 아래와 같아요.
좀 복잡하죠. 의도를 가볍게 표현하는 코드로 제가 다시 작성해볼게요.
위 함수형 컴포넌트(이하 컴포넌트)인 <Modal버튼컴포넌트>
에는 클릭하여 <Modal>
을 열기 위한 <버튼>
이 있고, 그에 해당하는 <Modal>
이 있어요.
이 <Modal>
에 대한 <버튼>
을 어떻게 사용하면 될까요?
위 코드처럼 필요한 곳에서 <Modal버튼컴포넌트>
를 불러와서 사용할 수 있어요.
버튼을 클릭하면 Modal이 열릴 것 같아요.
하지만 이러한 구조의 문제는 무엇일까요?
<Modal>
이 실행되면 console.log를 실행하도록 해볼게요.
열기 버튼을 누르기 전까진 <Modal>
이 렌더되지 않으니까, <Modal>
이 열리기 전까진 console.log
의 내용도 보이지 않을 것 같아요.
어? 아직 <Modal>
을 열지 않았는데, 안에 있는 console.log
가 벌써 실행되었어요.
만약 이 <Modal>
의 내용이 중요한 연산을 수행하거나, API 호출이 발생한다면 이대로는 안될 것 같아요.
<Modal>
이 보여지기 전까진 <Modal>
이 실행되지 않도록 해볼까요?
<버튼>
을 클릭하여 isOpen
이 true
일 때만 <Modal>
이 실행되도록 수정해봤어요.
<Modal>
이 열리기 전까진 console.log
가 실행되지 않네요.
좋아요. 기대했던 대로 작동하고 있어요. 그런데, 이 코드는 나쁜 코드에요.
<버튼>
과 <Modal>
의 결합이 강해요.즉, <Modal>
이 자신을 열어주는 버튼 혹은 그 액션에 종속되어있어요.
이 문제를 개선하기 위해 useModal
이라는 custom hook을 만들어볼게요.
원하는 바는 세가지에요.
<Modal>
을 열어주는 Action에 <Modal>
이 종속되지 않았으면 좋겠어요.<Modal>
이 열리기 전까진 실행되지 않아야 해요.<Modal>
이 종속되지 않았으면 좋겠어요.지금까지 작성한 코드를 다시 살펴볼까요?
하나의 <Modal버튼컴포넌트>
내에서 Modal의 열린 상태(isOpen)를 boolean
으로 갖고 있고, 이 상태를 제어하는 action이 <Modal>
바깥의 <버튼>
에 담겨있어요.
이 둘의 관계를 끊고 싶어요. 그러려면 isOpen
의 상태와, 이를 제어하는 action을 분리하면 될 것 같아요.
Context API 혹은 전역 상태 관리로 따로 빼주면 좋을 것 같아요.
저는 Recoil을 사용해서 구현해볼게요. <Modal>
과 <버튼>
도 3개 정도 만들어 볼까요?
회원 가입을 하는 상황이라고 가정해보고, 일반적인 회원 가입과 깃헙, 구글의 계정으로 회원가입도 가능하다고 해볼게요.
<Modal>
의 isOpen
boolean값을 Recoil을 이용해 전역 상태로 관리해볼게요.
그리고 이 상태를 불러오고, 열거나 닫는 action을 갖고올 수 있는 hook, useModal
을 구현해볼게요.
useModal
에 대상이 되는 Modal
의 이름을 넣으면 이에 대한 isOpen
상태 혹은 이를 컨트롤하는 action을 받아올 수 있도록 했어요.
버튼에는 useModal
의 onOpen
을 받아오고, Modal
에서는 isOpen
과 onClose
를 받아오면 될 것 같아요.
<버튼>
자체에서 onClick
에 대한 action을 useModal
로부터 직접 불러와서 할당했어요.
<회원가입Modal>
하나만 살펴볼게요.
Modal 자체에서 필요한 isOpen
과 onClose
를 useModal
로부터 직접 불러와서 할당했어요.
이제 페이지 컴포넌트는 어떻게 수정할 수 있을까요?
<버튼 컴포넌트>
와 <Modal 컴포넌트>
가 분리되었어요.
각 컴포넌트가 Stateful하므로 페이지는 isOpen
에 대한 상태나 action을 알 필요가 없어졌어요.
결과물을 한번 볼까요?
useModal
hook을 사용하였기 때문에 각 Modal들과 버튼들을 선언적으로 넣어주기만 해도 되네요!
<Modal>
이 열리기 전까진 실행되지 않아야 해요.앗! 하지만 다시 Modal
이 열리기 전에 실행되기 시작했어요.
그리고 지금의 형태로는 Modal
이 많아질 수록 <페이지 컴포넌트>
의 코드가 복잡해질 것 같아요.
개선해볼까요?
전역 상태인 modalStore
에서 어떤 상태가 true
로 변경된다면, 그 Modal
을 렌더할 수 있게 반환해주는 <Switch>
라는 컴포넌트를 만들었어요.
이제 <Modals>
컴포넌트에서 각 Modal
들을 불러와 <Switch>
에 넣어줄게요.
여러 개의 Modal
을 하나의 컴포넌트로 줄일 수 있게 되었어요.
물론 전역 상태를 사용하기 때문에, 다른 페이지나 자식 컴포넌트에서도 당연히 Modal
을 열 수 있어요.
3번의 조건을 충족하는지 다시 볼까요?
좋아요. 초반에 의도했던 대로 Modal
이 열리기 전까진 실행되지 않네요!
이렇게 전역 상태를 이용하여 Modal
의 사용 방식에 대한 고민을 해결했어요.
앱의 <Page>
에 <Modals>
만을 넣어 선언적인 코드 작성을 이루어냈어요!
이제 다양한 종류의 Modal
을 쉽게 사용할 수 있게 되었어요. 토스 오더에는 어떤 Modal
들이 있는지 볼까요?
잘못되었거나 부족한 부분이 있다면 자유롭게 피드백 해주세요!
코드에 대한 이야기를 하는 것을 좋아해요.
긴 글 읽어주셔서 감사해요!😚
저도 UIUX 디자인이하고 싶어서 이것저것 찾아보는 중인데, 귀한 정보 알려주셔서 정말 감사합니다ㅠㅠ 이 글 보니까 저도 얼른 준비해서 UIUX 디자인 해보고 싶어요ㅜㅜ 그런데 요즘은 UIUX 디자인 준비할 때 부트캠프 많이 한다고 하던데.. 현직자가 붙어서 실무 경험 쌓게 해주고, 포트폴리오 만들 수 있다고 해서 혹하네요. 제가 찾아본 곳은 여기있는데 (수강생들이 만든 포폴 보니까 혹해서요..) 주 3일만 들어도 UIUX 디자이너로 취업할 수 있는거 같더라고요. 혹시 여기는 어떻게 보시나요?
https://zrr.kr/kaZT
chakra ui의 useDisclosure를 간단히 만드셨군요! 좋은 글 감사합니다~!