앱에서 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 초기 버전까지 함께 대응하고 있었다는 점도 새롭게 알게 되었고 재밌는 이슈였다.
'Dev Log' 카테고리의 다른 글
| IntelliJ에서는 안 되고, Gradle에서는 되는 이유 (PKIX 에러 해결기) (0) | 2026.04.15 |
|---|---|
| 크롬 익스텐션 만들기 2 (1) | 2026.04.09 |
| 크롬 익스텐션 만들기 1 (0) | 2026.03.29 |
| 개발자가 알아야 하는 A/B 테스트와 관련 지표 (0) | 2026.03.22 |
| 날씨 기능 간단한 거 아냐? 공공 API 연동하기 삽질 과정 (0) | 2026.03.15 |