[Flutter] 웹뷰의 배신? webContentProcessTerminated 오류 완벽 정복 가이드
서론: 어느 날 갑자기 흰 화면만 보이는 웹뷰
안녕하세요, '개발하는남자'입니다.
플러터(Flutter)로 앱을 개발하다 보면 webview_flutter 플러그인을 사용할 일이 많습니다. 외부 웹페이지를 보여주거나, 웹 기반의 콘텐츠를 앱의 일부처럼 통합할 때 정말 유용한 플러그인이죠.
그런데 잘 동작하던 웹뷰가 어느 날 갑자기 아무것도 표시되지 않는 '흰 화면'으로 바뀌고, 디버그 콘솔에 webContentProcessTerminated라는 낯선 오류 메시지가 나타나는 경험, 해보신 적 있으신가요?
오늘은 바로 이 webContentProcessTerminated 오류가 왜 발생하며, 어떻게 우아하게 처리하여 앱의 안정성을 높일 수 있는지에 대해 깊이 파고들어 보겠습니다.
webContentProcessTerminated 오류란 무엇인가?
결론부터 말하자면, webContentProcessTerminated는 웹 콘텐츠를 렌더링하던 별도의 프로세스가 예기치 않게 종료되었다는 신호입니다.
웹뷰는 앱의 메인 프로세스가 아닌, 격리된 별도의 프로세스에서 웹 페이지의 렌더링을 담당합니다. 이는 웹페이지의 오류가 앱 전체의 안정성에 영향을 미치는 것을 막기 위한 좋은 설계입니다. 하지만 바로 이 렌더링 프로세스가 어떤 이유로든 '죽어버리면' 웹뷰는 더 이상 내용을 표시할 수 없게 되고, 우리는 webContentProcessTerminated 오류를 만나게 됩니다.
사용자 입장에서는 방금 전까지 잘 보이던 화면이 하얗게 변해버리는 당황스러운 경험을 하게 되죠.
주요 발생 원인 3가지
그렇다면 이 중요한 렌더링 프로세스는 왜 갑자기 종료되는 걸까요? 주된 원인은 다음과 같습니다.
1. 메모리 부족 (Out of Memory, OOM)
가장 흔한 원인입니다. 스마트폰의 운영체제(iOS, Android)는 시스템 메모리가 부족해지면, 자원을 확보하기 위해 우선순위가 낮은 프로세스를 강제로 종료합니다. 웹뷰의 렌더링 프로세스가 메모리를 많이 사용하고 있다면 바로 이 정리 대상이 되기 쉽습니다.
- 무거운 웹 페이지: 고해상도 이미지, 자동 재생 동영상, 복잡한 JavaScript 애니메이션이 가득한 페이지는 메모리를 많이 소모합니다.
- 웹페이지 자체의 메모리 누수: 잘 만들어지지 않은 웹페이지는 사용 시간이 길어질수록 계속해서 메모리 점유율이 상승하여 결국 시스템에 의해 종료될 수 있습니다.
- 여러 웹뷰 동시 사용: 앱 내에서 여러 웹뷰 인스턴스를 동시에 띄우는 것 역시 메모리 부담을 가중시킵니다.
2. 렌더러 프로세스 충돌
웹페이지를 구성하는 코드(HTML, CSS, JavaScript)에 문제가 있어 웹뷰의 렌더링 엔진이 처리하지 못하고 충돌하는 경우입니다. 특정 기기나 OS 버전에서만 발생하는 비호환성 문제일 수도 있습니다.
3. 앱의 백그라운드 전환
사용자가 앱을 잠시 백그라운드로 내렸을 때, OS는 자원 효율화를 위해 웹뷰 프로세스를 종료시킬 수 있습니다. 사용자가 다시 앱으로 돌아왔을 때 웹뷰가 비어있는 현상으로 나타납니다.
플랫폼별 동작 방식
이 오류는 iOS와 Android 모두에서 발생하지만, 내부적으로는 약간 다른 메커니즘을 가집니다.
- iOS:
WKWebView를 기반으로 하며, WebKit 프로세스가 종료되면WKNavigationDelegate의webViewWebContentProcessDidTerminate콜백이 호출됩니다. - Android: 별도의 렌더러 프로세스(Renderer Process)가 종료되면
WebViewClient의onRenderProcessGone콜백이 호출됩니다.
webview_flutter 플러그인은 이 두 플랫폼의 이벤트를 WebResourceErrorType.webContentProcessTerminated라는 통일된 오류 유형으로 개발자에게 전달해주는 역할을 합니다.
해결책: 방어적 코드로 앱 안정성 높이기
이 오류는 우리 앱 코드의 버그라기보다는 외부 요인(시스템, 웹 콘텐츠)에 의해 발생하는 경우가 많습니다. 따라서 언제든 발생할 수 있다는 것을 가정하고, 오류 발생 시 적절히 대응하는 '방어적인 코드'를 작성하는 것이 핵심입니다.
webview_flutter에서는 NavigationDelegate의 onWebResourceError 콜백에서 이 오류를 감지하고 처리할 수 있습니다.
대응 전략: 웹뷰 새로고침
가장 일반적이고 효과적인 방법은 프로세스가 종료된 것을 감지했을 때 웹뷰를 새로고침(reload()) 해주는 것입니다.
다음은 실제 적용 예시 코드입니다.
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class MyWebView extends StatefulWidget { final String url; const MyWebView({super.key, required this.url}); State<MyWebView> createState() => _MyWebViewState(); } class _MyWebViewState extends State<MyWebView> { late final WebViewController _controller; void initState() { super.initState(); _controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setNavigationDelegate( NavigationDelegate( onProgress: (int progress) { // 필요하다면 로딩 인디케이터를 표시할 수 있습니다. }, onPageStarted: (String url) {}, onPageFinished: (String url) {}, onWebResourceError: (WebResourceError error) { // 오류 유형을 정확히 확인합니다. if (error.errorType == WebResourceErrorType.webContentProcessTerminated) { // webContentProcessTerminated 오류 발생 시 // 자동으로 새로고침 하거나 사용자에게 알림을 표시합니다. debugPrint("Web Content Process Terminated: ${error.description}"); _handleTermination(); } }, ), ) ..loadRequest(Uri.parse(widget.url)); } // 오류 처리 로직 void _handleTermination() { // 1. 자동으로 즉시 새로고침 (가장 간단한 방법) _controller.reload(); /* // 2. 사용자에게 알리고 선택권을 주는 방법 (더 나은 UX) showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('페이지 오류'), content: const Text('웹 페이지를 표시하는 중 예기치 않은 문제가 발생했습니다. 페이지를 다시 불러올까요?'), actions: <Widget>[ TextButton( child: const Text('취소'), onPressed: () => Navigator.of(context).pop(), ), TextButton( child: const Text('새로고침'), onPressed: () { Navigator.of(context).pop(); _controller.reload(); // 웹뷰 새로고침 }, ), ], ); }, ); */ } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('WebView')), body: WebViewWidget(controller: _controller), ); } }
Write by :
개발하는남자
Developer
💬댓글 (0)
댓글을 작성하려면 로그인이 필요합니다.
댓글을 불러오는 중...