useOverlay close 시 컴포넌트가 unmount되도록 처리하기

2025. 9. 14. 19:58·Programming/React

프로젝트 내 모달을 닫을 때 useOverlay로 만든 컴포넌트가 언마운트되지 않아, invalidateQueries가 즉시 실행되는 문제가 있었다. 사용처 코드를 손대지 않고 기존 구조를 유지하면서, close 시점에 overlay를 바로 제거하도록 수정해 원하는 동작(닫을 때 언마운트 → 다시 열릴 때 refetch)을 만들었다.

 

 

작업 배경

모달을 닫을 때 invalidateQueries를 호출하도록 했다. 의도는 이랬다.

  1. 모달을 닫으면 쿼리를 stale 상태로 만든다.
  2. 이후 모달이 다시 열려 컴포넌트가 마운트될 때 자동으로 refetch가 실행된다.

하지만 실제 동작은 예상과 달리, 모달을 닫는 순간 refetch가 바로 실행되었다.

 

 

문제 원인

useOverlay 훅을 디버깅해보니, close를 호출해도 모달 컴포넌트가 언마운트되지 않는 이슈가 있었다.
조사해보니 원인은 close 동작 방식에 있었다.

  1. overlayProps.close는 Controller 내부의 isOpen만 false로 바꾼다.
  2. 하지만 Recoil의 overlayByIdAtom에서는 overlay가 제거되지 않는다.
  3. 결과적으로 모달 컴포넌트는 언마운트되지 않고 계속 트리에 남아 있었다.
  4. 이 상태에서 invalidateQueries를 호출했기 때문에 즉시 refetch가 실행된 것이다.
const Controller = forwardRef(function Controller(
  { overlayElement, overlayId }: { overlayElement: OverlayElement; overlayId: number },
  ref: Ref<{ close: () => void } | undefined>
) {
  const [isOpen, setIsOpen] = useState(false);

  const handleClose = useCallback(() => {
    setIsOpen(false); // ← 언마운트가 아니라 단순히 상태만 false
  }, [overlayId]);

  useEffect(() => {
    requestAnimationFrame(() => {
      setIsOpen(true);
    });
    return () => {
      console.log('Controller unmounted', { overlayId });
    };
  }, [overlayId]);

  useImperativeHandle(ref, () => ({ close: handleClose }), [handleClose]);

  return <OverlayElement isOpen={isOpen} close={handleClose} />;
});

사용처에서는 onClose={overlayProps.close}로 연결해두었기 때문에, 닫아도 atom에서는 overlay가 제거되지 않았다. 따라서 실제 언마운트는 다음번 open 시점에야 발생했다.

 

 

해결책

Toss의 slash/useOverlay는 close와 exit를 분리해서,

  • close: isOpen만 false → 닫기 애니메이션 실행
  • exit: 실제 overlay 언마운트

이런 구조로 되어 있었다. 닫는 애니메이션이 필요한 경우 유용한 방식이다.

하지만 백오피스에서는 애니메이션이나 특별한 처리 과정이 필요 없었기 때문에, close 시점에 바로 overlay를 atom에서 제거하도록 수정했다.

function removeOverlayById(prev: Map<number, ReactNode>, targetId: number) {
  const next = new Map(prev);
  next.delete(targetId);
  return next;
}

const Controller = forwardRef(function Controller(
  { overlayElement: OverlayElement, overlayId }: { overlayElement: OverlayElement; overlayId: number },
  ref: Ref<{ close: () => void } | undefined>
) {
  const [isOpen, setIsOpen] = useState(false);
  const setOverlayById = useSetRecoilState(overlayByIdAtom);

  const handleClose = useCallback(() => {
    setIsOpen(false);
    setOverlayById((prev) => removeOverlayById(prev, overlayId)); // ← close 시점에 overlay 제거
  }, [overlayId, setOverlayById]);

  useImperativeHandle(ref, () => ({ close: handleClose }), [handleClose]);

  return <OverlayElement isOpen={isOpen} close={handleClose} />;
});

이제는 모달을 닫을 때 바로 언마운트가 발생하고, invalidateQueries도 의도대로 모달 재오픈 시점에만 refetch가 실행된다.

 

 

회고

이 과정을 통해 모달의 생명주기를 더 직관적으로 다룰 수 있게 되었고, 불필요한 API 호출 문제도 해결할 수 있었다.

또한 동료와의 리뷰 과정에서 “slash/useOverlay처럼 close와 exit를 분리할 필요가 있는가?”라는 논의를 하면서, 단순히 오픈소스의 코드를 따라 쓰는 것이 아니라, 현재 프로젝트의 요구사항과 제약에 맞는 구조를 고민하는 계기가 되었다.
특히 백오피스 환경에서는 닫기 애니메이션이 필요하지 않았기에, 불필요하게 구조를 복잡하게 만들지 않고 최소한의 수정으로 문제를 해결하는 쪽을 선택했다.

이 경험을 통해 오픈소스의 다양한 접근 방식을 참고하되, 그대로 복제하기보다는 맥락에 맞게 취사 선택하는 것이 중요하다는 점을 다시금 깨달았다.

 

고고~

'Programming > React' 카테고리의 다른 글

React Quill Text Editor 도입 후 이슈 해결하기  (0) 2025.09.27
SDUI (Server Driven User Interface)  (0) 2025.09.13
React Hook Form 렌더링 최적화  (0) 2025.06.22
React 프로젝트 의존성 버전 업그레이드 하기 - axios (0.18.0 → 1.7.7)  (1) 2024.11.21
Kakao 소셜 로그인: Local 환경에서는 되고, S3 + Cloudfront 배포 후는 안되는 문제 해결  (1) 2024.09.23
'Programming/React' 카테고리의 다른 글
  • React Quill Text Editor 도입 후 이슈 해결하기
  • SDUI (Server Driven User Interface)
  • React Hook Form 렌더링 최적화
  • React 프로젝트 의존성 버전 업그레이드 하기 - axios (0.18.0 → 1.7.7)
gitit
gitit
짬내서 쓰는 프론트엔드와 기술 메모 공간
  • gitit
    깃잇-gitit
    gitit
  • 전체
    오늘
    어제
    • 분류 전체보기
      • Coding Test
        • Programmers
        • BackJoon
      • Development Tools
        • Firebase
        • Git
        • Monorepo
      • Programming
        • JavaScript
        • React
        • React-Native
      • etc.
        • GeekNews
        • Blockchain
      • Technical Interview
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    프론트엔드
    javascript
    BFS
    geeknews
    자바스크립트
    node.js
    백준
    Firebase
    styled-components
    독학
    알고리즘
    파이어베이스
    기본문법
    매일메일
    frontend
    긱뉴스
    React
    리액트
    파이썬
    js
    AWS
    modal
    코딩테스트
    개발
    프로그래머스
    kakao
    코딩
    코테
    기술 질문
    FE
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
gitit
useOverlay close 시 컴포넌트가 unmount되도록 처리하기
상단으로

티스토리툴바