'This AdWidget is already in the Widget tree' — 특정 흐름 하나에서만 충돌했습니다

발행: (2026년 4월 3일 AM 05:01 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

오류

Flutter 앱에서 google_mobile_ads 를 사용해 배너 광고를 띄우고 있었습니다. 어느 날 MyPage 화면에서 다음과 같은 오류가 발생했습니다:

This AdWidget is already in the Widget tree

Make sure you are not using the same ad object in more than one AdWidget.

메시지는 명확했지만, 충돌은 일관되지 않았습니다.

관찰된 동작

흐름결과
탭을 정상적으로 전환충돌 없음
다른 페이지로 이동했다가 다시 돌아옴충돌 없음
주소 업데이트 → 메인 페이지로 돌아가 MyPage 탭 열기충돌 발생

주소 업데이트 흐름만이 오류를 일으켰습니다.

왜 발생했는가

앱은 BottomNavigationBarIndexedStack 을 사용합니다. 주소 업데이트 후 코드는 다음과 같이 메인 페이지로 돌아갔습니다:

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => MainPage()),
);

Navigator.push 는 스택 위에 새 페이지를 추가할 뿐, 기존 페이지를 제거하지 않습니다. 따라서 이전 MainPage(및 그 안의 MyPage) 가 메모리에 남아 있었습니다.

MainPage 에서 MyPage 를 열면, 이전 MyPage 의 상태에 아직 BannerAd 와 그 AdWidget 이 마운트된 상태가 남아 있습니다. 이제 두 개의 AdWidget 이 동일한 광고 리소스를 동시에 사용하려고 하면서 충돌이 발생한 것입니다.

일반적인 탭 전환에서는 MainPageMyPage 가 각각 하나씩만 존재하므로 문제가 나타나지 않았습니다.

해결 방법: 네비게이션 스택 정리

push 대신 pushAndRemoveUntil 을 사용합니다:

Navigator.pushAndRemoveUntil(
  context,
  MaterialPageRoute(builder: (_) => MainPage()),
  (route) => false,
);

이렇게 하면 이전 모든 라우트가 제거되어 오래된 MainPage 와 그 안의 MyPage 가 폐기됩니다. 사용자가 다시 MyPage 를 열 때 새로운 인스턴스가 생성되어 중복 AdWidget 문제가 사라집니다. 이 변경 후 충돌이 발생하지 않게 되었습니다.

광고를 위한 didUpdateWidget

원래 코드는 위젯이 업데이트될 때마다 배너 광고를 재생성했습니다:

@override
void didUpdateWidget(covariant MyPage oldWidget) {
  super.didUpdateWidget(oldWidget);
  _bannerAd?.dispose();
  _bannerAd = AdHelper.createBannerAd();
}

만약 이전 AdWidget 이 아직 트리에 남아 있었다면, 광고를 폐기하고 다시 생성하면서 동일한 중복 문제가 발생했습니다. 해결책은 이 로직을 완전히 제거하고 배너 광고를 initStatedispose 에서만 관리하는 것이었습니다:

@override
void initState() {
  super.initState();
  _bannerAd = AdHelper.createBannerAd();
}

@override
void dispose() {
  _bannerAd?.dispose();
  super.dispose();
}

플러그인이 규칙을 강제하는 방식

google_mobile_ads 플러그인은 광고가 이미 위젯 트리에 마운트되어 있는지를 추적합니다. ad_containers.dart 에서:

  • _AdWidgetState.initStateinstanceManager.isWidgetAdIdMounted() 를 통해 광고 ID 를 확인합니다.
  • 이미 마운트된 경우, build 메서드가 “already in the Widget tree” 오류를 발생시킵니다.
  • ID 는 _AdWidgetState.disposeunmountWidgetAdId 를 호출할 때만 해제됩니다.

따라서 하나의 BannerAd한 번에 하나의 AdWidget 만 사용할 수 있으며, 플러그인은 코드 레벨에서 이를 강제합니다.

교훈

Navigator.push 는 이전 페이지(및 해당 페이지가 보유한 네이티브 리소스)를 살아 있게 둡니다. 광고와 같이 반드시 폐기해야 하는 리소스가 있을 경우, 이전 페이지의 상태를 네비게이션 스택에서 제거해야 합니다. pushAndRemoveUntil(또는 다른 스택 정리 네비게이션 방법)을 사용해 올바르게 폐기하고 중복 AdWidget 충돌을 방지하세요.

0 조회
Back to Blog

관련 글

더 보기 »