본문 바로가기

Android

[Android] 안드로이드 스튜디오에서 웹뷰 스크롤 늘려보기

 

프로젝트를 진행하며 안드로이드 스튜디오에서 웹뷰를 띄우는 작업을 진행하게 되었습니다.

 

웹뷰에서는 input 칸에 유저의 정보를 입력하는 단계가 진행되었는데요, 프로젝트의 특성 상 상단 바, 네비게이션 영역들을 (inset들) 없애자 웹뷰에서 스크롤이 되지 않는 문제가 발생하였습니다.

 

웹뷰에서 스크롤이 되지 않으니 유저의 정보를 입력할 때 키보드에 input 칸이 가려져 입력 내용이 보이지 않고 매우 불편한 UX가 되었습니다. 그래서 웹뷰에 강제로 스크롤을 되게 만들어야 했습니다.

 

 

그런데..! 단순히 웹뷰에 제가 스크롤 기능을 넣는다고 스크롤이 되는 것이 아니였습니다.

그래서 문제점을 찾아 차차 스크롤의 원리부터 다시 이해해 보았습니다.

 

오늘 포스팅에서 이 과정을 차근 차근 설명해 보겠습니다.

 

 

 

🤔 원인을 찾아보자1 : 스크롤은 왜 필요할까?

XML에서 ScollView라는 기능을 제공하는데요, 이 경우에도 단순히 ScrollView를 추가한다고 해서 모든 화면이 무조건 스크롤이 되는 것은 아닙니다.

 

우선 스크롤을 추가하는 이유를 먼저 생각해 보았습니다. 스크롤은 화면의 영역보다 화면에 보여져야 하는 컨텐츠 내용이 더 많을 때 필요합니다.

 

예를 들어 스마트폰 화면이 세로 800px인데, 웹페이지 전체 길이가 2000px이면 한 번에 다 못 보여줍니다.

그럼 이때 모든 컨텐츠를 보여주기 위해 스크롤이 필요한 것입니다!

 

즉, 컨텐츠가 화면의 영역보다 많아야 스크롤이 동작하는데 사실상 제가 띄운 웹뷰에서는 화면의 영역보다 컨텐츠가 적었기 때문에 스크롤이 동작하지 않고, 스크롤이 되지 않으니 키보드 위로 input 칸이 올라올 수 없던 것입니다.

 

그렇다면 스크롤의 동작 원리를 알아야 이를 해결 할 수 있겠죠..?

 

 

 

🤔 원인을 찾아보자2 : 그렇다면 스크롤은 어떻게 동작할까?

운영체제(UI 시스템)는 화면(Viewport) + 컨텐츠 좌표 공간(Content Size)을 구분하고, 좌표 변환(Translation)을 통해 “어느 부분을 현재 보여줄지” 결정합니다.

 

 

  • 뷰포트(Viewport): 사용자가 실제로 볼 수 있는 “창(window)” 크기. → WebView의 높이.
  • 컨텐츠(Content): 내부적으로 그려진 전체 문서의 크기. → HTML의 scrollHeight.

 

다시 정리해보면 시스템은 컨텐츠 전체를 한꺼번에 화면에 띄우는 게 아니라 컨텐츠 좌표계에서 Viewport 크기만큼을 잘라서 보여줍니다. 사용자가 손가락으로 화면을 위아래로 스와이프하면, 이 잘라내는 위치(offset)가 바뀌는 것입니다.

 

 

예를 들어 화면 높이가 800px이고 문서 높이가 2000px이라면,

  • 처음에는 offset=0 → 컨텐츠 0~800px 구간이 보인다.
  • 스크롤을 400px 내리면 offset=400 → 컨텐츠 400~1200px 구간이 보인다.
  • 끝까지 내리면 offset = 2000-800=1200 → 컨텐츠 1200~2000px이 보인다.

스크롤의 본질은 “고정된 창(뷰포트)”을 움직이는 것이 아니라, “큰 종이(컨텐츠)”를 위아래로 밀어서 창을 통해 보이는 영역을 바꿔주는 과정이라고 할 수 있습니다.

 

그렇다면 제가 단순히 웹뷰에 스크롤을 붙였으니 스크롤이 되거라! 라고 한다고 스크롤이 되는 것이 아니라.. 컨텐츠의 길이를 늘려줘야 스크롤이 가능해진다는 것이겠죠?

 

 

 

😯 해결하기 : 웹뷰 길이를 늘려보자!

그렇다면 안드로이드 스튜디오에서 웹뷰 길이를 늘려야 하는데, 웹뷰 길이를 어떻게 늘리지..? 하는 생각이 들었습니다.

 

처음엔 웹뷰 하단에 강제로 마진을 주어 웹뷰 길이를 늘려보았습니다.

 

// 페이지 로딩 완료 후 JS 실행
webView.webViewClient = object : WebViewClient() {
    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)

        // 하단에 margin-bottom 200px 추가
        val js = """
            (function() {
                document.body.style.marginBottom = "200px";
            })();
        """.trimIndent()

        view?.evaluateJavascript(js, null)
    }
}

 

강제로 마진을 넣으니 웹뷰 길이가 길어져 스크롤은 가능해졌지만, 키보드를 올리는 경우에도 키보드 위에 강제 마진이 추가되었습니다 😅

 

 

그럼 다른 방법으로 컨텐츠 길이를 늘릴 수 있는 방법이 뭐가 있을지 고민하다가 Spacer를 추가하는 방식을 도입해보았습니다.

 

webView.webViewClient = object : WebViewClient() {
    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)

        // Spacer 삽입 스크립트
        val js = """
            (function() {
                if (!document.getElementById('__wv_spacer__')) {
                    var spacer = document.createElement('div');
                    spacer.id = '__wv_spacer__';
                    spacer.style.width = '100%';
                    spacer.style.height = '300px';   // 원하는 높이(px)
                    spacer.style.background = 'transparent';
                    spacer.style.pointerEvents = 'none';
                    document.body.appendChild(spacer);
                }
            })();
        """.trimIndent()

        view?.evaluateJavascript(js, null)
    }
}

 

Spacer를 사용하니 마진을 사용했을 때와 마찬가지로 스크롤이 잘 되었고, 추가적으로 키보드가 올라왔을 경우에도 키보드 위에 마진이 생기지 않아 원하는 모습대로 뷰를 그릴 수 있었습니다!

 

원하는 대로 뷰가 그려졌을 뿐만 아니라 Spacer를 사용한 가장 큰 이유는 상황에 따라 spacer의 height를 원하는 값으로 조절이 가능하기 때문이었습니다.

예를 들어 키보드가 올라올 때는 300px, 아닐 떄는 0px 등 동적으로 변경이 가능했습니다.

 

또한 margin은 부모 레이아웃 속성(overflow:hidden, height:100%)에 따라 무효화 될 수 있지만, Spacer는 DOM에 실제 요소가 추가되므로 거의 항상 동작하였습니다.

 

물론 DOM 증가 문제와 같은 단점도 있지만, 이를 해결하기 위해서는 우선 화면 종료 시 제거하는 방식으로 해결을 하였습니다. 다만 웹뷰에 대해 더 공부하고 좋은 방식이 있을지는 더 고민해봐야 할 것 같습니다. 🤔

 

 

 

📚 마무리

아무래도 안드로이드에 비해 웹뷰를 다룰 일이 많이 없어서 낯설기도 하고 학부생 시절에 웹 찍먹 이후 너무 오랜만의 웹이라 어려운 부분이 많았지만 새삼 다양한 분야에 대한 공부를 다시금 다짐하게 되었습니다.

 

사실 프로젝트를 진행하며 해당 웹뷰에 비교적 많은 시간을 쏟을 수 있는 환경이 아니었던지라 이게 완벽한 해결책일지는 모르겠다는 생각도 듭니다,, 🤔

 

혹시 더 좋은 방법이 있다면 편하게 댓글 남겨주시면 감사하겠습니다!