DevLog

엔지니어링 블로그를 한 곳에서 탐색하고, 최근 발행 흐름을 빠르게 파악할 수 있는 서비스 입니다.

Quick Links

  • Latest Feed
  • Engineering Directory

Support

  • 소개
  • 개인정보처리방침

Contribute

  • 원하는 블로그 추가 (준비 중)
  • Feedback

© 2026 DevLog Inc. All rights reserved.

본 사이트는 공개 RSS 피드를 통해 콘텐츠를 수집하며, 모든 콘텐츠의 저작권은 원저작자에게 있습니다.

Back to Feed
Read Original

Contents

#Frontend

당근 동네걷기 위에서 동시에 쏟아지는 오버레이들을 우아하게 제어하기

당근 동네걷기 위에서 동시에 쏟아지는 오버레이들을 우아하게 제어하기
01

Summary

동시에 쏟아지는 모달과 OS 권한 팝업, 당근 FE팀은 어떻게 'Promise'로 줄 세웠을까?

웹뷰와 네이티브 경계를 넘어, 복잡한 오버레이 지옥을 우아한 비동기 큐로 정복한 이야기

당근 로컬비즈니스실 동네걷기팀에서는 무분별하게 마운트되어 겹쳐 나타나는 온갖 모달, 바텀시트, 네이티브 팝업으로 인해 골머리를 앓았습니다. 필자는 이 현상을 UI 라이브러리로 제어하는 대신, 본질적인 원인인 '제멋대로 발화되는 비동기 제어권'에 주목하여 자바스크립트의 표준 명세 기반인 비동기 상태 머신을 새로 정의했습니다. Promise와 마이크로태스크 큐를 조합해 도메인 로직의 응집도를 무너뜨리지 않는 세련된 동시성 제어 큐를 완성해낸 영리한 FE 엔지니어링 과정을 소개합니다.

  • 01오버레이 겹침을 화면 UI 문제가 아닌 '비동기 작업의 동시성 제어' 관점으로 치환한 뛰어난 문제 재정의 능력
  • 02웹뷰가 직접 통제할 수 없는 OS 시스템 팝업을 브릿지 Promise의 pending 기간을 활용해 실행 큐 속에 완벽히 병합
  • 03ES2024 표준으로 정립된 Promise.withResolvers 패턴을 사용해 선언적이고 간결하게 외부 settle 제어권 획득
  • 04React 마운트 주기에서 발생하는 다중 예약을 queueMicrotask로 가로채어 동기식 경합 없이 정확한 우선순위 정렬 보장
  • 05복합 비동기 시퀀스(권한 요청→조건 분기→모달 노출)를 단 하나의 트리거 단위로 묶어 완벽한 트랜잭션 보장

+RECOMMENDATION

복잡한 사용자 인터랙션과 다양한 비동기 온보딩 시나리오가 뒤얽힌 웹뷰 애플리케이션을 개발하고 계신 분들께 추천합니다. 특히 팝업 제어용 전역 플래그가 점차 비대해져 코드 파악이 어렵거나, 다단계 동시성 요구사항을 JS 순수 명세(Microtask, Promise)로 우아하게 해결하고 싶은 시니어 개발자에게 실무적인 통찰을 제공합니다.

The Problem

당근 동네걷기 서비스에서는 모달, 바텀시트, 네이티브 권한 팝업 등 다수의 UI 오버레이가 독립적인 조건 하에서 무분별하게 동시에 호출되어 겹쳐 뜨는 사용자 경험 저해 문제가 존재했습니다. 이전에는 컴포넌트 간에 onClose 핸들러를 체이닝하여 억지로 순서를 맞추었으나, 이는 결합도를 높이고 신규 오버레이 추가 시 모든 분기를 다시 이어야 해 유지보수 생산성을 심각하게 떨어뜨렸습니다.

The Solution

문제를 UI 렌더링이 아닌 '비동기 발화의 동시성 제어'로 재정의하고, 오버레이 노출 완료 상태를 Promise의 settled 상태에 대응시킨 우선순위 큐를 개발했습니다. ES2024의 Promise.withResolvers 명세를 기반으로 외부에서 Promise의 완료(resolve) 권한을 제어하도록 설계하고, queueMicrotask를 이용해 마운트 페이즈에서 연달아 들어오는 예약 요청들을 한 번에 모아 정렬한 뒤 안전하게 순차 실행하는 스케줄러를 도입했습니다.

The Result

웹뷰 내부 오버레이뿐만 아니라 제어할 수 없던 네이티브 브릿지 팝업의 pending 상태까지 단일 비동기 흐름으로 완벽히 병합했습니다. 다단계 비동기 시퀀스를 하나의 원자적 작업 단위로 묶어 처리함으로써 비동기 꼬임 현상을 제거하였으며, UI 렌더링 및 도메인 로직이 전역 레이어로 격상되지 않고 호출처 내부에 유지되어 코드의 결합도가 낮아지고 응집도가 유지되었습니다.

Trade-off

큐의 정상 작동이 호출처 비동기 함수의 Promise settle(완료/거부) 여부에 전적으로 의존하므로, 예기치 않은 예외 발생이나 컴포넌트의 비정상적 언마운트 시 큐가 영원히 pending 상태로 멈추는 데드락 위험이 있습니다. 보수적인 설계 관점 상 화면 이탈 처리나 중복 차단 시의 예외 케이스(cancelled 상태 분기 등)를 꼼꼼하게 정의하고 관리해주어야 하는 부가적인 개발 비용이 수반됩니다.

03

Key Concepts

Concept · 01

Promise.withResolvers

Promise 인스턴스와 함께 해당 Promise의 운명을 바꿀 수 있는 resolve, reject 함수를 객체 형태로 한꺼번에 반환해 주는 ES2024 표준 API입니다. 비동기 제어권을 생성자 스코프 외부로 간편하게 끌어내기 위한 최신 문법입니다.

  • 사용자가 오버레이를 닫는 외부 시점을 캐치하여 비동기 큐 내부의 작업 진행 상황과 유연하게 싱크를 맞출 수 있도록 다리를 놓아줍니다.
  • 하위 호환성을 위해 9줄의 간단한 헬퍼 함수로 폴리필 형태로 선언해 시스템 전반의 큐 동기화 기반으로 활용했습니다.
Concept · 02

queueMicrotask

브라우저 엔진이 현재 수행 중인 동기 스크립트(태스크)를 완전히 끝마친 직후, 렌더링 파이프라인이나 이벤트 루프의 매크로태스크를 시작하기 전에 특정 마이크로태스크를 즉시 실행하도록 보장하는 웹 표준 API입니다.

  • 각 컴포넌트 마운트 시점에 산발적이고 동기적으로 호출되는 reserve 요청들이 곧바로 단독 실행되는 것을 지연시킵니다.
  • 한 차례의 동기 스크립트 동안 모인 요청들을 하나의 배치처럼 묶어서 우선순위에 따라 완벽히 정렬한 뒤 안전하게 순차 실행하도록 스케줄링을 돕습니다.
Concept · 03

실행 컨텍스트와 렉시컬 환경

자바스크립트 코드가 실행될 때 생성되는 내부 데이터 구조로, 선언된 변수 정보와 변수가 바인딩되어 있는 스코프(렉시컬 공간) 및 제어 흐름 위치를 저장합니다. 비동기 await 구문을 만나면 현재 함수 컨텍스트는 일시 중단되며 백업됩니다.

  • 큐 루프가 'await entry.fn()' 단계에서 사용자가 오버레이를 닫기 전까지 멈춰 있어도, 기존 실행 맥락이 보존되어 다음 엔트리로 정상 실행됩니다.
  • 이로 인해 모달을 연 호출처 내부의 내비게이션 콜백(pop 등)이나 보상 처리 함수 등이 전역 클로저에서 등록 시점 스냅샷 상태 그대로 안전하게 생존하여 동작할 수 있습니다.

Source

당근
당근
Engineering Blog

Published · September 30, 2026

Topics

ReactPromiseWebviewMicrotaskZustand