React Native에서 WebView로 보내는 메시지: 앱 명령은 언제 어디에서 받는가
WebView ref의 postMessage를 페이지 수신 함수와 연결하고, 준비 시점과 iOS·Android의 서로 다른 message 사건 대상을 명시적으로 처리한다.
목차
표준·구현·측정·해석 표시는 무엇인가요?
- 표준웹 표준이나 언어 명세가 정한 동작
- 구현특정 기술이나 브라우저가 실제로 구현한 동작
- 측정명시한 환경에서 직접 실행해 관찰한 결과
- 해석앞선 근거에서 도출한 설계 판단
- 미확인아직 공식 근거나 재현 결과를 확인하지 못한 내용
30초 요약
ref는 현재 렌더된 WebView의 명령 기능을 가리키는 참조다. Window는 웹 전역 실행 대상,
Document는 현재 HTML 문서 구조이고, listener는 지정한 대상에서 사건을 기다리는 함수다.
React Native에서 이미 열린 WebView 페이지로 값을 보낼 때는 현재 WebView의 ref에서
postMessage(문자열)를 호출하고, 페이지는 message 사건을 받는다.
React Native
webViewRef.current.postMessage(JSON 문자열)
→ iOS: Window의 message 사건
→ Android(새 React Native 구조용 구현): Document의 message 사건
→ 페이지 수신 함수가 data 문자열을 해석명령은 페이지 수신 함수가 준비된 뒤 보내야 한다. 또한 고정한 WebView 14.0.1의 Apple 구현과 새 React Native 구조용 Android 구현은 사건 대상이 다르므로 한 플랫폼의 listener만 공통 계약처럼 사용하면 안 된다.
반복해서 기억할 문장은 이것이다.
현재 WebView, 준비된 페이지 수신 함수와 플랫폼별 사건 대상을 함께 확인한다.
이 글은 React Native → WebView 한 방향의 명령 전달만 다룬다. 여러 요청과 응답을 연결하는 식별자는 15편, 메시지 구조 검사는 16편에서 각각 추가한다.
이 글을 관통하는 상황: Android는 어두워졌는데 iOS 웹은 그대로다
13편의 WebBoundaryApp을 이어 쓴다. 앱의 버튼은 WebView 페이지에 theme.changed 명령을 보내고,
페이지는 light에서 dark로 바뀌어야 한다. 먼저 페이지가 document에만 수신 함수를 등록한 실패를
관찰한다.
이 글의 ref는 현재 렌더된 WebView가 제공하는 postMessage 메서드를 호출하기 위한 참조다.
useRef<WebView>(null)의 WebView 타입에는 패키지가 공개한 postMessage(message: string) 메서드가
포함된다.
다음은 플랫폼 차이를 재현하는 완결 App.tsx다.
import React, { useRef, useState } from 'react'
import { Button, SafeAreaView, StyleSheet, Text } from 'react-native'
import { WebView } from 'react-native-webview'
const PAGE_HTML = `<!doctype html>
<html lang="ko">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body { background: white; color: #111; }
body[data-theme='dark'] { background: #111; color: white; }
</style>
<body>
<p id="theme">웹 테마: light</p>
<p id="received">웹 수신 횟수: 0</p>
<script>
let receivedCount = 0
function receiveAppCommand(event) {
const command = JSON.parse(event.data)
receivedCount += 1
document.body.dataset.theme = command.theme
document.querySelector('#theme').textContent =
'웹 테마: ' + String(command.theme)
document.querySelector('#received').textContent =
'웹 수신 횟수: ' + String(receivedCount)
}
document.addEventListener('message', receiveAppCommand)
window.ReactNativeWebView.postMessage('web.ready')
</script>
</body>
</html>`
export default function App() {
const webViewRef = useRef<WebView>(null)
const [webReady, setWebReady] = useState(false)
const [sentCount, setSentCount] = useState(0)
function sendDarkTheme() {
if (!webReady || !webViewRef.current) return
const command = JSON.stringify({
type: 'theme.changed',
theme: 'dark',
})
webViewRef.current.postMessage(command)
setSentCount((count) => count + 1)
}
return (
<SafeAreaView style={styles.screen}>
<Text>웹 준비: {webReady ? '완료' : '대기'}</Text>
<Text>앱 명령 전송 횟수: {sentCount}</Text>
<Button
title="웹 테마를 dark로 변경"
disabled={!webReady}
onPress={sendDarkTheme}
/>
<WebView
ref={webViewRef}
originWhitelist={['*']} // navigation 없는 인라인 fixture 전용
source={{ html: PAGE_HTML }}
onLoadStart={() => setWebReady(false)}
onMessage={(event) => {
if (event.nativeEvent.data === 'web.ready') {
setWebReady(true)
}
}}
style={styles.webview}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
screen: { flex: 1, padding: 16, gap: 12 },
webview: { flex: 1, borderWidth: 1 },
})web.ready는 페이지가 document 수신 함수를 등록한 다음 앱으로 보내는 준비 신호다. 앱은 이 신호를
받기 전까지 명령 버튼을 비활성화한다. 이 순서로 “ref가 생겼으니 페이지도 준비됐을 것”이라는 시간
가정을 제거한다.
실행 전에 양 플랫폼 결과를 따로 예측한다.
document에만 listener가 있을 때 Android의 테마와 수신 횟수는 바뀔까?- 같은 코드에서 iOS의 테마와 수신 횟수도 바뀔까?
- 앱 명령 전송 횟수는 양쪽에서 무엇을 보여 줄까?
개발자 메뉴의 Reload 또는 앱 재시작으로 기준선을 초기화한다. 각 플랫폼에서 웹 준비: 완료를 먼저
확인하고 버튼을 한 번 누른다.
Android emulator
앱 명령 전송 횟수: 1
웹 화면: 어두운 배경·흰 글자
웹 테마: dark
웹 수신 횟수: 1
iOS Simulator
앱 명령 전송 횟수: 1
웹 화면: 흰 배경·어두운 글자
웹 테마: light
웹 수신 횟수: 0앱의 공통 ref 메서드는 양쪽에서 호출됐다. 그러나 페이지는 Android 구현이 사건을 보내는 Document
위치만 구독했다. iOS 구현은 Window에 사건을 보내므로 수신 함수가 실행되지 않는다.
공통 ref 메서드 뒤의 플랫폼 사건 대상을 확인한다
MessageEvent 생성자는 브라우저에서 message 사건 객체를 만드는 기능이다. Android는 이 기능을
쓸 수 없을 때 대신 실행하는 fallback 경로도 두며, 이 경로의 사건은 Document에서 Window까지
전파될 수 있다. 따라서 document와 window에 같은 함수를 무조건 등록하면 명령을 두 번 적용할
가능성까지 고려해야 한다. event.target은 사건이 처음 전달된 대상을 뜻하며, 다음 교정은 이 값을
기준으로 한 번만 적용한다.
iOS event.target === window
Android event.target === documentref가 있다는 사실과 페이지 준비를 합치지 않는다
onLoad는 문서가 load를 끝냈다는 신호지만 실제 제품 페이지가 내부 초기화를 더 수행할 수 있다. 준비
조건이 중요한 제품에서는 페이지가 명령 수신 준비를 마친 정확한 지점에서 신호를 보내는 편이 더 직접적인
증거다. 이 글은 13편에서 만든 WebView → React Native 입구를 그 준비 신호에 재사용한다.
양 플랫폼 수신 함수를 한 번만 적용되게 연결한다
다음 코드로 첫 PAGE_HTML 선언의 내용만 교체한다. 상수 이름과 App 코드는 그대로 유지한다.
const PAGE_HTML = `<!doctype html>
<html lang="ko">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body { background: white; color: #111; }
body[data-theme='dark'] { background: #111; color: white; }
</style>
<body>
<p id="theme">웹 테마: light</p>
<p id="received">웹 수신 횟수: 0</p>
<script>
let receivedCount = 0
function applyAppCommand(event) {
const command = JSON.parse(event.data)
receivedCount += 1
document.body.dataset.theme = command.theme
document.querySelector('#theme').textContent =
'웹 테마: ' + String(command.theme)
document.querySelector('#received').textContent =
'웹 수신 횟수: ' + String(receivedCount)
}
document.addEventListener('message', applyAppCommand)
window.addEventListener('message', (event) => {
if (event.target === window) {
applyAppCommand(event)
}
})
window.ReactNativeWebView.postMessage('web.ready')
</script>
</body>
</html>`Android 사건은 Document listener에서 적용한다. 그 사건이 Window까지 전파되는 fallback에서도
event.target은 최초 대상인 Document이므로 Window listener가 다시 적용하지 않는다. iOS 사건은
처음부터 Window가 대상이므로 event.target === window 조건에서 한 번 적용한다.
첫 코드를 관찰한 뒤 PAGE_HTML 선언을 두 번째 내용으로 교체한다. Reload 또는 앱 재시작 뒤
웹 준비: 완료와 다음 기준선을 확인한다.
앱 명령 전송 횟수: 0
웹 테마: light
웹 수신 횟수: 0양쪽에서 버튼을 한 번 누른다.
Android emulator
앱 명령 전송 횟수: 1
웹 화면: 어두운 배경·흰 글자
웹 테마: dark
웹 수신 횟수: 1
iOS Simulator
앱 명령 전송 횟수: 1
웹 화면: 어두운 배경·흰 글자
웹 테마: dark
웹 수신 횟수: 1수신 횟수가 2라면 두 listener가 같은 사건을 중복 적용한 것이다. 테마 문자열만 보면 같은 값을 두
번 적용해도 문제가 숨을 수 있으므로 반드시 횟수도 함께 관찰한다.
실행할 코드가 아니라 해석할 데이터를 보낸다
앱 command 객체
→ JSON.stringify
→ WebView postMessage 문자열
→ page event.data 문자열
→ JSON.parse
→ 페이지가 해석한 command 값문자열을 JavaScript 코드로 이어 붙여 실행하는 injectJavaScript와 목적을 섞지 않는다. 명령의
type과 값을 데이터로 보내고 페이지에 이미 등록한 처리 함수가 해석한다. 이 구조는 문자열 따옴표나
사용자 입력을 실행 코드에 직접 끼워 넣는 위험도 피한다.
다만 JSON.parse 성공만으로 허용된 명령이라는 뜻은 아니다. 이 고정 fixture는 앱이 만든 한 명령만
받지만, 실제 계약의 버전·필드·허용 목록과 실패 처리는 16편에서 별도로 검증한다.
전송 호출과 웹 적용 결과를 따로 관찰한다
앱의 sentCount는 ref 메서드를 호출한 횟수다. 페이지의 receivedCount와 theme은 실제 수신 함수가
실행되고 명령을 적용한 결과다.
| 관찰값 | 증명하는 것 | 증명하지 않는 것 |
|---|---|---|
| 앱 전송 횟수 1 | 앱 버튼과 ref 호출 경로 실행 | 페이지 수신·적용 성공 |
| 웹 수신 횟수 1 | 페이지 수신 함수 한 번 실행 | 업무 동작 전체 완료 |
| 웹 테마 dark | 이 fixture의 테마 값 적용 | 앱이 처리 결과를 응답받음 |
페이지가 결과를 앱에 다시 보내야 한다면 13편 방향을 사용한다. 동시에 여러 명령을 보낼 수 있다면 어느 요청의 결과인지 구분할 식별자가 필요하다. 이 요청·응답 연결은 15편에서 다룬다.
iOS와 Android에서 무엇을 각각 확인할까
| 관찰 | Android emulator | iOS Simulator | 실기기 조건 |
|---|---|---|---|
document-only 실패 차이 | 어두운 화면·dark·1 | 흰 화면·light·0 | 보통 불필요 |
| 양 대상 교정 결과 | 어두운 화면·dark·1 | 어두운 화면·dark·1 | 보통 불필요 |
| 앱 전송과 웹 수신 횟수 분리 | 확인 | 확인 | 보통 불필요 |
| camera·결제·파일 같은 실제 명령 | 가상 환경 제공 범위 | 가상 환경 제공 범위 | 장치 능력이 판단 대상이면 필요 |
한쪽의 성공으로 다른 플랫폼을 대신하지 않는다. 특히 공통 TypeScript 코드 뒤의 네이티브 구현이 어떤 웹 사건을 만드는지 버전마다 다시 확인한다.
pull request에서 달라져야 할 행동
React Native → WebView 명령을 추가할 때 다음 계약을 먼저 적는다.
명령 소유자 어느 React Native 화면이 보내는가
현재 대상 어떤 WebView ref인가
준비 신호 어느 page load의 listener가 준비됐는가
문자열 형식 type, 값, 계약 버전
웹 수신 위치 Window / Document / 양 플랫폼 처리
중복 방지 한 사건을 한 번만 적용하는 증거
적용 증거 웹 수신 횟수와 실제 화면 결과
응답 필요 처리 결과와 request ID가 필요한가
검증 환경 Android / iOS / 필요한 실기기 능력검토자는 다음을 확인한다.
- ref가 있다는 사실만으로 페이지 listener 준비를 가정하지 않았는가?
- 고정한 WebView 버전의 Apple 구현과 새 React Native 구조용 Android 구현에서 사건 대상을 각각 확인했는가?
- 두 listener가 한 사건을 중복 적용하지 않는가?
- command를 실행 코드가 아니라 문자열 데이터로 전달하는가?
- 앱 전송 횟수와 웹 적용 결과를 별도 증거로 남겼는가?
- 응답이나 여러 요청 연결이 필요한 요구를 postMessage 호출 하나로 끝내지 않았는가?
한 장으로 다시 보기
문제 Android 웹은 dark, iOS 웹은 light인 채 남음
관찰 앱은 양쪽 모두 1회 전송, document listener는 Android만 수신
원인 14.0.1 Apple은 Window, 새 React Native 구조용 Android는 Document에 사건 전달
행동 web.ready 뒤 전송하고 두 대상을 한 번만 처리하는 listener 등록
검증 양 플랫폼 앱 전송 1 → 웹 수신 1 → 테마 dark
경계 호출 성공은 웹 업무 완료나 앱의 처리 응답을 증명하지 않음React Native의 공통 postMessage 메서드는 페이지 수신 코드까지 자동으로 공통화하지 않는다. 현재
WebView와 준비된 page load를 확인하고, 고정한 Apple 구현과 새 React Native 구조용 Android 구현의
사건 대상을 각각 처리하며, 전송과 적용을 별도 증거로 관찰해야 명령 전달이 실무 계약이 된다.
스스로 확인할 질문
- WebView ref가 있어도 페이지 준비 신호가 필요한 이유는 무엇인가?
- 고정한 14.0.1의 Apple 구현과 새 React Native 구조용 Android 구현은 각각 어느 대상에 message 사건을 보내는가?
- 두 대상을 모두 듣되 Android에서 중복 적용을 막는 조건은 무엇인가?
- command 객체를 JSON 문자열로 보내는 이유는 무엇인가?
- 앱 전송 횟수와 웹 적용 결과를 왜 따로 관찰해야 하는가?
Active recall
기억에서 꺼내 보기
답을 쓰지 않아도 됩니다. 먼저 머릿속으로 답한 뒤 펼쳐서 정답·이유·흔한 오해를 비교하세요.
01React Native에서 webViewRef.current.postMessage를 호출하면 페이지가 message 수신 함수를 등록하기 전이어도 나중에 자동으로 다시 받을까?
정답
아니다. 현재 WebView 대상과 페이지 수신 함수가 준비된 뒤 보내야 한다. 이 글은 페이지가 listener 등록 뒤 web.ready를 앱으로 보내는 순서로 준비 상태를 닫는다.
관련 설명 다시 읽기왜 그런가
ref 존재, 문서 load와 페이지 명령 수신 준비는 서로 다른 조건이다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
02페이지가 document에만 message listener를 등록하면 iOS와 Android가 모두 같은 결과를 낼까?
정답
아니다. 고정한 WebView 14.0.1의 Apple 구현은 Window, 새 React Native 구조용 Android 구현은 Document에 사건을 보낸다. 양쪽 대상을 처리하되 같은 사건을 중복 적용하지 않는 수신 경계가 필요하다.
관련 설명 다시 읽기왜 그런가
공통 React Native 메서드 뒤의 웹 사건 대상은 플랫폼 구현에서 다를 수 있다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
03앱의 command 객체를 postMessage에 넘기면 페이지가 같은 객체 참조를 받을까?
정답
아니다. ref 메서드는 문자열을 받고 페이지의 message 사건도 data 문자열을 전달한다. 이 글은 JSON 문자열로 명령 모양을 합의한다.
관련 설명 다시 읽기왜 그런가
서로 다른 JavaScript 실행 영역은 객체 참조가 아니라 명시적 문자열 경계를 사용한다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
04앱 화면에 명령 전송 횟수가 1로 표시되면 웹 테마 적용도 성공했다고 볼 수 있을까?
정답
아니다. 앱의 호출 기록과 웹의 수신·적용 결과를 함께 확인해야 한다. 실제 처리 응답과 request ID는 다음 편에서 별도 계약으로 만든다.
관련 설명 다시 읽기왜 그런가
전송 요청과 수신·업무 적용은 서로 다른 관찰점이다.
선택한 상태와 다음 복습일은 이 브라우저에만 저장됩니다.
출처와 검증 범위
아래 날짜는 링크를 마지막으로 열어 본 날입니다. 문서 상단의 검증일은 글의 설명과 적용 범위를 다시 확인한 날입니다.
- React Native WebView 14.0.1 ReferenceReact Native WebView · 공식 문서 · 확인 2026-08-09
- React Native WebView 14.0.1 package root type declarationsReact Native WebView · 소스 코드 · 확인 2026-08-09
- React Native WebView 14.0.1 WebView typesReact Native WebView · 소스 코드 · 확인 2026-08-09
- React Native WebView 14.0.1 iOS component commandsReact Native WebView · 소스 코드 · 확인 2026-08-09
- React Native WebView 14.0.1 Android component commandsReact Native WebView · 소스 코드 · 확인 2026-08-09
- React Native WebView 14.0.1 Apple postMessage implementationReact Native WebView · 소스 코드 · 확인 2026-08-09
- React Native WebView 14.0.1 Android New Architecture managerReact Native WebView · 소스 코드 · 확인 2026-08-09
- useRefReact · 공식 문서 · 확인 2026-08-09
- DOM Standard — Dispatching eventsWHATWG · 표준 · 확인 2026-08-09
- HTML Standard — MessageEventWHATWG · 표준 · 확인 2026-08-09
- ECMAScript Language Specification — JSON.parse and JSON.stringifyEcma International · 표준 명세 · 확인 2026-08-09
- Extended controls, settings, and helpAndroid Developers · 공식 문서 · 확인 2026-08-09
- Running your app on simulated or physical devicesApple Developer · 공식 문서 · 확인 2026-08-09