요즘 Semantic Web과 동시에 대두되는 개념이 Interactive Web이다.
사용자 행동에 따라서, 또 콘텐츠의 변경에 따라서 동적으로 변화하는 웹페이지를 인터랙티브 웹이라고 한다. 이번 아티클에서 나는 좀더 광의로 해석해서 사용자의 디바이스에 따라서도 '동적'(콘텐츠의 동적 변화와는 의미가 좀 다르긴 하나)으로 변화하는 기술인 Responsive Web 기술 또한 인터랙티브 웹 개념에 포함시켜보기로 했다.
미디어 쿼리를 사용한 반응형 웹 구현
Responsive Web을 구현하는 핵심적인 기술은 사용자가 보는 화면의 크기를 인식하고, 이에 맞추어 화면 구성요소의 크기나 레이아웃에 변화가 발생하는 것이다.
이때 사용자가 보는 화면이 어떻게 생겼는지 알 수 있는 방법에는 여러 가지가 있는데, user-client를 분석하는 방법(여기에는 접속한 기기 정보가 담겨있다.), viewport 사이즈를 활용하는 방법이 주로 사용된다.
Viewport?
뷰포트는 사용자 기기의 화면 크기를 나타내는 수치로, px(픽셀)단위를 통해 나타낸다. 이는 화면의 화소 수 픽셀과는 전혀 상관없고, 기기 제조사가 해당 기기의 화면크기를 적절히 나타내기 위해 부여해두는 값이다. 보통 Viewport를 기준으로 Breakpoint(편집점)을 걸어서 해당 점을 기준으로 몇 가지의 레이아웃 중 적절한 것을 사용하도록 한다.

출처: Apple Developer
예전에는 모바일-태블릿 Breakpoint를 375px로 잡는 경우가 흔했으나 (아이폰은 320px이었던 시절)
요즘에는 스마트폰이 점점 커지면서 모바일-태블릿 Breakpoint 400px : 태블릿-데스크톱 Breakpoint 768px 정도를 걸기도 한다.
미디어 쿼리를 통해 Viewport Size에 맞추어 화면 변환이 일어나는 리액트 컴포넌트를 만들어보자.
return (
<SectionLayout
outerLayerClassName="h-fit md:h-auto"
innerLayerClassName="h-fit py-20 md:py-16"
>
<div className="w-full h-full flex flex-col gap-20 md:gap-10">
{/* Header Section */}
<div className="w-full flex flex-col md:flex-row md:justify-between md:items-center gap-4 md:gap-0">
<div className="space-y-2 md:space-y-6 text-center md:text-left">
<h3 className="text-2xl md:text-4xl font-extrabold text-neutral-800 dark:text-white">
사주 공유하기
</h3>
<p className="text-sm md:text-xl font-bold text-neutral-800 dark:text-white">
채팅으로 사주를 공유해보세요
</p>
</div>
<a href="/chat">
<Button
className="w-[180px] h-[40px] text-sm md:w-[250px] md:h-[50px] md:text-base"
isRounded={true}
>
1:1 채팅 하러가기
</Button>
</a>
</div>
{/* Card Section */}
<div className="flex flex-col md:flex-row gap-4 md:gap-10 justify-center">
{shareCardInfo.map((card) => (
<ShareCard
key={card.title}
title={card.title}
description={card.description}
img={card.img}
/>
))}
</div>
</div>
</SectionLayout>
);
};
const ShareCard = ({ title, description, img }) => {
return (
<div className="flex flex-col rounded-xl shadow-md w-full max-w-[350px] md:max-w-[450px] overflow-hidden dark:bg-neutral-400">
<img src={img} alt={title} className="w-full object-cover" />
<div className="p-4 md:p-5 flex flex-col items-start gap-1.5">
<h4 className="text-sm md:text-base font-normal text-neutral-800">
{title}
</h4>
<p className="text-base md:text-xl font-extrabold text-neutral-800">
{description}
</p>
</div>
</div>
);
TailwindCSS는 모바일 반응형을 아주 쉽게, 잘 지원한다.
'md:'로 시작하는 클래스를 넣으면 Mobile Device(Viewport Width 375px 이하) 가 감지되었을 때 해당 속성으로 변한다. 물론 설정값을 어떻게 수동으로 건드려 볼 수는 있지만, 아이폰 16이 이미 Viewport Width가 390px이 되어버린 시점에서 아직 375px을 디폴트값으로 쓰고 있는 게 괘씸하다.
애니메이션
이제 애니메이션을 구현해보자. 보통 스크롤이나 호버에 따라서 반응형 애니메이션을 구현하고, 이외에 loop설정을 통해서 지 혼자 계속 되풀이되는 애니메이션을 넣는 경우도 있다.
오늘은 간지나는트렌디한 웹페이지를 위해 부드러운 스크롤 애니매이션을 구현해볼 것이다.
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
handleScroll을 사용하면 브라우저가 빠르고 똑똑하게 현재 스크롤 위치를 보고해준다. 겁나게 빠르기 때문에 그대로 useEffect에 갖다 넣어버려도 좋다.
gsap
npm 라이브러리 gsap은 자기네 소개글에 의하면
"GSAP is an industry standard JavaScript animation library from GreenSock that lets you craft high-performance animations that work in every major browser."
산업 표준 JS 애니메이션 라이브러리이다.
npm install @gsap/react
설치부터 해 주고,
const container = useRef();
const { contextSafe } = useGSAP({ scope: container });
const onClickGood = contextSafe(() => {
gsap.to('.good', { rotation: 180 });
});
return (
<div ref={container}>
<button onClick={onClickGood} className="good"></button>
</div>
);
출처: gsap docs
누르면 180도 회전하는 버튼을 생성한다.
위에 언급한 기술들을 적용해서 멋사 프로젝트를 하나 해 봤다.
이름은 "멋쟁이 사주처럼"