화면 제어를 넘어 비동기 동시성으로: 당근 동네걷기의 오버레이 아키텍처 전환

ARCH-B

ARCH-B

당근 기술 블로그 기반 · 2026년 9월 30일 · 6분 분량

0

한눈에 보는 핵심요약

당근 동네걷기 서비스는 웹뷰와 네이티브가 뒤섞여 동시다발적으로 발생하는 오버레이 UI의 순서 제어 문제를 해결하기 위해, 추상화 단위를 UI 데이터에서 비동기 함수 실행 단위로 전환하고 자바스크립트 마이크로태스크 스케줄링을 접목했습니다.

  • 웹뷰 내부 오버레이와 네이티브 OS 권한 팝업이 비동기적으로 동시에 발화하면서 발생하던 UI 중첩 및 플래그 증식 문제를 해결했습니다.
  • 전역 컴포넌트 등록 방식의 응집도 저하 한계를 극복하기 위해, 큐에 줄 세우는 대상을 오버레이 데이터가 아닌 비동기 함수 자체로 재정의했습니다.
  • Promise의 대기 상태를 오버레이 노출 생명주기와 일치시키고 ES2024 표준 Promise.withResolvers 패턴을 적용해 복합 흐름의 원자성을 확보했습니다.
화면 제어를 넘어 비동기 동시성으로: 당근 동네걷기의 오버레이 아키텍처 전환
이미지 제공: 당근 기술 블로그 · 원출처 기사

웹뷰와 네이티브가 뒤섞인 화면에서 발생한 오버레이 중첩 현상

당근 앱 내 웹뷰 기반 서비스인 '동네걷기'는 걸음 수를 측정하고 리워드를 지급하는 구조상 홈 화면 진입 시점에 다양한 안내 창이 한꺼번에 집중되는 화면 구조를 갖고 있습니다. 사용자 화면에는 OS 신체 활동 권한 팝업, 웹뷰 기반의 권한 안내 모달, 알림 설정 바텀시트, 온보딩 코치마크와 보상 애니메이션, 연속 방문 안내 및 각종 이벤트 프로모션 등 수많은 오버레이가 각자의 트리거 조건에 따라 독립적으로 발화했습니다.

문제는 이 오버레이들이 사용자의 직접적인 클릭 상호작용이 아니라 앱 버전 확인, 서버 응답, 네이티브 상태 조회 등 비동기 맥락이 완료되는 즉시 스스로 노출을 시도한다는 점이었습니다. 기존에는 이전 모달의 종료 시점(onClose 핸들러)에 다음 모달을 직접 연결하는 체이닝 방식을 취했으나, 이벤트성 프로모션이 추가될 때마다 분기 로직과 프롭 전달 체인을 전면 수정해야 했습니다. 특히 걸음 수 조회를 위해 네이티브 브릿지를 호출할 때 OS가 띄우는 권한 팝업은 웹뷰 밖 네이티브 영역에 있어 웹뷰가 팝업의 개폐 여부를 조회할 수 없고, 오직 브릿지가 반환하는 Promise가 pending 상태라는 신호에만 의존해야 하는 구조적 한계가 존재했습니다.

전역 컴포넌트 레지스트리 방식의 한계와 응집도 결손

문제를 해결하기 위해 개발팀이 초기에 검토했던 방안은 외부 라이브러리 모델과 유사하게 오버레이 컴포넌트를 전역 레지스트리에 사전 등록(register)하고, 각 호출처에서 식별자(ID)와 우선순위를 기반으로 제어하는 전역 큐잉 구조였습니다. 오버레이를 우체국 창구처럼 대기열에 세워 한 번에 하나의 UI만 노출하고, 중요도에 따라 대기 순서를 재배치하는 멘탈 모델이었습니다.

하지만 내부 설계 검토 과정에서 도메인 의존성이 높은 오버레이를 전역 상태 레이어로 끌어올리는 방식의 트레이드오프가 지적되었습니다. 호출처는 등록만 담당하고 실제 렌더링은 전역 호스트에서 수행되면서, 단일 오버레이의 흐름을 파악하기 위해 호출처 컴포넌트, 렌더 매핑 테이블, 정책 카탈로그를 동시에 추적해야 하는 구조적 파편화가 발생했기 때문입니다. 더욱이 호출처의 네비게이션이나 보상 처리 함수가 포함된 클로저가 전역 상태에 캡처되면서 컴포넌트 언마운트나 리렌더 시점과의 불일치가 일어났고, 컴포넌트가 지녀야 할 응집도가 크게 훼손되는 결과를 낳았습니다.

ARCH.B 분석

오버레이 제어에서 비동기 작업 동시성 제어로의 문제 재정의

이번 사례에서 주목할 지점은 화면에 보이는 오버레이 UI 그 자체를 관리 대상으로 삼지 않고, 배경에서 비동기적으로 시작되는 '작업의 동시성 제어'로 문제의 본질을 전환했다는 점입니다. 오버레이는 비동기 작업이 화면에 시각화된 형태에 불과하며, 실제 통제해야 하는 본체는 여러 비동기 발화의 순서와 대기 상태라는 점에 착안했습니다.

이러한 관점의 전환은 큐에 저장하는 추상화 단위를 '오버레이 속성 데이터'에서 '실행할 비동기 함수(trigger)'로 격상시키는 결과로 이어졌습니다. 우선순위 큐는 내부에서 어떤 UI가 렌더링되는지 알 필요 없이 오직 전달받은 비동기 함수가 완료될 때까지 실행을 대기하는 역할만 수행합니다. 이로 인해 UI 렌더링과 비즈니스 로직을 개별 컴포넌트 내부에 온전히 보존하면서도 큐 동기화를 처리할 수 있어, 전역 레지스트리 모델이 야기했던 응집도 저하 문제를 말끔히 덜어냈습니다.

ARCH.B 분석

표준 Promise 상태 머신과 ES2024 withResolvers 패턴의 활용

함수 단위 추상화의 실무적 강점은 자바스크립트 엔진의 런타임 스케줄링 메커니즘을 그대로 활용할 수 있다는 데 있습니다. 오버레이가 화면에 노출되어 있는 기간을 비동기 함수의 Promise가 pending 상태인 기간으로 정의함으로써, 단순한 'await entry.fn()' 구문 하나로 다음 대기열 작업의 실행 시점을 제어할 수 있게 되었습니다. 실행 순서와 대기는 마이크로태스크 큐가 보장하므로 명령형 동기화 코드를 대폭 줄일 수 있습니다.

이는 웹뷰 바깥의 네이티브 OS 팝업을 포섭하는 결정적 계기가 되었습니다. 네이티브 브릿지 호출이 반환하는 pending 상태의 Promise를 trigger 함수 내부에서 await 처리하기만 하면, 브릿지가 응답을 반환할 때까지 큐의 다음 엔트리가 실행되지 않고 대기합니다. 아울러 권한 요청 후 모달 분기로 이어지는 복합 플로우를 단일 비동기 함수 내 시퀀스로 묶음으로써 별도 상태 머신 없이도 작업의 원자성을 완벽히 보호할 수 있습니다. 이때 ECMAScript 2024 표준으로 등재된 Promise.withResolvers() 패턴을 적용하면 생성자 외부 스코프에서 promise와 resolve를 분리 제어할 수 있어, 비동기 디퍼드(deferred) 큐잉 구현의 코드 가독성과 안정성을 극대화할 수 있습니다.

ARCH.B 분석

하이브리드 웹뷰 환경을 위한 실무적 시사점과 검증 과제

웹뷰와 네이티브가 결합된 하이브리드 앱 환경에서는 플랫폼 간 생명주기 격차로 인해 UI 제어 실패가 빈번하게 발생합니다. 당근 동네걷기팀의 시도는 라이브러리를 무작정 도입하기보다 브라우저 런타임의 기본 동작인 Promise와 마이크로태스크 큐를 기반으로 아키텍처를 재설계함으로써, 외부 네이티브 팝업과 내부 웹 모달의 실행 맥락을 단일 파이프라인으로 통합하는 실무적 해법을 제시했습니다.

다만 공개된 자료에는 해당 구조 도입 이후 렌더링 지연 시간, 메모리 점유율, 오버레이 노출 실패율 등 정량화된 지표는 포함되지 않았습니다. 실무에 이 같은 우선순위 비동기 큐를 도입할 때는 특정 비동기 작업이 resolve되지 않고 무한 대기 상태에 빠질 경우 전체 UI 파이프라인이 차단되는 현상을 방지하기 위해 엄격한 타임아웃 및 예외 처리 정책을 병행 수립해야 할 것으로 판단됩니다.

출처

당근 기술 블로그의 원출처와 본문에 표시한 참고자료를 바탕으로 한국어로 재구성했습니다. ‘ARCH.B 분석’은 자료를 읽고 덧붙인 편집 해석입니다.

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

Share

Related

함께 읽기 좋은 글