본문 바로가기

Dev Log

Android WebView safe-area 이슈 대응 (Chromium 버전 변화 포함)

728x90

앱에서 WebView를 사용할 때 safe-area 처리는 전체 레이아웃에 직접적인 영향을 주기 때문에, 매우 민감하고 까다로운 문제다.

 

최근 Chromium 업데이트 이후

Android WebView에서도 env(safe-area-inset-*) 값이 정상적으로 내려오기 시작하면서 기존 방식에서 예상치 못한 문제가 발생했다.

이번 글에서는 그 문제와 해결 과정을 정리해봤다.

기존 구조

기존에는 Android에서 safe-area 값이 항상 0으로 내려왔다.

그래서 네이티브에서 다음과 같은 방식으로 처리하고 있었다.

v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)

구조를 보면

OS → WindowInsets → Android View padding 적용
  • WebView 내부는 inset 값을 알 수 없음 ❌
  • CSS에서는 safe-area 개념이 없음
  • 바깥 View에 padding을 줘서 대응

👉 즉, safe-area 값의 출처가 CSS 레벨에서는 보이지 않는 상태

Chromium 업데이트 이후 변화

Android WebView의 동작이 바뀌었다.

기존: env(safe-area-inset-top) = 0
변경: env(safe-area-inset-top) = 실제 값 (ex. 20px)

이제 Android에서도 iOS처럼 CSS에서 safe-area 값을 직접 사용할 수 있게 됨

문제 발생 (double spacing)

기존 방식 + 새로운 동작이 겹치면서 문제가 생겼다.

Android native padding + env(safe-area-inset-top)

👉 safe-area가 두 번 적용됨

해결 방향

앱에서 Chromium 버전에 따라 이전 번의 경우 기존 로직을 태우고 이후 버전은 safe-inset-area 가 적용되도록 하는 방법에 대해서 고민해봤으나, 또 특정 버전 139에서 리그레션이 있어서 이렇게 분기를 태우기에는 문제가 있었다. 따라서 웹뷰쪽에서 기존 css 코드를 수정하는 방향으로 테스트를 진행했다.

1️⃣ Android에서 safe-area 계산

safeAreaInsets = SafeAreaInsets(
    top = systemBars.top / density,
    bottom = systemBars.bottom / density,
    left = systemBars.left / density,
    right = systemBars.right / density,
)

2️⃣ WebView에 CSS 변수로 주입

document.documentElement.style.setProperty('--safe-area-inset-top', '${safeAreaInsets.top}px');

👉 Android → CSS 변수로 전달

3️⃣ CSS에서 통합 처리

top: calc(var(--safe-area-inset-top, env(safe-area-inset-top)) + $top);
Android → var(--safe-area-inset-top)
iOS → env(safe-area-inset-top)

위처럼 처리하게 되면 Android에서는 주입된 —safe-area-inset-top을 참고하게 되고 그 값을 참고 못하는 경우 fallback으로 env(safe-area-inset-top)값이 사용되게 된다.

  • Android: 주입값 사용
  • iOS: env 값 사용

기존 레거시 코드

기존 레거시 코드를 보면서 의아한 부분이 있었는데 safe-area를 적용한 모든 영역에 아래 코드가 반복적으로 들어가 있었기 때문이다. 이 부분에 대해 검색해보니 iOS 11.2 이후에 env(safe-area-inset-*) 방식으로 변경되어 이전 버전에 대해 대응하기 위해 constant를 사용하고 있었던 것이었다.

top: calc(constant(safe-area-inset-top) + $top);
top: calc(env(safe-area-inset-top) + $top);

이 부분을 처리하면서 CSS var()를 지원하는 브라우저 환경뿐만 아니라, iOS와 Android에서 env(safe-area-inset-top)이 각각 어떻게 동작하고 있었는지에 대해서도 이해할 수 있었다. 또한 constant 형식을 사용해 iOS 11 초기 버전까지 함께 대응하고 있었다는 점도 새롭게 알게 되었고 재밌는 이슈였다.

728x90