본문 바로가기
개발

React Native WebView로 PortOne 결제 API 연동하기

by 현명5079 2024. 12. 4.

📌  개요

React Native cli환경에서 개발하던 중 PortOne API v2로 결제 API를 연결할 일이 있었다.

 

- SDK가 있을 줄 알았지만 RN용 SDK는 따로 없다😞

- PortOne 깃허브를 찾아보아도 v1만 있을 뿐 v2의 RN은 보이지 않았다😞

 

그래서 PortOne의 Web전용 APIWebView를 이용해서 결제 연동을 하기로 했다.

 

🔎  간단한 WebView 사용법

WebView는 앱에 내장된 웹 브라우저이다.  앱에서 WebView 컴포넌트를 사용하면 앱에도 웹 화면을 보여주는 것이 가능하다. 이 과정에서 React Native와 Web이 데이터를 주고 받을 일이 있다면 다음의 메소드를 사용하면 된다.

  RN -> Web Web -> RN 
데이터 전송시 사용 webView.ref.postMessage window.ReactNativeWebView.postMessage
데이터 수신시 사용 onMessage window.addEventListener('message',onSuccess)

 

해결

결제 요청 시퀀스

WebView를 통해 PortOne을 연결하려면 총 3개의 페이지가 필요하다

 

1️⃣ WebView 페이지: WebView를 사용할 페이지 (React Native) => 1번의 시작점, 4번의 도착점에 해당

2️⃣ 결제요청 페이지: WebView로 연결될 웹페이지, 즉 PortOne 결제 API를 요청할 페이지 (React) => 1번의 도착점

3️⃣ 리다이렉트 페이지: PortOne 결제 API의 결과를 받을 리다이렉트 페이지 (React) => 3번의 도착점

 

환경변수

  • WEB_VIEW_SERVER, FRONT_SERVER: WebView로 보여줄 웹 콘텐츠의 도메인
  • PORTONE_REDIRECT_URI : 결제 후 리다이렉트 되는 웹 페이지

1️⃣ WebView 페이지

import WebView, {WebViewMessageEvent} from 'react-native-webview';
import {PORTONE_REDIRECT_URI, WEB_VIEW_SERVER} from '@env';
import {useRef} from 'react';

function PortOnePaymentScreen({}) {
  
  //결제 데이터
  const message = {
    type: 'payment',
    email: 'abc@naver.com',
    phoneNumber: '01012345678',
    fullName: '이름',
    orderName: '포트원 결제',
    totalAmount: 13000,
    paymentId: '1234678',
  };

  const webViewRef = useRef<WebView>(null);

  const handleOnLoad = async () => {
    if (webViewRef.current) {
    //RN -> Web: 결제에 필요한 데이터 전송
      webViewRef.current.postMessage(JSON.stringify({...message}));
    }
  };

  const handleOnMessage = async (event: WebViewMessageEvent) => {
    if (event.nativeEvent.url.includes(`${PORTONE_REDIRECT_URI}?`)) {
      //성공 후 로직
    }
  };

  return (
    <WebView
      source={{
        uri: `${WEB_VIEW_SERVER}/payment`,
      }}
      onMessage={handleOnMessage} // Web -> RN
      onLoad={handleOnLoad}
      ref={webViewRef}
    />
  );
}
  1. WebView 컴포넌트를 이용해 보여줄 웹페이지 uri를 source 에 적는다. 즉, 결제 요청 페이지의 uri를 적어주면 된다.
  2. 웹페이지가 로딩 됐을 때 실행할 로직을 onLoad에 적어주면된다. handleOnLoad를 보면 postMessage가 나온다. postMessage는 ReactNative에서 Web으로 데이터를 전달할 때 사용하는 메소드이다. 이를 통해 PortOne 결제 API에서 사용할 데이터를 전송해주었다.
  3. onMessage는 Web에서 React Native로 데이터가 전달될때 실행할 로직을 적어주면된다. onMessage의 첫번째 인자인 event에서는 데이터가 전송된 웹페이지 url을 담고 있다. 이 url이 리다이렉트 페이지에서 왔다면 성공 로직을 실행하도록 코드를 짰다.

2️⃣  결제 요청 페이지

function PaymentPage() {
  const [data, setData] = useState()
  const onMessageHandler = (e) => {
    setData(JSON.parse(e.data))
  }
  
  // RN  -> Web 으로 데이터가 왔을 때, 처리할 로직
  useEffect(() => {
    window.addEventListener("message", onMessageHandler);
    return () => {
      window.removeEventListener("message", onMessageHandler);
    };
  }, []);
  
  useEffect(() => {
    data && fetchPortOne(data)
  }, [data]);
  
  return (
      <div>
      </div>
  );
}

//fetchPortOne
import {requestPayment} from "@portone/browser-sdk/v2";

export async function fetchPortOne(data) {
  try {
    //결제 정보를 가지고 PortOne 요청
    await requestPayment({
      storeId: PORTONE_STORE_ID,
      channelKey: PORTONE_CHANNEL_KEY,
      customer: {
        email: data?.email,
        phoneNumber: data?.phoneNumber,
        fullName: data?.fullName,
      },
      paymentId: `paymentId-${data.paymentId}`,
      orderName: data.orderName,
      totalAmount: data.totalAmount,
      currency: "CURRENCY_KRW",
      payMethod: "CARD",
      redirectUrl: `${FRONT_SERVER}/paymentRedirect`
    });

  } catch (error) {
    // 에러시 로직
  } 
}
  1. addEventListener를 활용해 'message'이벤트가 발생했을 때, 실행할 로직을 적어준다. React Native -> Web 으로 postMessage를 하면 Web에서 message 이벤트가 발생한다. 이 이벤트 핸들러를 달아주면 결국 postMessage가 왔을 때 실행할 로직을 적어준 셈이 된다.
  2. portone 라이브러리의 requestPayment를 활용하여 결제 요청을 보낸다. 이때 모바일이라면 redirectUrl을 적어주어야 하는 것 같다. 여기에 적은 url에 쿼리스트링 형태로 결제 API 결과를 남겨준다.

3️⃣ 리다이렉트 페이지

function PaymentRedirectPage() {
  window.ReactNativeWebView.postMessage('결제가 완료되었습니다')
  return <LoadingComponent/>
}
  1. 리다이렉트 페이지로 오면 결제가 완료되었다는 메세지를 ReactNative로 보낸다. 1번의 handleOnMessage를 통해 이때의 로직을 적어두었다.

 

📝 마무리하며

이번 프로젝트에서는 컴포넌트를 네이티브 컴포넌트로 개발하고 외부 API와 WebSocket 활용시에만 WebView를 활용했다. 그런데 WebView를 학습해보고, 처음부터 React로 개발한 뒤에 React Native의 WebView를 활용했다면 더 빠르게 개발할 수 있었을텐데라는 아쉬움이 남았다. 다음 프로젝트를 한다면 빠르게 WebView로 개발하고 디벨롭 과정에서 Native가 필요한 부분만 바꾸는 전략을 선택할 것 같다.

'개발' 카테고리의 다른 글

Spring Security + 세션 + 소셜로그인  (0) 2025.01.08
CORS 에러가 발생하는 과정  (0) 2025.01.03
Builder 패턴을 도입한 이유  (0) 2024.12.23
Git pull rebase, merge 차이  (0) 2024.12.03
비동기와 싱글스레드  (1) 2024.12.03