상세 컨텐츠

본문 제목

React 전역 Modal 추상화 정복기 - (4)

전역 모달 추상화 정복기

by 뎁희 2024. 4. 13. 02:19

본문

ContextAPI와 상태 관리 라이브러리의 목적

-

이번 글은 완성 모달에 영향을 미치지 않는, 최종 코드에서 삭제된 부분에 대한 내용이지만 그 과정을 적어두고자 작성하는 챕터로 방향성을 잘못 잡으면 코드가 얼마나 산으로 갈 수 있는지를 느껴본다.


Local 모달의 의미를 찾아서

내가 창조한 요상한 기능은 Local 모달 이라고 부르기로 한다. 먼저 이 상황이 발생한 이유는 최종이라고 생각한 모달이 `Jotai`로 했을 때와 어떤 차이가 있는지 체감이 되지 않았기 때문이다. 분명 뷰와 관련된, 특히 재사용 가능한 UI를 전역으로 관리할 때에는 `Context`가 낫다고 들었고, 공감이 되는 듯하면서도 정확히 설명할 수 없었다. 

 

`Context`의 역할을 느끼기 위해 모달을 부분적으로 사용하는 상황을 가정하여 재사용할 수 있는 로직을 구현해 보자.


Local 모달의 등장

const renderModal = useCallback(
  (portalContainer?: MutableRefObject<HTMLDivElement | null>) => {
    const modal = modals.find((modal) => modal.id === modalId);
    return (
      modal &&
      (modal.isLocal && portalContainer ? (
        <ModalPortal portalContainer={portalContainer.current}>
          <LocalModal modal={modal} onClose={closeModal} />
        </ModalPortal>
      ) : (
        <ModalPortal>
          <Modal modal={modal} onClose={closeModal} />
        </ModalPortal>
      ))
    );
  },
  [modals, modalId, closeModal],
);

원하는 경우에만 로컬 영역에 한정되게 열리므로 로컬 모달이라 칭했다. `modal state`에 `isLocal` 선택 옵션을 추가하고, 유무에 따라 다른 모달을 렌더링 할 수 있도록 전체적으로 수정한다. `renderModal`은 `portalContainer`를 선택적으로 받을 수 있게 되었다. 하지만 `LocalModal` 레이아웃도 별도로 필요하고 여러모로 복잡해졌다. 원하는 대로 구현되지만 이 코드를 볼 때마다 냄새난다는 소리를 들을 것 같았다.


결론

온갖 테스트를 해보고 나서야 어떤 상황에서 ContextAPI와 Jotai를 구분해 사용할까? 에 대해 다시 생각해 볼 수 있었다.

상태를 전역으로 공유하다가 부분적으로 독립적인 상태 공유가 필요할 때

모달을 예를 들면 전역으로 `modals` 상태를 공유하다가 특정 컴포넌트부터 그 하위 자식 컴포넌트에는 독립적인 `modals` 상태를 갖고 싶다면 모든 로직은 동일하기 때문에 `modals`상태만 분리하면 된다. 꼭 모달이 아니더라도 상태와 로직을 기준으로 생각해 보면 이해가 쉽다. 똑같은 상황을 `ContextAPI`와 `Jotai`로 비교해 보자.

 

ContextAPI

  1. 원하는 부분을 Provider로 감싼다.

 

Jotai

  1. atom을 새로 만든다.
  2. atomFamily를 이용한다.
  3. Provider를 이용해 store를 생성한다.

 

`Jotai`의 `1번` 방법은 atom을 생성하고 별도의 이름을 부여하면서 관리해야 하는 atom이 늘어나게 된다. `2번` 이 해결책이 될 수 있지만, id와 같은 구분값이 필요하고 구분된 상태를 공유하는 컴포넌트끼리는 동일한 id를 알아야 하므로 신경 쓸 일이 많아진다. `3번`은 store가 모달 상태 외 다른 상태들을 함께 관리 중이라면 부분적으로 Provider로 감쌌을 때, 다른 상태에 접근할 수 있도록 또 다른 처리를 해주어야 한다.


정말 간단하다는 이유만으로 사용해도 되는 걸까?

전역 상태 관리 도구로 Jotai를 쓰고 있기 때문에 Context로 모달을 관리하게 되었을 때의 확실한 장점을 얻고 싶었다. 그렇지 않으면 굳이 여러 가지로 나누어 관리할 필요가 없기 때문이다. 그래서 간단함을 떠나 무언가 다른 점이 있을 것 같았다. 컨텍스트의 특징과 함께 다시 생각해 보자.

 

  1. 부분적인 Provider 적용
    Provider가 여러 개 있더라도 컴포넌트와 가장 가까운 Provider의 값을 공유한다. 즉, 같은 로직을 원하는 컴포넌트마다 별도의 상태를 갖도록 하는 모듈화가 가능하고, 이 특징 때문에 독립적인 상태 공유가 간단해진다.
  2. 독립적인 라이프사이클
    Provider는 하나의 컴포넌트이면서 공유 중인 상태를 소비하는 자식 컴포넌트의 라이프사이클과는 독립적이다. 이 말은 자식 컴포넌트의 라이프사이클에 영향을 받지 않는다는 뜻이다. 이 때문에 컴포넌트와의 의존성이 낮아져 외부의 영향을 받지 않고, 영향을 주지도 않는다. 그만큼 안정적으로 동작해 재사용성이 높아진다.
  3. React가 제공하는 공식 API

 

처음에는 모달 같은 UI 요소는 비슷한 로직으로 재사용하는 경우가 많고, 화면과 관련된 상태라 독립적인 라이프사이클이 더 적합하다고 생각했다. 하지만 최종 완성 코드에서 어떤 상태 관리 방법을 써도 `useModal` 커스텀훅으로 분리하는 것은 동일했기 때문에 독립적인 라이프사이클의 장점은 활용하지 않게 되었다. 

 

그래서 조금은 뻔하지만 가장 큰 장점을 3번이라고 결론지었다. 모달과 같은 UI는 현재 진행 중인 프로젝트뿐만 아니라 다른 프로젝트에서도 많이 사용되는 요소로 다음에 다른 프로젝트를 하더라도 또 사용하게 될 가능성이 높다. 그렇기 때문에 외부 라이브러리에 의존하지 않고, 미리 추상화된 UI 컴포넌트는 React로 만드는 어떤 프로젝트에서도 재사용이 가능하다는 점은 큰 장점으로 느껴진다.


짚고 넘어가기

무엇이든 이유 없이 쓰지 말자!라는 생각으로 돌고 돌았지만 이 차이를 구분해내지 못했다면 나는 이 프로젝트에 Jotai를 쓰겠다고 생각한 순간부터 어떤 상황에서도 atom을 만들어 관리했을 것이다. 이전 글에서 말했듯이 만들어진 기능을 가져다 쓸 때에는 어떤 불편함을 해소하기 위해 만들어졌을까를 한 번 더 고민해 보면 좋은 것 같다. 이제 개운한 마음으로 남은 챕터에서 모달 비동기 처리를 완성해 본다.


 

 

 


 

 

 

 

관련글 더보기