웹 애플리케이션 또는 웹페이지만 클라이언트 애플리케이션의 일부로 제공하려는 경우 WebView
를 사용하면 됩니다. WebView
클래스는 Android의 View
클래스의 확장으로, 웹페이지를 활동 레이아웃의 일부로 표시할 수 있게 해 줍니다. 탐색 컨트롤이나 주소 표시줄 등 완전히 개발된 웹브라우저의 기능은 전혀 포함되어 있지 않습니다. WebView
의 모든 작업은 기본적으로 웹페이지를 표시하는 것입니다.
일반적으로 WebView
를 사용하는 것이 도움이 되는 상황은 최종 사용자 계약이나 사용자 가이드 같은 업데이트해야 할 정보를 앱에서 제공하려는 경우입니다. Android 앱 내에서는 WebView
를 포함하는 Activity
를 만들어 온라인으로 호스팅된 문서를 표시하는 데 사용할 수 있습니다.
WebView
가 도움이 되는 또 다른 상황은 이메일과 같은 데이터를 가져오기 위해 항상 인터넷이 연결되어 있어야 하는 사용자에게 앱에서 데이터를 제공하는 경우입니다. 이 경우 네트워크 요청을 수행한 다음 데이터를 파싱하고 Android 레이아웃에서 렌더링하는 것보다 모든 사용자 데이터가 포함된 웹페이지를 표시하는 WebView
를 Android 앱에 빌드하는 것이 더 쉽습니다. 대신에 Android 기기에 맞춤화된 웹페이지를 설계한 다음 웹페이지를 로드하는 WebView
을 Android 앱에 구현할 수 있습니다.
이 문서에서는 WebView
를 시작하는 방법과 추가 작업을 수행하는 방법(예: 페이지 탐색 처리, 웹페이지에서 Android 앱의 클라이언트 측 코드로 자바스크립트를 결합)을 설명합니다.
앱에 WebView 추가
WebView
를 앱에 추가하려면 활동 레이아웃에서 <WebView>
요소를 포함하거나 onCreate()
에서 전체 활동 창을 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")
자바
WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.loadUrl("http://www.example.com");
onCreate()에서 WebView 추가
대신 활동의 onCreate()
메서드에서 WebView
를 앱에 추가하려면 다음과 유사한 로직을 사용합니다.
Kotlin
val myWebView = WebView(activityContext) setContentView(myWebView)
자바
WebView myWebView = new WebView(activityContext); setContentView(myWebView);
그런 다음 아래와 같이 페이지를 로드합니다.
Kotlin
myWebView.loadUrl("http://www.example.com")
자바
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")
자바
// 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");
참고: 이 HTML로 수행할 수 있는 작업에는 제한이 있습니다. 인코딩 옵션에 관한 자세한 내용은 loadData()
및 loadDataWithBaseURL()
을 참조하세요.
그러나 이 작업을 수행하려면 앱이 인터넷에 액세스할 수 있어야 합니다. 인터넷 액세스 권한을 받으려면 manifest 파일에서 INTERNET
권한을 요청합니다. 예:
<manifest ... > <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
이러한 작업이 웹페이지를 표시하는 기본 WebView
에 필요한 모든 사항입니다.
이 외에도 다음을 수정하여 WebView
를 맞춤설정할 수 있습니다.
WebChromeClient
로 전체 화면 지원 사용 설정. 이 클래스는WebView
가 창을 만들거나 닫고 자바스크립트 대화상자를 사용자에게 전송하는 등 호스트 앱의 UI를 변경하기 위한 권한을 필요로 할 때도 호출됩니다. 이 같은 상황에서 디버깅하는 방법을 자세히 알아보려면 웹 앱 디버깅을 참조하세요.WebViewClient
를 사용한 탐색 오류 또는 양식 제출 오류 등 콘텐츠 렌더링에 영향을 미치는 이벤트 처리. 이 서브클래스를 사용하여 URL 로드를 가로챌 수도 있습니다.WebSettings
를 수정하여 자바스크립트 사용 설정.WebView
에 삽입된 Android 프레임워크 객체에 자바스크립트를 사용하여 액세스.
WebView에서 자바스크립트 사용
WebView
에 로드하려는 웹페이지가 자바스크립트를 사용하는 경우 에 자바스크립트를 사용하도록 설정해야 합니다. 자바스크립트가 사용 설정되면 앱 코드와 자바스크립트 코드 간에 인터페이스를 만들 수도 있습니다.
자바스크립트 사용 설정
기본적으로 자바스크립트는 WebView
에서 사용 중지됩니다. 자바스크립트는 WebView
에 연결된 WebSettings
를 통해 사용 설정할 수 있습니다. getSettings()
로 WebSettings
를 가져온 다음 setJavaScriptEnabled()
로 자바스크립트를 사용 설정할 수 있습니다.
예:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.settings.javaScriptEnabled = true
자바
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true);
WebSettings
를 사용하면 다양하고 유용한 기타 설정에 액세스할 수 있습니다. 예를 들어 Android 앱의 WebView
전용으로 설계된 웹 애플리케이션을 개발하는 경우 setUserAgentString()
을 사용하여 맞춤 사용자 에이전트 문자열을 정의한 다음, 웹페이지에서 맞춤 사용자 에이전트를 쿼리하여 웹페이지를 요청하는 클라이언트가 실제로 Android 앱인지 확인할 수 있습니다.
자바스크립트 코드를 Android 코드에 결합
Android 앱의 WebView
전용으로 설계된 웹 애플리케이션을 개발하는 경우 자바스크립트 코드와 클라이언트 측 Android 코드 간에 인터페이스를 만들 수 있습니다.
예를 들어 자바스크립트 코드는 Dialog
를 표시할 때 자바스크립트의 alert()
함수를 사용하는 대신 Android 코드에서 메서드를 호출할 수 있습니다.
자바스크립트 코드와 Android 코드 간의 새 인터페이스를 결합하려면 addJavascriptInterface()
를 호출한 다음, 자바스크립트에 결합할 클래스 인스턴스와 자바스크립트가 클래스 액세스를 위해 호출할 수 있는 인터페이스 이름에 전달합니다.
예를 들어 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() } }
자바
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(); } }
주의: targetSdkVersion
을 17 이상으로 설정한 경우 자바스크립트에 사용 가능하게 할 모든 메서드에 @JavascriptInterface
주석을 추가해야 합니다(메서드도 공개 메서드여야 함). 주석을 제공하지 않으면 Android 4.2 이상에서 실행될 때 웹페이지가 메서드에 액세스할 수 없습니다.
이 예에서는 WebAppInterface
클래스에서 showToast()
메서드를 사용하여 웹페이지에 Toast
메시지를 만들 수 있도록 허용합니다.
addJavascriptInterface()
를 사용하여 WebView
에서 실행되는 자바스크립트에 이 클래스를 결합하고 인터페이스에 Android
라는 이름을 지정할 수 있습니다. 예:
Kotlin
val webView: WebView = findViewById(R.id.webview) webView.addJavascriptInterface(WebAppInterface(this), "Android")
자바
WebView webView = (WebView) findViewById(R.id.webview); webView.addJavascriptInterface(new WebAppInterface(this), "Android");
이렇게 하면 WebView
에서 실행되는 자바스크립트를 위한 Android
라는 인터페이스가 만들어집니다. 이제 웹 애플리케이션은 WebAppInterface
클래스에 액세스할 수 있습니다. 예를 들어 다음은 사용자가 버튼을 클릭하면 새 인터페이스를 사용하여 알림 메시지를 만드는 자바스크립트 및 HTML입니다.
<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()
메서드를 호출합니다.
참고: 자바스크립트에 결합된 객체는 객체가 생성된 스레드가 아닌 다른 스레드에서 실행됩니다.
주의: addJavascriptInterface()
를 사용하면 자바스크립트가 Android 앱을 제어할 수 있게 됩니다. 이는 유용한 기능일 수 있지만 동시에 위험한 보안 문제가 될 수 있습니다. WebView
에 포함된 HTML이 신뢰할 수 없는 HTML인 경우(예: HTML의 일부 또는 전체가 알 수 없는 사람이나 프로세스에 의해 제공된 경우) 공격자가 클라이언트 측 코드와 공격자가 선택한 모든 코드를 실행하는 HTML을 포함할 수 있습니다. 따라서 개발자는 WebView
에 표시되는 HTML 및 자바스크립트를 모두 작성한 경우가 아니면 addJavascriptInterface()
를 사용해서는 안 됩니다. 또한 사용자에게 WebView
내에서 개발자 소유가 아닌 다른 웹페이지로 이동하도록 허용해서는 안 됩니다. 대신 사용자의 기본 브라우저 애플리케이션에서 외부 링크를 열 수 있도록 허용합니다. 기본적으로 사용자의 웹브라우저는 모든 URL 링크를 엽니다. 따라서 위 섹션에서 설명한 것처럼 페이지 탐색을 처리하는 경우에만 주의합니다.
페이지 탐색 처리
사용자가 WebView
에서 웹페이지의 링크를 클릭하면 URL을 처리하는 앱이 Android에서 실행되는 것이 기본 동작입니다. 대개 기본 웹브라우저에 도착 URL이 열리고 로드됩니다. 하지만 링크가 WebView
내에서 열리도록 WebView
의 이 동작을 재정의할 수 있습니다. 그러면 WebView
에 의해 유지 관리되는 웹페이지 방문 기록을 통해 사용자가 앞뒤로 탐색할 수 있습니다.
참고: 보안상의 이유로 시스템의 브라우저 앱은 앱과 애플리케이션 데이터를 공유하지 않습니다.
사용자가 클릭한 링크를 열려면 setWebViewClient()
를 사용하여 WebView
에 WebViewClient
를 제공합니다. 예:
Kotlin
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient
= WebViewClient()
자바
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient
(MyWebViewClient);
이상입니다. 이제 사용자가 클릭한 모든 링크가 WebView
에 로드되었습니다.
클릭한 링크가 로드되는 위치를 좀더 세부적으로 설정하려면 shouldOverrideUrlLoading()
메서드보다 우선 적용되는 고유한 WebViewClient
를 만듭니다. 예:
Kotlin
private class MyWebViewClient : WebViewClient() {
override fun shouldOverrideUrlLoading
(view: WebView?, url: String?): Boolean {
if (Uri.parse(url).host == "www.example.com") {
// This is my web site, so do not override; let my WebView load the page
return false
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
startActivity(this)
}
return true
}
}
자바
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading
(WebView view, String url) {
if ("www.example.com".equals(Uri.parse(url).getHost())) {
// This is my website, so do not override; let my WebView load the page
return false;
}
// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
그런 다음 WebView
에 이 새 WebViewClient
의 인스턴스를 만듭니다.
Kotlin
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient
= MyWebViewClient()
자바
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient
(new MyWebViewClient());
이제 사용자가 링크를 클릭하면 시스템은 URL 호스트가 위에 정의된 대로 특정 도메인과 일치하는지 확인하는 shouldOverrideUrlLoading()
을 호출합니다. 일치하는 경우 메서드는 URL 로드를 재정의하지 않도록 false를 반환합니다(따라서 WebView
가 정상적으로 URL을 로드함). URL 호스트가 일치하지 않으면 URL(사용자의 기본 웹브라우저로 확인됨) 처리에 관한 기본 활동을 실행하기 위한 Intent
가 생성됩니다.
웹페이지 방문 기록 탐색
WebView
가 URL 로드를 재정의하면 방문한 웹페이지의 방문 기록이 자동으로 누적됩니다. goBack()
및 goForward()
를 사용하여 방문 기록을 통해 앞뒤로 탐색할 수 있습니다.
예를 들면 다음은 Activity
가 기기 뒤로 버튼을 사용하여 뒤로 탐색하는 방법입니다.
Kotlin
override funonKeyDown
(keyCode: Int, event: KeyEvent?): Boolean { // Check if the key event was the Back button and if there's history if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack
()) { myWebView.goBack() return true } // If it wasn't the Back key or there's no web page history, bubble up to the default // system behavior (probably exit the activity) return super.onKeyDown(keyCode, event) }
자바
@Override public booleanonKeyDown
(int keyCode, KeyEvent event) { // Check if the key event was the Back button and if there's history if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack
()) { myWebView.goBack
(); return true; } // If it wasn't the Back key or there's no web page history, bubble up to the default // system behavior (probably exit the activity) return super.onKeyDown(keyCode, event); } }
canGoBack()
메서드는 사용자가 방문할 웹페이지 방문 기록이 실제로 있으면 true를 반환합니다. 마찬가지로 개발자는 canGoForward()
를 사용하여 이전 페이지 방문 기록이 있는지 확인할 수 있습니다. 개발자가 이 확인을 하지 않으면 사용자가 마지막 방문 기록에 도달했을 때 goBack()
또는 goForward()
는 아무 반응을 하지 않습니다.
기기 구성 변경 처리
런타임 중에 사용자가 기기를 회전하거나 IME(입력 방식 편집기)를 닫는 등 기기의 구성이 변경되면 액티비티 상태가 변경됩니다. 이 변경으로 인해 WebView
객체의 액티비티가 삭제하고 새 액티비티가 생성될 뿐 아니라 삭제된 객체의 URL을 로드하는 새 WebView
객체도 생성됩니다.
액티비티의 기본 동작을 수정하려면 manifest에서 액티비티가 orientation
변경을 처리하는 방법을 변경하면 됩니다. 런타임 시 구성 변경을 처리하는 방법에 관한 자세한 내용은 구성 변경 처리를 참조하세요.
창 관리
기본적으로 새 창을 열어 달라는 요청은 무시됩니다. 이 점은 창이 자바스크립트에 의해 열리든지 링크의 타겟 속성에 의해 열리든지 관계없습니다. WebChromeClient
를 맞춤설정하여 창을 여러 개 열기 위한 고유 동작을 제공할 수 있습니다.
주의: 앱을 더 안전하게 유지하려면 팝업 및 새 창이 열리지 않도록 하는 것이 가장 좋습니다. 이 동작을 구현하는 가장 안전한 방법은 "true"
를 setSupportMultipleWindows()
에 전달하지만 setSupportMultipleWindows()
가 의존하는 onCreateWindow()
메서드를 재정의하지 않는 것입니다.
그러나 유의할 점은 이 로직을 사용할 경우 링크의 target="_blank"
를 사용하는 모든 페이지도 로드되지 않도록 차단된다는 점입니다.