📌 개요
React Native cli환경에서 개발하던 중 PortOne API v2로 결제 API를 연결할 일이 있었다.
- SDK가 있을 줄 알았지만 RN용 SDK는 따로 없다😞
- PortOne 깃허브를 찾아보아도 v1만 있을 뿐 v2의 RN은 보이지 않았다😞
그래서 PortOne의 Web전용 API와 WebView를 이용해서 결제 연동을 하기로 했다.
🔎 간단한 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}
/>
);
}
- WebView 컴포넌트를 이용해 보여줄 웹페이지 uri를 source 에 적는다. 즉, 결제 요청 페이지의 uri를 적어주면 된다.
- 웹페이지가 로딩 됐을 때 실행할 로직을 onLoad에 적어주면된다. handleOnLoad를 보면 postMessage가 나온다. postMessage는 ReactNative에서 Web으로 데이터를 전달할 때 사용하는 메소드이다. 이를 통해 PortOne 결제 API에서 사용할 데이터를 전송해주었다.
- 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) {
// 에러시 로직
}
}
- addEventListener를 활용해 'message'이벤트가 발생했을 때, 실행할 로직을 적어준다. React Native -> Web 으로 postMessage를 하면 Web에서 message 이벤트가 발생한다. 이 이벤트 핸들러를 달아주면 결국 postMessage가 왔을 때 실행할 로직을 적어준 셈이 된다.
- portone 라이브러리의 requestPayment를 활용하여 결제 요청을 보낸다. 이때 모바일이라면 redirectUrl을 적어주어야 하는 것 같다. 여기에 적은 url에 쿼리스트링 형태로 결제 API 결과를 남겨준다.
3️⃣ 리다이렉트 페이지
function PaymentRedirectPage() {
window.ReactNativeWebView.postMessage('결제가 완료되었습니다')
return <LoadingComponent/>
}
- 리다이렉트 페이지로 오면 결제가 완료되었다는 메세지를 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 |