[Flutter] 안드로이드 WebView 이니시스 ERR_UNKNOWN_URL_SCHEME 오류 해결

💛DabongLog·2022년 7월 13일
7

📌이슈 해결

목록 보기
2/5
post-thumbnail

Flutter WebView 내에서 KG이니시스를 연동하여 결제 시스템 구현중 각 은행 사 어플로 인텐트 처리가 되지 않는 오류가 발생했다.


아이폰의 경우 Info.plist 파일 내 LSApplicationQueriesSchemes영역에 은행사들의 scheme값들을 넣어주면 별도의 추가적인 처리 없이 작동이 잘 된다. 문제는 안드로이드다..!

WebView 에서 띄운 것이기도 하고 네이티브에서 뭘 설정해야 할 게 있을까 웹쪽에서 해결해야하는 게 아닌가 했지만! 클라이언트 쪽에서 처리해야 하는 로직이 필수적이다!

먼저, 은행 사 어플로 이동하는 버튼 클릭 시 내가 만난 오류 화면이다.

flutter는 http 또는 https로 시작하는 url만 url 처리를 할 수 있다. 하지만 은행 사 어플을 불러오는 url은 intent:// 등 http/https가 아닌 주소로 시작하기 때문에 발생하는 오류페이지다.
이를 해결하기 위해선 'MethodChannel'을 이용해야 한다.

아래의 링크를 통해 공식 홈페이지에서 아키텍처 구조와 MethodChannel 사용 예까지 자세한 정보를 확인할 수 있다.
Platform Channel 공식 홈페이지 이동하기


메서드 채널을 간단히 말하자면, 크로스 플랫폼인 플러터에서 네이티브 안드로이드에 접근하게 도와주는 역할을 한다고 보면 된다.

채널을 이용하기 위해서 다음과 같이 MethodChannel을 정의한다.

static const platform = MethodChannel('fcm_default_channel');

적용시켜 보면, flutter webview의 navigationDelegate가 url이 변경되는 걸 listen하고 있으면서 메서드 채널을 통해 그 url을 네이티브에서 받아 flutter 내에서 다룰 수 있는 url로 파싱한 후, flutter는 그 url값을 받아 navigate 시키는 것이다.

1. navigationDelegate 작성

navigationDelegate: (request) async{
          LogPrint('navigate url : ${request.url}');

          // 2 채널이용
          if(!request.url.startsWith('http') && !request.url.startsWith('https')) {

            if(Platform.isAndroid) {
              getAppUrl(request.url.toString());

              return NavigationDecision.prevent;

            }else if(Platform.isIOS){

              if (await canLaunchUrl(Uri.parse(request.url))) {
                LogPrint('navigate url : ${request.url}');

                await launchUrl(Uri.parse(request.url),);

                return NavigationDecision.prevent;
              }
            }

          }

          return NavigationDecision.navigate;

        },

IOS의 경우 launchUrl를 사용하여, Android의 경우 채널을 이용하여 처리한다. 이때! return에 NavigationDecision은 꼭 Prevent를 적용해야 한다.

2. getAppUrl 함수 작성

Future getAppUrl(String url) async {
    await platform.invokeMethod('getAppUrl', <String, Object>{'url': url}).then((value) async{
      LogPrint('paring url : $value');

      if(value.toString().startsWith('ispmobile://')) {
        await platform.invokeMethod('startAct', <String, Object>{'url': url}).then((value) {
          LogPrint('parsing url : $value');

          return;
        });
      }

      if (await canLaunchUrl(Uri.parse(value))) {
        await launchUrl(Uri.parse(value),);

        return;
      } else {

        showNotiDialog(context, '해당 앱 설치 후 이용바랍니다.');

        return;
      }

    });
  }

invokeMethod를 활용하여 해당 url을 네이티브로 보내고 future value값을 체크하는 기능을 담고 있다.


3. 네이티브 코드 작성 (MainActivity.kt)

private var CHANNEL = "fcm_default_channel"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        GeneratedPluginRegistrant.registerWith(flutterEngine!!)
        MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
        
            when {
                call.method.equals("getAppUrl") -> {
                    try {
                    
                        val url: String = call.argument("url")!!
                        val intent = Intent.parseUri(url, URI_INTENT_SCHEME)
                        result.success(intent.dataString)

                    } catch (e: URISyntaxException) {
                        result.notImplemented()
                    } catch (e: ActivityNotFoundException) {
                        result.notImplemented()
                    }
                }
        }
    }

이렇게 하면 result.success(intent.dataString)값으로 파싱된 url 값이 가고 2번에서 정의한 getAppUrl함수에서 future value값으로 사용할 수 있다.





이 오류를 해결함에 앞서 구글에 있는 레퍼런스를 모두 보다시피 했지만 이렇다하게 작성된 글을 찾을 수 없었고.. 나도 결제 시스템 도입을 처음하다 보니 정말 애를 많이 먹었다. 나와 같은 오류로 애먹고 있는 분들의 시간을 아껴주고자 작성해본다..
해결하고 나면 이렇게 간단한 것을 😫


오늘도 성장중 ...

profile
모바일 어플리케이션 개발자 (Flutter, iOS, Android)

13개의 댓글

comment-user-thumbnail
2022년 8월 29일

안녕하세요. 많은 도움이 됐습니다. 감사합니다.!!
혹시 해당 기능을 이용해서 인텐트를 띄우는 것은 잘 되나
현대카드 -> 일반결제 같은 경우 팝업형태로 백신 앱이 실행되는데
이 과정에서 오류가 발생합니다. 혹시 같은 문제를 겪으신적이 있을까요..?
오랜 시간 찾아본 결과.. 한분은 크롬99 이상일 때 그러한 현상이 일어난다고 하시던데..
도움좀 받을 수 있을까요..

"다음 이유로 intent://%7B"CD":"1c3326" 이런식의 오류 웹페이지가 뜨네요ㅠㅠ..
기술팀에 문의도 해봤지만,, 아무래도 플러터라 시원한 답을 못해주는 것 같네요..

좋은 자료 감사합니다.

1개의 답글
comment-user-thumbnail
2022년 10월 21일

안녕하세요~!
flutter 웹뷰에서 결제관련 자료 찾다가 방문하였습니다.

혹시 아이폰에서는 info.plist만 추가해도 정상적으로 작동 잘 하셨나요?
저같은 경우에는 현대카드를 예로들면
-canOpenURL: failed for URL: "hdcardappcardansimclick://appcard?acctid=202210201739132058335170737671" - error: "The operation couldn’t be completed. (OSStatus error -10814.)"

와같이 로그가 남으며 앱이 자동으로 뜨지가 않아서요~
hdcardappcardansimclick는 info.plist에 등록이 되어있는 상황입니다.

혹시 다른작업을 하신게 있다면 조언 부탁드립니다.

감사합니다~!

1개의 답글
comment-user-thumbnail
2022년 11월 22일

안녕하세요
canLaunchUrl , launchUrl 은 빨간줄나는데 어디서 정의하는건가요?

2개의 답글
comment-user-thumbnail
2023년 1월 23일

정성스런 글 정말 감사하게 읽었습니다.!!
질문하나 있습니다!
그냥 URL 스트링 잘라서 하는거랑 실제 차이는 없을 수 있겠네요??

답글 달기
comment-user-thumbnail
2023년 9월 8일

안녕하세요. 플러터 초보가 도움을 구하고자 댓글로 문의 남깁니다. ㅠㅠ
작성하신 소스에

await platform.invokeMethod('startAct', <String, Object>{'url': url}).then((value) {
          LogPrint('parsing url : $value');
          return;
 });

이부분은 어떤 역할을 하는건가요?
mainActivity에 구현되어 있지않아 유추하기가 힘들어서요 ㅠ
저 부분을 무시하고 진행하면 유효하지 않은 거래라 에러가 나서 도움 구하고자 댓글 남깁니다!

1개의 답글
comment-user-thumbnail
2023년 11월 24일

하아.. 저도 pg 결제에서 애 많이 먹고 있었는데.. 정말 감사합니다 ㅠㅠㅠㅠ

답글 달기
comment-user-thumbnail
2024년 3월 7일

사람하나 살리셨습니다. 감사합니다. 좋은하루 보내세요.

답글 달기