
웹뷰와 네이티브 경계를 넘어, 복잡한 오버레이 지옥을 우아한 비동기 큐로 정복한 이야기
당근 로컬비즈니스실 동네걷기팀에서는 무분별하게 마운트되어 겹쳐 나타나는 온갖 모달, 바텀시트, 네이티브 팝업으로 인해 골머리를 앓았습니다. 필자는 이 현상을 UI 라이브러리로 제어하는 대신, 본질적인 원인인 '제멋대로 발화되는 비동기 제어권'에 주목하여 자바스크립트의 표준 명세 기반인 비동기 상태 머신을 새로 정의했습니다. Promise와 마이크로태스크 큐를 조합해 도메인 로직의 응집도를 무너뜨리지 않는 세련된 동시성 제어 큐를 완성해낸 영리한 FE 엔지니어링 과정을 소개합니다.
복잡한 사용자 인터랙션과 다양한 비동기 온보딩 시나리오가 뒤얽힌 웹뷰 애플리케이션을 개발하고 계신 분들께 추천합니다. 특히 팝업 제어용 전역 플래그가 점차 비대해져 코드 파악이 어렵거나, 다단계 동시성 요구사항을 JS 순수 명세(Microtask, Promise)로 우아하게 해결하고 싶은 시니어 개발자에게 실무적인 통찰을 제공합니다.
당근 동네걷기 서비스에서는 모달, 바텀시트, 네이티브 권한 팝업 등 다수의 UI 오버레이가 독립적인 조건 하에서 무분별하게 동시에 호출되어 겹쳐 뜨는 사용자 경험 저해 문제가 존재했습니다. 이전에는 컴포넌트 간에 onClose 핸들러를 체이닝하여 억지로 순서를 맞추었으나, 이는 결합도를 높이고 신규 오버레이 추가 시 모든 분기를 다시 이어야 해 유지보수 생산성을 심각하게 떨어뜨렸습니다.
문제를 UI 렌더링이 아닌 '비동기 발화의 동시성 제어'로 재정의하고, 오버레이 노출 완료 상태를 Promise의 settled 상태에 대응시킨 우선순위 큐를 개발했습니다. ES2024의 Promise.withResolvers 명세를 기반으로 외부에서 Promise의 완료(resolve) 권한을 제어하도록 설계하고, queueMicrotask를 이용해 마운트 페이즈에서 연달아 들어오는 예약 요청들을 한 번에 모아 정렬한 뒤 안전하게 순차 실행하는 스케줄러를 도입했습니다.
웹뷰 내부 오버레이뿐만 아니라 제어할 수 없던 네이티브 브릿지 팝업의 pending 상태까지 단일 비동기 흐름으로 완벽히 병합했습니다. 다단계 비동기 시퀀스를 하나의 원자적 작업 단위로 묶어 처리함으로써 비동기 꼬임 현상을 제거하였으며, UI 렌더링 및 도메인 로직이 전역 레이어로 격상되지 않고 호출처 내부에 유지되어 코드의 결합도가 낮아지고 응집도가 유지되었습니다.
Trade-off
큐의 정상 작동이 호출처 비동기 함수의 Promise settle(완료/거부) 여부에 전적으로 의존하므로, 예기치 않은 예외 발생이나 컴포넌트의 비정상적 언마운트 시 큐가 영원히 pending 상태로 멈추는 데드락 위험이 있습니다. 보수적인 설계 관점 상 화면 이탈 처리나 중복 차단 시의 예외 케이스(cancelled 상태 분기 등)를 꼼꼼하게 정의하고 관리해주어야 하는 부가적인 개발 비용이 수반됩니다.
Promise 인스턴스와 함께 해당 Promise의 운명을 바꿀 수 있는 resolve, reject 함수를 객체 형태로 한꺼번에 반환해 주는 ES2024 표준 API입니다. 비동기 제어권을 생성자 스코프 외부로 간편하게 끌어내기 위한 최신 문법입니다.
브라우저 엔진이 현재 수행 중인 동기 스크립트(태스크)를 완전히 끝마친 직후, 렌더링 파이프라인이나 이벤트 루프의 매크로태스크를 시작하기 전에 특정 마이크로태스크를 즉시 실행하도록 보장하는 웹 표준 API입니다.
자바스크립트 코드가 실행될 때 생성되는 내부 데이터 구조로, 선언된 변수 정보와 변수가 바인딩되어 있는 스코프(렉시컬 공간) 및 제어 흐름 위치를 저장합니다. 비동기 await 구문을 만나면 현재 함수 컨텍스트는 일시 중단되며 백업됩니다.