모바일 웹뷰에서 일어나는 문제들을 고치는 라이브러리 (webview-fix-kit)
webview-fix-kit이란
모바일 Webview 환경에서 일어나는 문제점을 해결하기 위해 만든 React 라이브러리이다.
웹뷰로 만든 앱에 관심이 많아 웹뷰로 만든 앱 (토스, 당근 등...) 을 써보면서 여러 문제들을 발견했고 이런 문제를 해결하기 위해 이 라이브러리를 만들었다.
오픈 소스 라이브러리는 https://github.com/je0ngmin/hangul_utils, https://github.com/je0ngmin/screenstack 이후로 세 번째로 만드는 건데, hangul_utils는 Dart 라이브러리라 배포가 쉬웠지만 screenstack과 webview-fix-kit은 Typescript 기반이라 tsdown 같은 번들러를 설정해야 해서 좀 어려웠다... 그래도 만들면서 여러모로 배운 게 많은 것 같아서 재밌었다.
설치
npm i webview-fix-kit
features
useWebviewHover
터치 스크린 환경에서는 CSS의 :hover가 작동하지 않거나 이상하게 동작하는 문제점이 있다. useWebviewHover를 이용해 ref을 만들고 버튼에 ref을 넘기면 해당 버튼에 hover 중인 상태일 시 data-webview-hover 속성이 붙는다.
import { useWebviewHover } from 'webview-fix-kit';
function HoverButton() {
const hoverRef = useWebviewHover<HTMLButtonElement>();
return (
<button ref={hoverRef} type="button">
Touch me
</button>
);
}
해당 속성을 이용해 CSS에서 data attribute selector로 hover 스타일을 지정할 수 있다.
button[data-webview-hover] {
transform: scale(0.95);
}
WebviewImageLongClickGuard
모바일 웹 브라우저 또는 웹뷰 환경에서 이미지를 길게 누를 시 context menu가 나오는 문제를 해결하기 위한 컴포넌트. img 태그에 감싸주면 해당 부분에 길게 누르는 동작이 작동하지 않도록 할 수 있다.
import { WebviewImageLongClickGuard } from 'webview-fix-kit';
function ProtectedImage() {
return (
<WebviewImageLongClickGuard>
<img src="/photo.jpg" alt="Sample" />
</WebviewImageLongClickGuard>
);
}
WebviewInput, WebviewTextarea
iOS 웹 브라우저 또는 웹뷰 환경에서 Input에 focus했을 시 페이지 전체의 스크롤이 밀리는 문제를 해결하기 위한 컴포넌트. WebviewInput, WebviewTextarea 모두 input, textarea과 똑같이 사용할 수 있어 컴포넌트만 교체할 수 있다.
import { useState } from 'react';
import { WebviewInput, WebviewTextarea } from 'webview-fix-kit';
function NicknameInput() {
const [nickname, setNickname] = useState('');
const [message, setMessage] = useState('');
return (
<form style={{
display: "flex",
flexDirection: "column",
gap: 8
}}>
<WebviewInput
name="nickname"
required
value={nickname}
placeholder="Enter your nickname"
onChange={(event) => setNickname(event.target.value)}
/>
<WebviewTextarea
name="message"
rows={5}
value={message}
placeholder="Enter your message"
onChange={(event) => setMessage(event.target.value)}
/>
</form>
);
}