WebView에서 웹 앱 빌드

WebView를 사용하여 웹 애플리케이션 또는 웹페이지를 클라이언트 애플리케이션의 일부로 제공합니다. WebView 클래스는 Android View 클래스의 확장으로, 웹페이지를 활동 레이아웃의 일부로 표시할 수 있게 해줍니다. 탐색 컨트롤이나 주소 표시줄과 같이 완전히 개발된 웹브라우저의 기능은 포함되어 있지 않습니다. WebView는 기본적으로 웹페이지를 표시합니다.

WebView를 사용하면 최종 사용자 계약 또는 사용자 가이드와 같이 업데이트가 필요할 수 있는 정보를 앱에서 제공할 수 있습니다. Android 앱 내에서 WebView를 포함하는 Activity를 만든 다음 이를 사용하여 온라인으로 호스팅된 문서를 표시할 수 있습니다.

WebView는 앱에서 이메일과 같은 데이터를 검색하기 위해 인터넷 연결이 필요한 데이터를 사용자에게 제공할 때도 유용합니다. 이 경우 네트워크 요청을 실행한 후 데이터를 파싱하여 Android 레이아웃으로 렌더링하는 것보다 모든 사용자 데이터가 포함된 웹페이지를 표시하는 WebView를 Android 앱에서 빌드하는 것이 더 쉬울 수 있습니다. 대신 Android 지원 기기에 맞게 웹페이지를 디자인한 후 웹페이지를 로드하는 WebView을 Android 앱에 구현할 수 있습니다.

이 문서에서는 WebView를 시작하는 방법, 웹페이지의 JavaScript를 Android 앱의 클라이언트 측 코드에 결합하는 방법, 페이지 탐색을 처리하는 방법, WebView를 사용할 때 창을 관리하는 방법을 설명합니다.

이전 버전의 Android에서 WebView 사용

앱을 실행하는 기기에서 최신 WebView 기능을 안전하게 사용하려면 AndroidX Webkit 라이브러리를 추가합니다. 이전 플랫폼 버전에서는 사용할 수 없는 android.webkit API를 사용하기 위해 애플리케이션에 추가할 수 있는 정적 라이브러리입니다.

다음과 같이 build.gradle 파일에 추가합니다.

Kotlin

dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}

Groovy

dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

자세한 내용은 GitHub의 WebView를 참고하세요.

앱에 WebView 추가

앱에 WebView을 추가하려면 활동 레이아웃에 <WebView> 요소를 포함하거나 onCreate()에서 전체 Activity 창을 WebView로 설정하면 됩니다.

활동 레이아웃에 WebView 추가

레이아웃에서 앱에 WebView을 추가하려면 다음 코드를 활동의 레이아웃 XML 파일에 추가합니다.

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>

WebView에서 웹페이지를 로드하려면 다음 예와 같이 loadUrl()를 사용합니다.

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

onCreate()에서 WebView 추가

대신 활동의 onCreate() 메서드에서 WebView를 앱에 추가하려면 다음과 유사한 로직을 사용하세요.

Kotlin

val myWebView = WebView(activityContext)
setContentView(myWebView)

Java

WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

그런 다음 페이지를 로드합니다.

Kotlin

myWebView.loadUrl("http://www.example.com")

Java

myWebView.loadUrl("https://www.example.com");

또는 HTML 문자열에서 URL을 로드합니다.

Kotlin

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
val unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
myWebView.loadData(encodedHtml, "text/html", "base64")

Java

// Create an unencoded HTML string, then convert the unencoded HTML string into
// bytes. Encode it with base64 and load the data.
String unencodedHtml =
     "<html><body>'%23' is the percent code for ‘#‘ </body></html>";
String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(),
        Base64.NO_PADDING);
myWebView.loadData(encodedHtml, "text/html", "base64");

앱이 인터넷에 액세스할 수 있어야 합니다. 인터넷에 액세스하려면 다음 예와 같이 매니페스트 파일에서 INTERNET 권한을 요청합니다.

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

다음 중 한 가지 방법으로 WebView를 맞춤설정할 수 있습니다.

  • WebChromeClient를 사용하여 전체 화면 지원을 사용 설정합니다. 이 클래스는 WebView에서 창을 만들거나 닫거나 사용자에게 JavaScript 대화상자를 전송하는 등 호스트 앱의 UI를 변경하기 위한 권한이 필요할 때도 호출됩니다. 이 상황에서 디버깅하는 방법을 자세히 알아보려면 웹 앱 디버그를 참고하세요.
  • WebViewClient를 사용한 양식 제출 또는 탐색 오류와 같이 콘텐츠 렌더링에 영향을 미치는 이벤트 처리 이 서브클래스를 사용하여 URL 로드를 가로챌 수도 있습니다.
  • WebSettings를 수정하여 JavaScript를 사용 설정합니다.
  • WebView에 삽입한 Android 프레임워크 객체에 JavaScript를 사용하여 액세스

WebView에서 JavaScript 사용

WebView에 로드하려는 웹페이지가 JavaScript를 사용하는 경우 WebView에 JavaScript를 사용 설정해야 합니다. JavaScript를 사용 설정한 후 앱 코드와 JavaScript 코드 간에 인터페이스를 만들 수 있습니다.

자바스크립트 사용 설정하기

기본적으로 자바스크립트는 WebView에서 사용 중지됩니다. WebView에 연결된 WebSettings를 통해 사용 설정할 수 있습니다. getSettings()WebSettings를 검색한 후 setJavaScriptEnabled()로 JavaScript를 사용 설정합니다.

아래 예를 참고하세요.

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings를 사용하면 다양하고 유용할 수 있는 기타 설정에 액세스할 수 있습니다. 예를 들어 Android 앱의 WebView 전용으로 설계된 웹 애플리케이션을 개발하는 경우 setUserAgentString()로 맞춤 사용자 에이전트 문자열을 정의한 다음 웹페이지에서 맞춤 사용자 에이전트를 쿼리하여 웹페이지를 요청하는 클라이언트가 Android 앱인지 확인할 수 있습니다.

Android 코드에 JavaScript 코드 결합

Android 앱의 WebView 전용으로 설계된 웹 애플리케이션을 개발하는 경우 JavaScript 코드와 클라이언트 측 Android 코드 간에 인터페이스를 만들 수 있습니다. 예를 들어 JavaScript 코드는 JavaScript의 alert() 함수를 사용하는 대신 Android 코드의 메서드를 호출하여 Dialog를 표시할 수 있습니다.

JavaScript와 Android 코드 간에 새 인터페이스를 결합하려면 addJavascriptInterface()를 호출하여 JavaScript에 결합할 클래스 인스턴스와 JavaScript가 클래스에 액세스하기 위해 호출할 수 있는 인터페이스 이름을 전달합니다.

예를 들어 Android 앱에 다음 클래스를 포함할 수 있습니다.

Kotlin

/** Instantiate the interface and set the context.  */
class WebAppInterface(private val mContext: Context) {

    /** Show a toast from the web page.  */
    @JavascriptInterface
    fun showToast(toast: String) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show()
    }
}

Java

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context. */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page. */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}

이 예에서는 WebAppInterface 클래스를 통해 웹페이지에서 showToast() 메서드를 사용하여 Toast 메시지를 만들 수 있습니다.

다음 예와 같이 addJavascriptInterface()를 사용하여 WebView에서 실행되는 JavaScript에 이 클래스를 결합할 수 있습니다.

Kotlin

val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")

Java

WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

이렇게 하면 WebView에서 실행되는 JavaScript를 위한 Android라는 인터페이스가 생성됩니다. 이제 웹 애플리케이션은 WebAppInterface 클래스에 액세스할 수 있습니다. 예를 들어 다음은 사용자가 버튼을 탭할 때 새 인터페이스를 사용하여 토스트 메시지를 만드는 일부 HTML 및 JavaScript입니다.

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script type="text/javascript">
    function showAndroidToast(toast) {
        Android.showToast(toast);
    }
</script>

자바스크립트에서 Android 인터페이스를 초기화할 필요가 없습니다. WebView는 웹페이지에서 사용할 수 있도록 자동으로 만듭니다. 따라서 사용자가 버튼을 탭하면 showAndroidToast() 함수는 Android 인터페이스를 사용하여 WebAppInterface.showToast() 메서드를 호출합니다.

페이지 탐색 처리

사용자가 WebView의 웹페이지에서 링크를 탭하면 기본적으로 Android는 URL을 처리하는 앱을 실행합니다. 일반적으로 기본 웹브라우저가 도착 URL을 열고 로드합니다. 그러나 링크가 WebView 내에서 열리도록 WebView의 이 동작을 재정의할 수 있습니다. 그런 다음 사용자가 WebView에서 관리하는 웹페이지 기록을 통해 앞뒤로 이동하도록 할 수 있습니다.

사용자가 탭한 링크를 열려면 setWebViewClient()를 사용하여 WebViewWebViewClient를 제공합니다. 사용자가 탭하는 모든 링크가 WebView에 로드됩니다. 클릭된 링크가 로드되는 위치를 더 세부적으로 관리하려면 shouldOverrideUrlLoading() 메서드를 재정의하는 자체 WebViewClient를 만드세요. 다음 예에서는 MyWebViewClientActivity의 내부 클래스라고 가정합니다.

Kotlin

private class MyWebViewClient : WebViewClient() {

    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        if (Uri.parse(url).host == "www.example.com") {
            // This is your website, so don't override. Let your WebView load
            // the page.
            return false
        }
        // Otherwise, the link isn't for a page on your site, so launch another
        // Activity that handles URLs.
        Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
            startActivity(this)
        }
        return true
    }
}

Java

private class MyWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        if ("www.example.com".equals(request.getUrl().getHost())) {
      // This is your website, so don't override. Let your WebView load the
      // page.
      return false;
    }
    // Otherwise, the link isn't for a page on your site, so launch another
    // Activity that handles URLs.
    Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
    startActivity(intent);
    return true;
  }
}

그런 다음 WebView에 이 새 WebViewClient의 인스턴스를 만듭니다.

Kotlin

val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()

Java

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());

이제 사용자가 링크를 탭하면 시스템은 shouldOverrideUrlLoading() 메서드를 호출하여 이전 예에 정의된 대로 URL 호스트가 특정 도메인과 일치하는지 확인합니다. 일치하면 메서드가 false를 반환하고 URL 로드를 재정의하지 않습니다. 이렇게 하면 WebView에서 평상시처럼 URL을 로드할 수 있습니다. URL 호스트가 일치하지 않으면 사용자의 기본 웹브라우저로 확인되는 URL을 처리하기 위한 기본 Activity를 실행하기 위해 Intent가 생성됩니다.

커스텀 URL 처리

WebView는 커스텀 URL 스키마를 사용하는 리소스를 요청하고 링크를 확인할 때 제한사항을 적용합니다. 예를 들어 shouldOverrideUrlLoading() 또는 shouldInterceptRequest()와 같은 콜백을 구현하면 WebView는 유효한 URL에 관해서만 콜백을 호출합니다.

예를 들어 다음과 같은 링크의 경우 WebViewshouldOverrideUrlLoading() 메서드를 호출하지 않을 수 있습니다.

<a href="showProfile">Show Profile</a>

앞의 예와 같이 잘못된 URL은 WebView에서 일관되지 않게 처리되므로 올바른 형식의 URL을 대신 사용하는 것이 좋습니다. 조직에서 제어하는 도메인에 커스텀 스키마 또는 HTTPS URL을 사용할 수 있습니다.

이전 예와 같이 링크에 간단한 문자열을 사용하는 대신 다음과 같은 맞춤 스키마를 사용할 수 있습니다.

<a href="example-app:showProfile">Show Profile</a>

그런 다음 shouldOverrideUrlLoading() 메서드에서 이 URL을 다음과 같이 처리할 수 있습니다.

Kotlin

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
const val APP_SCHEME = "example-app:"

override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
    return if (url?.startsWith(APP_SCHEME) == true) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8")
        respondToData(urlData)
        true
    } else {
        false
    }
}

Java

// The URL scheme must be non-hierarchical, meaning no trailing slashes.
private static final String APP_SCHEME = "example-app:";

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith(APP_SCHEME)) {
        urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8");
        respondToData(urlData);
        return true;
    }
    return false;
}

shouldOverrideUrlLoading() API는 주로 특정 URL의 인텐트를 실행하는 데 사용됩니다. API를 구현할 때는 WebView에서 처리하는 URL에 false를 반환해야 합니다. 그러나 인텐트를 실행하는 것으로만 제한되지는 않습니다. 이전 코드 샘플에서 인텐트 시작을 모든 맞춤 동작으로 대체할 수 있습니다.

WebView가 URL 로드를 재정의하면 방문한 웹페이지 기록이 자동으로 누적됩니다. goBack()goForward()를 사용하여 기록을 통해 앞뒤로 이동할 수 있습니다.

예를 들어 다음은 Activity에서 기기의 뒤로 버튼을 사용하여 뒤로 이동하는 방법을 보여줍니다.

Kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check whether the key event is the Back button and if there's history.
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it isn't the Back button or there isn't web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event)
}

Java

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    // Check whether the key event is the Back button and if there's history.
    if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
        myWebView.goBack();
        return true;
    }
    // If it isn't the Back button or there's no web page history, bubble up to
    // the default system behavior. Probably exit the activity.
    return super.onKeyDown(keyCode, event);
}

앱에서 AndroidX AppCompat 1.6.0 이상을 사용하는 경우 이전 스니펫을 훨씬 더 단순화할 수 있습니다.

Kotlin

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}

Java

onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack();
    }
}

canGoBack() 메서드는 사용자가 방문할 웹페이지 기록이 있는 경우 true를 반환합니다. 마찬가지로 canGoForward()를 사용하여 전달 기록이 있는지 확인할 수 있습니다. 이 검사를 실행하지 않으면 사용자가 기록의 끝에 도달한 후에 goBack()goForward()가 아무 작업도 실행하지 않습니다.

기기 구성 변경 처리

런타임 중에는 사용자가 기기를 회전하거나 IME(입력 방식 편집기)를 닫는 등 기기 구성이 변경되면 활동 상태가 변경됩니다. 이러한 변경사항으로 인해 WebView 객체의 활동이 소멸되고 새 활동이 생성되며, 소멸된 객체의 URL을 로드하는 새로운 WebView 객체도 생성됩니다. 활동의 기본 동작을 수정하려면 매니페스트에서 활동이 orientation 변경을 처리하는 방식을 변경하면 됩니다. 런타임 시 구성 변경을 처리하는 방법을 자세히 알아보려면 구성 변경 처리를 참조하세요.

창 관리

기본적으로 새 창을 열어 달라는 요청은 무시됩니다. 이는 객체가 자바스크립트로 열리거나 링크의 타겟 속성에 의해 열릴 때 항상 적용됩니다. WebChromeClient를 맞춤설정하여 창을 여러 개 여는 고유한 동작을 제공할 수 있습니다.

앱을 더 안전하게 유지하려면 팝업 및 새 창이 열리지 않도록 하는 것이 가장 좋습니다. 이 동작을 구현하는 가장 안전한 방법은 "true"setSupportMultipleWindows()에 전달하되 setSupportMultipleWindows()가 의존하는 onCreateWindow() 메서드를 재정의하지 않는 것입니다. 이 로직은 링크에서 target="_blank"를 사용하는 페이지가 로드되지 않도록 합니다.