리액트 모달 화면 깜빡임 없이 여러 개 출력하기

리액트 타입스크립트 공부 중입니다.

초기 진입 화면에 레이어 영역, 컨텐츠 영역, 모달 영역, 딤 영역 등으로 구분하여 컴포넌트를 import 해놨습니다.

zustand store에 모달 정보(path, params)를 저장합니다.

map을 사용하여 화면에 쌓아 출력하는 구조인데 store 값(modal list)을 바라보다보니 모달이 추가되거나 닫힐 때 재랜더링 되는 현상이 발생합니다.

이를 해결하기 위한 방법 추천해주세요.

store를 그대로 사용할 수 있으면 좋겠습니다. 이론적으로 불가능하다면 아예 새로운 방식을 도입해도 좋아요.

3개의 답변이 있어요!

  • 안녕하세요. 이승호 전문가입니다.

    쥬스탄드 스토어를 활용해 다중 모달을 관리할 때 모달 리스트 배열 전체를 구독하게 되면, 모달이 하나만 추가되거나 삭제되어도 배열의 참조 값이 변하기 때문에 현재 열려 있는 다른 모달들까지 전부 재렌더링되는 깜빡임 현상이 발생합니다.

    현재 스토어 구조를 그대로 유지하면서 불필요한 재렌더링을 막고 깜빡임을 해결할 수 있는 가장 현실적이고 효율적인 방법들을 소개해 드리겠습니다.

    첫 번째로 추천하는 방법은 스토어를 구독할 때 셀렉터를 쪼개서 가져오는 방식입니다.

    기존에는 컴포넌트에서 모달 리스트 배열 전체를 통째로 가져와 맵 함수를 돌렸을 텐데, 모달을 화면에 뿌려주는 모달 컨테이너 컴포넌트와 개별 모달 컴포넌트의 역할을 철저히 분리해야 합니다.

    최상위 모달 컨테이너 컴포넌트에서는 쥬스탄드 스토어에서 모달들의 고유한 아이디 값이나 패스 목록만 포함된 단순 배열만 구독하도록 합니다. 그리고 맵 함수를 돌릴 때 개별 모달 컴포넌트에게 아이디 값만 프롭스로 넘겨줍니다.

    각각의 개별 모달 컴포넌트 내부에서는 넘겨받은 아이디 값을 가지고 쥬스탄드 스토어에서 자기 자신의 파람스나 데이터만 쏙 골라서 구독하도록 코드를 수정합니다. 이렇게 하면 새로운 모달이 추가되어도 기존에 열려 있던 모달 컴포넌트들은 자기가 감시하고 있는 데이터가 변하지 않았기 때문에 재렌더링을 수행하지 않고 그 자리에 가만히 유지됩니다.

    두 번째 방법은 쥬스탄드에서 제공하는 얕은 비교 함수인 샬로우를 활용하는 것입니다.

    만약 모달 배열의 주소값은 바뀌더라도 내부의 실질적인 컨텐츠 데이터가 변하지 않았다면 재렌더링을 건너뛰도록 제어할 수 있습니다. 쥬스탄드 스토어에서 데이터를 호출할 때 두 번째 인자로 샬로우 함수를 넣어주면, 리액트가 배열 내부의 값들을 하나씩 비교하여 실제로 내용물이 바뀌었을 때만 컴포넌트를 다시 그립니다. 이 방법은 기존 코드를 크게 고치지 않고 한 줄의 코드 추가만으로도 어느 정도 렌더링 최적화 효과를 볼 수 있습니다.

    세 번째로 리액트의 내장 기능인 유즈메모나 리액트 닷 메모를 적극적으로 결합하는 방법이 있습니다.

    개별 모달 컴포넌트들을 리액트 닷 메모로 감싸주면 프롭스로 전달되는 값들이 변하지 않는 한 부모 컴포넌트인 모달 컨테이너가 스토어 변화로 인해 재렌더링되더라도 자식 모달들은 이전에 그려둔 화면을 그대로 재사용합니다. 이때 주의할 점은 맵 함수를 돌릴 때 주는 키 값으로 배열의 인덱스를 쓰면 안 되고, 반드시 모달의 고유한 고유 아이디나 패스 명을 지정해 주어야 리액트가 어떤 모달이 그대로 유지되어야 하는지 정확히 인지합니다.

    이론적으로 스토어를 바꾸지 않고도 컴포넌트의 구조적 분리와 개별 구독 방식을 도입하면 깜빡임 현상을 완벽하게 잡아낼 수 있습니다. 핵심은 모달 리스트 전체를 한 곳에서 다 들여다보게 하지 말고, 껍데기만 리스트를 보게 한 뒤 실제 알맹이들은 자기 데이터만 바라보도록 파편화하는 것임을 기억하시면 좋겠습니다.

    채택 보상으로 174베리 받았어요.

    채택된 답변
  • 안녕하세요. 김재훈 전문가입니다.

    Zustand 스토어의 전체 상태를 그대로 구독하면 배열의 참조값이나 원소 변경 시 모달 영역 전체가 불필요하게 재렌더링 되므로 개별 모달 컴포넌트가 자신의 ID 나 인덱스만으로 스토어의 특정 모달 데이터만 선택적으로 구독하도록 구조를 변경해야 합니다 즉 메인 모달 영역은 모달의 ID 배열만 구독하여 리스트가 추가 삭제될 때만 렌더링 되게 하고 실제 모달 컨텐츠는 각작 독립된 개별 컴포넌트로 분리하여 shallow나 selector를 통해 자신에게 필요한 pach 와 params 만 꺼내 쓰도록 격리하면 Zustand 스토어를 그대로 유지하면서도 불필요한 전체 재렌더링을 막을 수 있습니다

  • 안녕하세요. 김상엽 전문가입니다.

    Zustand에서 전체 배열을 구독하면 요소를 추가/삭제할 때 배열 주소가 바뀌어 모든 모달이 재렌더링됩니다.
    이를 막으려면 컨테이너는 모달 ID 배열만 구독하게 하고, 개별 모달 컴포넌트 내부에서 자신의 ID로 strore 데이터를 각각 독립 구독하게 분리하세요.
    이렇게 하면 변경된 모달만 정밀 렌더링되어 깜빡임이 사라집니다.