이 발표는 WebView와 React Native가 통신을 할 때의 문제점에 대해 설명하고,
webview-bridge 라이브러리가 이 문제점들을 어떻게 해결하는지에 대한 내용이 담겨있다.
https://github.com/gronxb/webview-bridge
1. 웹뷰 통신이 왜 필요할까?
웹뷰 통신이 왜 필요할까? → InAppBrowser , Native Navigation, Shared Data
이 요소들이 각각 무엇을 뜻하는지 AI에게 물어봤다.
InAppBrowser (인앱 브라우저): 인앱 브라우저는 모바일 앱 내에서 웹 콘텐츠를 표시하는 기능입니다.
웹뷰 통신이 필요한 이유:
- 앱 내에서 외부 웹 콘텐츠를 원활하게 통합할 수 있습니다.
- 사용자가 앱을 벗어나지 않고도 웹 기반 기능을 이용할 수 있습니다.
- 앱과 웹 콘텐츠 간의 데이터 교환이 가능해집니다.
Native Navigation (네이티브 내비게이션): 네이티브 내비게이션은 앱의 고유한 탐색 기능을 의미합니다.
웹뷰 통신이 이 부분에서 필요한 이유:
- 웹뷰 내의 페이지 이동을 앱의 네이티브 내비게이션과 연동할 수 있습니다.
- 웹 페이지에서 앱의 다른 섹션으로 이동하거나, 앱에서 특정 웹 페이지로 이동하는 등의 통합된 사용자 경험을 제공할 수 있습니다.
- 뒤로 가기, 앞으로 가기 등의 네이티브 내비게이션 기능을 웹뷰와 연동할 수 있습니다.
Shared Data (공유 데이터): 공유 데이터는 앱과 웹뷰 간에 정보를 주고받는 것을 의미합니다.
이를 위해 웹뷰 통신이 필요한 이유:
- 앱에서 저장된 사용자 정보나 설정을 웹뷰에 전달할 수 있습니다.
- 웹뷰에서 수집된 데이터를 앱의 저장소에 저장하거나 처리할 수 있습니다.
- 앱과 웹 간의 일관된 사용자 경험을 제공하기 위해 데이터를 동기화할 수 있습니다.
2. 기존 방식 문제 정의
- onMessage에서 무수한 분기
- 양쪽 사이드에서 통신 함수 재정의 - 양쪽에서 openInAppBrowser 코드를 적어줘야 한다.
- 단방향 통신의 한계 - Web에서 PostMessage로 RN으로 보내면 결과값을 보낼 수 없다.
- 하위 호환성 → 모바일 애플리케이션은 배포를 해도 최신을 유지x (앱스토어 심사) , but 웹은 배포해도 최신을 유지 → 웹에서 새로운 네이티브 기능이 담긴 이벤트를 호출했는데 구 버전 애플리케이션을 사용 중이라면?
AI한테 위 문제들에 대하여 자세한 설명을 부탁했다.
- onMessage에서 무수한 분기:
- 웹뷰와 네이티브 앱 간의 통신에서 onMessage 핸들러가 여러 종류의 메시지를 처리해야 합니다.
- 이로 인해 코드가 복잡해지고, 새로운 기능을 추가할 때마다 분기문이 늘어나 유지보수가 어려워집니다.
- 코드의 가독성이 떨어지고 버그 발생 가능성이 높아집니다.
- 양쪽 사이드에서 통신 함수 재정의:
- 웹과 네이티브 앱 양쪽에서 동일한 통신 함수(예: openInAppBrowser)를 각각 구현해야 합니다.
- 이는 코드 중복을 야기하고, 한 쪽에서 변경이 있을 때 다른 쪽도 함께 수정해야 하는 번거로움이 있습니다.
- 일관성 유지가 어려워지고 개발 및 유지보수 비용이 증가합니다.
- 단방향 통신의 한계:
- 웹에서 PostMessage를 통해 React Native(RN)로 메시지를 보낼 때, 즉시 결과값을 받을 수 없습니다.
- 이는 비동기 작업이나 복잡한 데이터 교환이 필요한 경우 처리가 어려워집니다.
- 콜백이나 추가적인 메시지 교환이 필요해져 코드가 복잡해지고 성능에 영향을 줄 수 있습니다.
- 하위 호환성 문제:
- 모바일 애플리케이션은 앱스토어 심사 등의 이유로 배포 후에도 모든 사용자가 최신 버전을 사용하지 않을 수 있습니다.
- 반면, 웹은 배포 즉시 모든 사용자가 최신 버전을 사용하게 됩니다.
- 이로 인해 웹에서 새로 추가된 네이티브 기능을 호출했을 때, 구 버전 앱을 사용 중인 사용자에게는 오류가 발생하거나 기능이 작동하지 않을 수 있습니다.
- 이는 사용자 경험을 해치고, 개발자가 여러 버전의 앱을 동시에 지원해야 하는 부담을 줍니다.
이러한 문제점들은 웹뷰를 사용하는 하이브리드 앱 개발에서 자주 발생하는 도전 과제들입니다.
이를 해결하기 위해서는 더 체계적인 통신 구조, 버전 관리 시스템, 그리고 하위 호환성을 고려한 설계가 필요합니다. 예를 들어, 통신 프로토콜을 표준화하고, 기능 검출 메커니즘을 구현하며, 양방향 통신을 지원하는 브리지 라이브러리를 사용하는 등의 방법을 고려할 수 있습니다.
3. 어떻게 하면 위 문제점들을 해결할 수 있을까?
관점을 바꾸자
React Native — Response → Webview
WebView —Request(PostMessage) → React Native
사용자 중심 설계
- Usage → bridge 활용
- Initialization
- Hydration
- Event To Promise
- 존재하지 않는 메소드 예외처리
관점 변경과 사용자 중심 설계에 대해 AI한테 자세한 설명을 부탁했다.
1. 관점 변경:
- React Native를 응답(Response) 주체로, WebView를 요청(Request) 주체로 설정합니다.
- WebView에서 PostMessage를 통해 React Native로 요청을 보내고, React Native가 이에 응답하는 구조입니다.
- 이는 기존의 양방향 통신 문제를 해결하고, 더 명확한 통신 흐름을 만듭니다.
2. 사용자 중심 설계:
이 라이브러리는 다음 다섯 가지 주요 개념을 중심으로 설계되었습니다:
a) Usage (bridge 활용):
- 개발자가 쉽게 사용할 수 있는 bridge API를 제공합니다.
- 이를 통해 웹과 네이티브 간의 통신을 추상화하고 단순화합니다.
b) Initialization:
- 라이브러리의 초기 설정 과정을 정의합니다.
- 웹뷰와 네이티브 앱 간의 연결을 설정하고 필요한 리소스를 로드합니다.
c) Hydration:
- 웹뷰가 로드될 때 필요한 데이터나 상태를 네이티브 앱에서 웹으로 전달하는 과정입니다.
- 이를 통해 웹뷰가 초기 상태를 빠르게 구성할 수 있습니다.
d) Event To Promise:
- 단방향 이벤트 기반 통신을 Promise 기반의 양방향 통신으로 변환합니다.
- 이를 통해 웹에서 네이티브 기능을 호출하고 결과를 받아올 수 있게 됩니다.
- 비동기 작업을 더 쉽게 처리할 수 있게 해줍니다.
e) 존재하지 않는 메소드 예외처리:
- 구 버전 앱에서 지원하지 않는 새로운 기능에 대한 호출을 안전하게 처리합니다.
- 오류 대신 적절한 폴백(fallback) 동작을 정의하거나 사용자에게 업데이트 안내를 할 수 있습니다.
이 접근 방식의 장점:
- 코드 복잡성 감소: onMessage의 복잡한 분기 처리가 필요 없어집니다.
- 재사용성 향상: 통신 로직이 라이브러리에 캡슐화되어 있어 재사용이 쉽습니다.
- 양방향 통신 지원: Promise 기반 통신으로 결과값을 쉽게 받아올 수 있습니다.
- 버전 호환성 개선: 존재하지 않는 메소드에 대한 예외 처리로 하위 호환성 문제를 해결합니다.
- 개발 경험 향상: 사용자 중심의 API 설계로 개발자가 더 쉽게 사용할 수 있습니다.
이 라이브러리는 웹뷰와 네이티브 앱 간의 통신을 더 효율적이고 안정적으로 만들어, 하이브리드 앱 개발의 여러 문제점들을 해결하고자 하는 시도로 보입니다.
4. 타입스크립트 진영에서 자주 언급되는 Type-Safe 왜 필요할까요?
외부 시스템에서 일어날 수 밖에 없는 타입 불일치
Backend 프로젝트(or React Native 프로젝트)
Frontend 프로젝트(or Webview 프로젝트)
= 독립적으로 존재하는 프로젝트에서는 다른 프로젝트의 스키마를 알 수 없음
= 타입 수동 정의
= 실수 발생
사람의 손으로 만든 타입 100% 믿을 수 있을까요?
- 반복된 타입 불일치는 타입스크립트의 목적성을 잃게합니다.
타입스크림트 목적: 컴파일 단계에 버그를 조기 발견
타입 불일치를 해결하려는 노력
REST API: swagger to typescript(openapi-generator)
GraphQL: schema to typescript(graphql-codegen)
타입 직접 정의하지 않기(타입 추론)
- typeof를 통해서 타입을 추론하자.
- 키 추출을 통해서 타입을 추론하자.
- Generic을 활용하자
- infer를 활용하자
→ Input을 통해서 모든 타입이 완성되는 경험
타입 추론을 적극 활용한다면 순수 JS를 쓰는 것 같지만 실제로는 모든 타입이 안전한 상태이다.
AI한테 설명을 부탁했다.
"사람의 손으로 만든 타입을 100% 믿을 수 있을까요?"라는 질문은 매우 중요한 포인트를 짚고 있습니다. 사람이 직접 타입을 작성할 때 실수할 가능성이 있기 때문입니다. 이런 실수로 인한 타입 불일치는 TypeScript를 사용하는 주된 목적인 '컴파일 단계에서의 버그 조기 발견'을 무력화시킬 수 있습니다.
이를 해결하기 위한 노력들:
- REST API의 경우:
- Swagger 같은 API 문서화 도구에서 TypeScript 타입을 자동으로 생성합니다.
- 'openapi-generator'라는 도구를 사용하여 이를 수행합니다.
- GraphQL의 경우:
- GraphQL 스키마에서 TypeScript 타입을 자동으로 생성합니다.
- 'graphql-codegen'이라는 도구를 사용합니다.
이러한 도구들은 사람의 실수를 줄이고 API와 타입 간의 일관성을 유지하는 데 도움을 줍니다.
더 나아가, 타입을 직접 정의하지 않고 TypeScript의 타입 추론 기능을 최대한 활용하는 방법들이 제시됩니다:
- typeof 사용:
- 이미 존재하는 값이나 함수의 타입을 자동으로 추론합니다.
- 예: const myObj = { name: "John", age: 30 };type MyObjType = typeof myObj;
- 키 추출:
- 객체의 키를 이용해 새로운 타입을 만듭니다.
- 예: type Keys = keyof typeof myObj; // "name" | "age"
- Generic 활용:
- 재사용 가능한 유연한 타입을 만듭니다.
- 예: function identity<T>(arg: T): T { return arg; }
- infer 키워드 사용:
- 조건부 타입 내에서 타입을 추론합니다.
- 예: type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
이러한 방법들을 통해 "Input을 통해서 모든 타입이 완성되는 경험"을 제공합니다. 즉, 개발자가 초기 입력 데이터나 함수만 정의하면, 나머지 필요한 타입들은 TypeScript가 자동으로 추론하여 만들어내는 것입니다.
이 접근 방식의 장점:
- 타입 정의의 실수를 줄일 수 있습니다.
- 코드와 타입이 항상 동기화됩니다.
- 타입 정의에 들이는 시간과 노력을 줄일 수 있습니다.
- 코드의 변경에 따라 타입도 자동으로 업데이트됩니다.
이렇게 함으로써, TypeScript의 장점을 최대한 활용하면서도 타입 관리의 부담을 줄일 수 있습니다.
5. 웹뷰에서 네이티브 앱에 있는 인증 정보를 가져오려면 어떻게 해야할까요?
→ 만약 토큰 완료로 인해 기존 토큰이 바뀌게 된다면?
React Native와 웹의 상태를 동기화하기 → Shared State
사용법 중심 설계(코어 로직 개발)
React 18에서는 useSyncExternalStore 훅 존재
AI한테 위 내용들에 대한 설명을 부탁했다.
- 웹뷰에서 네이티브 앱의 인증 정보 가져오기:
- 일반적으로 네이티브 앱에서 웹뷰로 인증 정보를 주입하는 방식을 사용합니다.
- 예를 들어, 웹뷰 로드 시 자바스크립트 인터페이스를 통해 토큰을 전달할 수 있습니다.
- 토큰 갱신 문제:
- 토큰이 갱신되면 웹뷰와 네이티브 앱 양쪽의 토큰을 업데이트해야 합니다.
- 이를 위해 '공유 상태(Shared State)' 개념이 필요합니다.
- React Native와 웹의 상태 동기화 (Shared State):
- 네이티브 앱과 웹뷰 간에 실시간으로 상태를 공유하고 동기화하는 메커니즘입니다.
- 토큰이 갱신되면 양쪽에 즉시 반영되어야 합니다.
- 사용법 중심 설계 (코어 로직 개발):
- 개발자가 쉽게 사용할 수 있는 API를 설계합니다.
- 복잡한 동기화 로직을 추상화하여 간단한 인터페이스로 제공합니다.
- React 18의 useSyncExternalStore 훅:
- React 18에서 도입된 이 훅은 외부 상태와 React 컴포넌트의 상태를 동기화하는 데 사용됩니다.
- 웹뷰 내의 React 앱에서 이 훅을 사용하여 네이티브 앱의 상태 변화를 구독하고 반영할 수 있습니다.
구현 아이디어:
- 네이티브 앱에서 상태 관리 시스템 구축:
- 토큰 등의 중요 정보를 저장하고 관리합니다.
- 상태 변경 시 이벤트를 발생시킵니다.
- 웹뷰와 네이티브 앱 간의 통신 채널 구축:
- 양방향 메시지 전달 시스템을 만듭니다.
- 네이티브 앱의 상태 변경을 웹뷰에 알립니다.
- 웹뷰 내 React 앱에서 useSyncExternalStore 사용:
- const token = useSyncExternalStore( subscribe, // 네이티브 앱의 상태 변경을 구독하는 함수 getSnapshot, // 현재 토큰 상태를 가져오는 함수 getServerSnapshot // SSR을 위한 함수 (옵션) )
토큰 갱신 시나리오:
- 네이티브 앱에서 토큰 갱신
- 상태 관리 시스템에서 이벤트 발생
- 웹뷰로 메시지 전송
- useSyncExternalStore를 통해 React 컴포넌트 업데이트
이러한 접근 방식을 통해 네이티브 앱과 웹뷰 간의 상태를 일관성 있게 유지하면서, 개발자에게는 간단한 사용법을 제공할
수 있습니다. 이는 복잡한 하이브리드 앱 개발에서 상태 관리의 어려움을 크게 줄여줄 수 있습니다.
6. 결론
- Type-Safe한 WebView 통신 라이브러리 개발 완료
- 타입스크립트에서 Input을 기준으로 타입이 완성되는 경험
- 사용법 중심 설계로 더 나은 추상화
- 비슷한 개념의 라이브러리의 사용법을 분석하여 적용하기
- 특정 프레임워크에 종속되지 않는 확장성 높은 구조
'Event' 카테고리의 다른 글
‘엔비디아 RTX AI PC 캠퍼스 세미나’를 다녀오고 개념 정리하기 (3) | 2024.11.27 |
---|---|
[2024 FEConf] '바퀴 대신 로켓 만들기' 내용 정리(feat. AI) (0) | 2024.08.30 |
큐시즘 전시회 - ENGAGE ENERGY 갔다온 후기 (4) | 2024.06.08 |
[GDSC Konkuk kprintf] React 19 업데이트 강의 정리 + 후기 (1) | 2024.04.06 |
동아리에서 주최한 해커톤 참여 후기 (1) | 2023.12.21 |