Next,  Flutter,  Web,  Development,  Article
Flutter webView에서 결제 서비스 붙이기
이제 앱 심사는 그만! 하이브리드 앱
2023. 5. 21.2023. 5. 25.

그래요, 웹뷰로 갑시다.

회사 앱을 하이브리드 앱으로 전환하기로 결정했습니다.

이유는 다음과 같았습니다.

  1. 웹의 장점
    1. 현재 추가적인 리액트 공부와 아키텍처 재구성으로 개발 속도가 Flutter 대비 리액트가 월등히 빠르다.
    2. 앱에 비해 배포가 매우 쉽다. (심사없이 바로 적용이 가능)
    3. 웹 서비스도 같이 운영될 예정인데, 이 때 서로 컴포넌트를 공유하여 사용할 수 있다.
  2. 앱의 장점
    1. 웹으로 들어갈 때 보다 훨씬 자연스럽게 사용자가 사용할 수 있다.
    2. Flutter로 감쌌기 때문에 디바이스의 기능을 더 사용할 수 있다(네이티브 기능들 사용가능).
    3. 스토어를 통해서 다운로드 가능하다.

하지만 잘 몰랐기 때문에 걱정되는 점이 있었습니다.

“웹뷰로 결제가 가능한가?"

이 부분에 확신이 없었기 때문에 결제는 Flutter로 구현하고, 나머지는 웹뷰로 구성하는 방식을 처음에 고려했습니다.

하지만 생각만해도 귀찮지 않나요. 결제 부분은 민감해야하는 곳인데, 그 정보들을 어떤 방식으로 Flutter로 넘길 것이며, 그 많은 결제 관련 어플에 다 대응해야하고… 웹뷰만으로 결제를 할 방법을 찾아내야했습니다. 이번 게시글에서는 이를 구현하기 위해 고군분투했던 저의 경험을 작성합니다.

image.png

카카오페이, 네이버페이, 토스페이, 페이코, KB페이…

“페이” 춘추전국시대

모든 금융 회사들이 각자의 페이를 만들고 경쟁하는 지금은 바야흐로 2023년. 너무 많습니다. 각 서비들은 개발자센터를 운영하며 플랫폼별 개발 가이드를 제시합니다만, 이걸 “혼자서” 직접 다 구현하는 것은 현실적으로 불가능합니다.

image.png

image(1).png

결제 서비스들의 개발 가이드 페이지

그래서 이 결제 서비스들을 하나로 묶어서 제공해주는 서비스가 있습니다. 바로 포트원(구 아임포트)입니다.

심지어 무료

입니다. 그리고 포트원 개발자 센터에도 마찬가지로 서비스 개발 가이드를 제공하고 있습니다. 기본적으로 JavaScript library를 CDN으로 다운받아 사용하는 형태입니다.

저는 가이드를 따라 결제창을 구현하고 웹뷰에서 확인해보았습니다.

image(2).png

image(3).png

image(4).png

웹뷰에서 진행해본 카카오페이 간편 결제

일반결제는 잘 되었지만 간편결제는 예상대로 안됐습니다. 지금부터가 진짜 문제라는게 확 느껴지네요.

intent가 여기서 왜 나와?

예전에 잠시 android 개발을 해보았기에 intent가 어떤 친구인지는 알고 있었습니다. android 문서에는 intent를 다음과 같이 설명합니다.

intent는 메시징 객체로, 다른 앱 구성 요소로부터 작업을 요청하는 데 사용할 수 있습니다.

URL의 scheme을 나타내는 자리에 intent라니, 잠시 당황했지만 생각해보니 웹에서 어플을 열려면 어떤 채널로 메시지를 전달해야 할테니, 이게 그 방식이라는 것을 알아차렸습니다. 조금 더 찾아보니 deep link의 존재를 알게 되었습니다.

마찬가지로 android 공식 문서에서 deep link는 다음과 같이 정의합니다.

딥 링크는 사용자를 앱의 특정 콘텐츠로 바로 연결하는 URL입니다.

open하면 해당 어플로 바로 연결시켜주는 URL이 바로 deep link였습니다. 다음과 같이 사용한다고 합니다.


<intent-filter>
  ...
  <data android:scheme="https" android:host="www.example.com" />
  <data android:scheme="app" android:host="open.my.app" />
</intent-filter>

위와 같은 코드가 app manifest에 작성되어있으면 다음 URL들이 어플을 오픈합니다.

그래서 테스트로 fb://profile, kakaopay://test 로 이동해보니, 페이스북의 프로필 페이지와, 카카오페이가 열리는 것을 확인했습니다(카카오페이는 해당 페이지가 없다고 하며 홈 화면으로 이동시킵니다.)

하지만 현재 포트원에서 연결해주는 URL 바로 열리지 않았습니다. deep link를 여는게 문제는 없는 걸 확인했으니, 모두 intent://로 시작하는 URL을 진짜 Intent 객체로 변경해야 하겠다고 생각이 들었습니다.

intent:// 를 진짜 intent로 변경하는 방법

Flutter에는 MethodChannel이라는 messing channel이 있습니다. Platform Specific한 기능이 필요할 때, 해당 채널로 message 통신으로 문제를 해결할 수 있도록 한것이죠.

저희는 현재 intent://로 시작하는 URL을 intent로 변환해서 이동하는 것이 목표였습니다. 다행히도 intent를 일반적인 URL intent로 다시 parsing하는 방법이 있었습니다! 그리고 이 URL은intent://로 시작하지 않습니다.

그래서 Flutter의 공식 문서를 참고하여서 android 부분의 코드를 코틀린으로 작성해보았습니다.

override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(
        flutterEngine.dartExecutor.binaryMessenger,
        CHANNEL
    ).setMethodCallHandler{call, result->
when (call.method) {
            "convertUri" -> try {
                val intent: Intent =
                    Intent.parseUri(call.argument<String>("uri"), Intent.URI_INTENT_SCHEME)
// 실제로 사용가능한 URL로 변환되는 과정
                result.success(intent.getDataString()) // 반환
            } catch (e: URISyntaxException) {
                result.notImplemented()
            } catch (e: ActivityNotFoundException) {
                result.notImplemented()
            }
        }
}

Flutter 쪽의 코드는 다음과 같습니다.

if (Platform.isAndroid) {
  otherAppUri = Uri.parse(await MethodChannel('kr.co.dapada')
      .invokeMethod('convertUri', {"uri": request.url}));
}

webview_flutteronNavigationRequest부분에서 intent URL을 판단하고, 맞다면 위의 MethodChannel을 통해 실행가능한 URL로 변경하고 해당 URL을 반환하여 이동합니다.

image(6).png

성공! 이제 Flutter의 Webview에서 deep link를 모두 사용할 수 있게 처리했습니다!

마무리 하며

image(8).png

크로스 플랫폼(Flutter)에서 하이브리드 앱(WebView)를 개발하는 것은 다양한 분야를 넘나드는 경험을 선사해줍니다🥲. 오랜만에 전문 분야가 아닌 곳에서(android) 꼬리에 꼬리를 무는 문제를 만났네요. 중간에 정말 포기하고 다른 방법을 찾고 싶었지만, 그곳이 더 먼 길이라는걸 분명히 느꼈기 때문에 해결할 수 있었습니다. (하지만 포스팅한 뒤에는 너무나도 당연해 보이는 문제들)

이전 포스팅에도 올렸듯이 리액트 아키텍쳐 변경 후 눈에 띄는 개발 속도 향상이 있었고, 회사 앱도 Flutter로 개발한 부분을 거의 대부분 리액트로 다시 개발했습니다. 덕분에 지금 결제와 로그인을 신경쓸 수 있는거겠죠. 감사하며 개발중인 요즘입니다. 하지만 구현 작업이 많고, 이 같이 시퀀스가 어려운 작업이 많지는 않기 때문에 포스팅이 뜸하게 되네요. 그래도 한 달에 1~2개는 꾸준히 쓰려 노력하겠습니다.

Reference

  1. 모바일웹 / 웹앱 / 하이브리드앱 / 네이티브앱
  2. Flutter Webview Intent 처리 ERR_UNKNOWN_URL_SCHEME