RippleTrip 앱의 핵심 기능은 AI 프롬프트를 활용한 사진 편집입니다.
사용자는 여행 중 찍은 사진에서 특정 영역을 브러시로 선택하고,
그 영역에 텍스트 프롬프트를 입력해 객체를 생성하거나 제거할 수 있습니다.
또한, 카카오 소셜 로그인을 지원하기 위해 웹 기반 인증 과정을 거쳐야 했는데,
로그인 후 다시 앱으로 리다이렉션되는 구조가 사용자 입장에서 다소 불편했습니다.
게다가 사용자가 여행지 정보를 쉽게 탐색할 수 있도록
TripAdvisor의 여행지 추천 데이터를 보여주는 기능도 필요했습니다.
이 기능은 웹과 모바일 환경 모두에서 동일한 UX로 제공되어야 했기 때문에
처음에는 React로 웹 애플리케이션을 구현했습니다.
하지만 모바일 환경에서는 웹 링크를 누를 때마다 외부 브라우저가 열리는 문제가 있었고,
이로 인해 사용자 입장에서 생각을 해 봤을때 앱과 웹이 분리되어 있다는 느낌을 받았습니다.
그래서 앱 안에서 바로 웹 페이지를 띄울 수 있는 방법이 없을까? 고민하다가
저희는 웹뷰 라는 기술을 발견하게 되었습니다.
웹뷰를 사용하면 앱 내부에 웹 페이지를 임베드하여
사용자가 별도의 브라우저를 열지 않고도 자연스럽게 웹 기능을 사용할 수 있었습니다.
웹뷰는 네이티브 앱 내부에서 웹 페이지를 렌더링할 수 있게 해주는 컴포넌트입니다.
즉, 브라우저를 열지 않아도 앱 내부에서 HTML, CSS, JavaScript로 구성된 웹 콘텐츠를 그대로 보여줄 수 있습니다.
react-native-webview 패키지를 사용합니다.WebView, iOS의 WKWebView를 호출합니다.<WebView /> 컴포넌트가 렌더링됨 source 속성에 지정된 uri를 네이티브 WebView 엔진이 로드 이전에는 앱과 웹 사이에 데이터를 주고받아야 정보가 있어서,
WebView의 양방향 통신 구조를 직접 공부하여 적용하였지만.
지금은 해당 기능이 필요하지 않아 제거했습니다.
그때 WebView의 통신 원리를 공부하면서 내부 동작을 이해할 수 있었습니다.
WebView는 단순히 웹 화면을 보여주는 역할을 넘어,
앱과 웹이 서로 데이터를 주고받을 수 있는 브릿지 역할을 합니다.
React Native 쪽에서는 onMessage, injectedJavaScript, injectJavaScript() 같은 API를 통해
이 통신을 제어할 수 있습니다.
(참고: React Native WebView 공식 문서)
웹에서 React Native로 데이터를 보낼 때는
window.ReactNativeWebView.postMessage(data) 를 사용합니다.
이 코드를 실행하면 WebView 내부의 네이티브 브릿지를 통해
onMessage 이벤트 핸들러로 메시지가 전달됩니다.
<WebView
source={{ uri: "https://example.com" }}
onMessage={(event) => {
console.log("웹에서 받은 메시지:", event.nativeEvent.data);
}}
/>
// 웹 페이지 내부 코드
window.ReactNativeWebView.postMessage("AI 편집 완료!");
웹 쪽에서 postMessage()를 호출하면,
React Native의 onMessage()가 실행되어 데이터를 받을 수 있습니다.
이 방법은 주로 웹 내 이벤트 발생 시 앱에서 처리해야 하는 경우(예: 결제 완료, 로그인 성공)에 사용됩니다.
반대로 React Native에서 웹으로 명령이나 데이터를 전달할 때는
injectedJavaScript 또는 injectJavaScript()를 사용합니다.
injectedJavaScript : 웹 페이지가 로드될 때 자동으로 실행되는 코드injectJavaScript() : 이미 로드된 웹뷰에서 특정 시점에 실행할 코드const webRef = useRef<WebView>(null);
<WebViewref={webRef}
source={{ uri: "https://example.com" }}
injectedJavaScript={`
window.ReactNativeWebView.postMessage("웹이 로드되었습니다");
true;
`}
/>
<Buttontitle="웹으로 데이터 보내기"
onPress={() => {
webRef.current?.injectJavaScript(`
alert("React Native에서 보낸 데이터입니다!");
true;
`);
}}
/>
이렇게 하면 앱에서 웹 쪽의 전역 스코프(window)에 접근해
DOM 조작, 함수 호출 등 다양한 상호작용이 가능합니다.
(참고: WebView props – injectedJavaScript / onMessage)
다만, 제 경우에는 웹이 완전히 로드된 상태에서 정보를 주고받는 시나리오가 대부분이었기 때문에,
onLoad 시점에서 postMessage를 활용하여 데이터를 송신하는 방식을 주로 사용했습니다.
이 접근 방식은 불필요한 초기 통신을 줄이고,
웹이 렌더링된 이후 안정적인 데이터 전달을 보장한다는 장점이 있습니다.
┌──────────────────────────────┐
│ React Native (App) │
│ │
│ injectJavaScript() ─────▶ 웹 내부 JS 실행
│ │
│ onMessage(event) ◀───── window.ReactNativeWebView.postMessage(data)
│ │
└──────────────────────────────┘
window.ReactNativeWebView.postMessage()injectJavaScript() 또는 injectedJavaScriptRippleTrip 앱에서는 WebView를 단순한 웹 표시용이 아니라,
로그인, 여행지 정보 탐색, AI 편집 기능을 모두 하나의 앱 안에서 자연스럽게 통합하기 위해 사용했습니다.
카카오 로그인은 웹 기반 OAuth 인증 구조를 사용했기 때문에,
앱 내부에서 WebView를 띄워 로그인 과정을 처리했습니다.
로그인이 완료되면 백엔드가 redirectUri로 토큰을 반환하고,
onNavigationStateChange로 해당 리다이렉션을 감지해 로그인 완료 상태로 전환합니다.
<WebView
source={{
uri: `${AXIOS_BASE_URL}/auth/kakao/login?redirectUri=${encodeURIComponent(
REDIRECT_URI
)}`,
}}
onNavigationStateChange={(navState) =>
handleNavigationStateChange({
navState,
setShowWebView,
setError,
router,
setUser,
})
}
style={{ flex: 1 }}
/>
이 접근 방식 덕분에 사용자는 앱을 이탈하지 않고도
카카오 인증 과정을 자연스럽게 마칠 수 있었습니다.
사용자가 선택한 여행지를 웹 페이지로 띄워주는 화면에서는
외부 API(TripAdvisor)의 데이터를 렌더링하는 웹 페이지를 WebView에 표시했습니다.
React Native의 Suspense와 Skeleton 컴포넌트를 함께 사용해
웹 콘텐츠 로딩 중에도 깔끔한 UX를 유지했습니다.
<Suspense fallback={<WebViewSkeleton />}>
<WebViewsource={{ uri: url }}
mediaPlaybackRequiresUserAction
allowsInlineMediaPlayback={false}
javaScriptEnabled
domStorageEnabled
startInLoadingState
/>
</Suspense>
이를 통해 사용자는 별도의 브라우저 전환 없이
앱 내에서 여행지 상세 정보를 바로 확인할 수 있습니다.
AI 프롬프트를 활용한 이미지 편집 도구는
이미 웹 버전으로 구현된 에디터를 그대로 재사용했습니다.
ImageView 화면에서는 단순히 WebView로 웹 에디터를 임베드해
웹과 앱의 코드를 공유했습니다.
<View style={{ flex: 1 }}>
<Header />
<WebView source={{ uri: WEB }} style={{ flex: 1 }} />
</View>
이 덕분에 별도의 네이티브 UI를 새로 만들지 않고도
웹과 동일한 편집 기능을 앱 안에서 사용할 수 있었습니다.

| 적용 위치 | 사용 목적 | 핵심 포인트 |
|---|---|---|
| LoginScreen | 카카오 OAuth 인증 | WebView 내 리다이렉션 감지로 로그인 처리 |
| WebViewScreen | 여행지 정보 표시 | Suspense + Skeleton으로 로딩 UX 개선 |
| ImageView | AI 편집 기능 재사용 | 웹 기반 AI 에디터를 앱에 그대로 임베드 |
WebView를 통해 RippleTrip은