웹뷰에서 마주하는 여러 팁을 공유합니다.
웹뷰를 구현하다보면 아래와 같은 화면을 자주 마주하게 됩니다.
아래 사진은 토스에서 가져왔습니다.
이때 하단에 항상 위치하고 있는 버튼을 보통 CTA(Click-To-Action) 버튼이라고 부르는데요. 이러한 버튼은 일반적으로 컨텐츠 위에 위치하고 있어야하기에 position:fixed
로 구현하면 됩니다.
간단한 것 같지만 한가지 고려 해야할 점이 있습니다. 컨텐츠 영역에서 특정한 높이를 차지하고 있지 않기에 뒤에 컨텐츠를 가리게 된다는 점입니다.
그럼 어떻게 하면 뒤 컨텐츠 영역을 가리지 않고 CTA 버튼을 만들 수 있을까요?
가장 단순한 방법으로는 최하단에 높이를 차지하는 요소를 두면 됩니다.
const Example = () => {
return (
<div>
content
<Footer />
<div style={{ height: 60 }} />
{/* 내부적으로 60px의 높이를 가진 fixed element를 렌더하고 있음*/}
<CTAButton />
</div>
)
}
export default Example
위와 같은 방식은 잘 동작하겠지만, 아래와 같은 문제점들을 마주할 수 있습니다.
바로 버튼의 높이가 동적으로 바뀌는 것에 대응할 수 없다는 것입니다.
이를 해결하기 위해서는 ResizeObserver 라는 API를 사용하면 됩니다.
해당API를 사용하면 요소의 사이즈 변화를 감지하여 해당 높이를 가져올 수 있고, 이를 spacer에 주입해줄 수 있습니다.
const Example = () => {
const targetRef = useRef<HTMLDivElement>(null)
const spacerRef = useRef<HTMLDivElement>(null)
const observer = new ResizeObserver(([entry]) => {
if (spacerRef.current) {
spacerRef.current.style.height = `${
entry.target.getBoundingClientRect().height
}px`
}
})
useEffect(() => {
if(!targetRef.current) return
const el = targetRef.current
observer.observe(el)
return () => {
observer.unobserve(el)
}
}, [])
return (
<div>
content
<Footer />
<div style={{ height: 60 }} />
{/* 내부적으로 60px의 높이를 가진 fixed element를 렌더하고 있음*/}
<CTAButton />
</div>
)
}
export default Example
이러한 CTA 버튼이나 Appber처럼 fixed요소만큼 너비를 차지해야하는 경우들이 자주 발생하기에 useSpacer
라는 hook으로 분리해 보겠습니다.
import { useRef, useEffect } from "react";
export const useSpacer = () => {
const targetRef = useRef<HTMLDivElement>();
const spacerRef = useRef<HTMLDivElement>(null);
const observer = new ResizeObserver(([entry]) => {
if (spacerRef.current) {
spacerRef.current.style.height = `${entry.target.getBoundingClientRect().height}px`;
}
});
useEffect(() => {
if(!targetRef.current) return
const el = targetRef.current
observer.observe(el)
return () => {
observer.unobserve(el)
}
}, [])
return { spacerRef, targetRef };
};
이제 CTA 버튼을 손쉽게 구현할 수 있습니다! 👏