'롤링' 프로젝트가 거의 끝나갈 때 쯤 유저가 경험하는 사용 플로우대로 쭉 사용해보며 ‘롤링’을 점검하고 있었다. 분명 시안대로 잘 만들었고, 기능상으로도 전혀 문제가 없어보였다. 그런데 뭔가 심심한 느낌이 들었다. 바로 호버 효과의 부재였다. 버튼 같은 경우는 호버되면 옅은 회색으로 반응이 나오는데, 사용자가 가장 많이 클릭해볼 캐러셀의 카드, 메세지 카드 등에는 호버 효과가 전혀 없었던 것이다. 평소 웹사이트를 사용할 때 크게 의식하던 부분은 아니었는데 막상 없으니까 심심한 느낌이 들었던 것 같다. 고민하던 차에 팀원분께서 ‘framer-motion’이라는 라이브러리를 알려주셔서 이를 사용해서 프로젝트 전반에 애니메이션을 적용해보기로 했다.
호버 애니메이션은 사용자의 마우스 움직임에 ‘즉각적인 반응’을 제공한다. 웹사이트 전반에는 이러한 ‘즉각적인 반응’이 필수라고 생각한다. 마우스를 올렸을 때, 또는 회원가입이 성공했을 때, URL 복사가 성공했을 때, 비밀번호 변경이 완료되었을때, 등등.. 사용자가 서비스에서 하는 모든 행동에는 즉각적인 안내가 있어야하고 그 수단은 토스트, 모달, 하단 팝업, 호버 인터랙션 등등으로 다양할 수 있다. 프론트엔드 개발자는 그 수단을 디자이너와 협업하여 기술적인 제안을 할 줄 알아야 한다고 생각한다.
그러면 프로젝트를 사용해본 유저의 입장에서 ‘내가 각 지점마다 어떤 반응을 기대했는지’로 거슬러 올라가면 될 것 같았다
팝오버 컴포넌트들 부드럽게 튀어나오게 하기
‘롤링’에는 다양한 팝오버 컴포넌트들이 있었다. ‘팝오버 컴포넌트’란 원 페이지에는 보이지 않다가 사용자가 특정 버튼을 누르면 그제서야 더 자세한 내용을 보여주는 컴포넌트를 말하는데, 이러한 컴포넌트들이 너무 사용자에게 갑작스럽게 튀어나온다는 인상을 받았었다.
// modal.jsx
<motion.dialog
initial='hidden'
animate='visibleSmoother'
variants={popover}
className={clsx(styles.modal, className)}
{...props}
ref={ref}
>
{children}
</motion.dialog>
framer motion 사용은 정말 편했다! 기존 컴포넌트의 최상위를 motion 태그로 감싸주고, initial, animate, variants 등의 속성을 부여하면 되는 식이었다. 팝오버 컴포넌트는 페이지 곳곳에 있었기 때문에 일관된 스타일 적용을 위해 popover라는 variants를 제작해뒀다.
popover 라는 variants는 아래같은 모습이었는데, 사용할 땐 initial, animate 부분에 어떤 값을 사용할 지 지정하면 됐었다.
// framerAnimation.js
const popover = {
hidden: { scale: 0 },
hiddenStateOfToast: { scale: 0, x: '-50%' },
visible: { scale: 1 },
visibleSmoother: {
scale: 1,
transition: { type: 'spring', stiffness: 260, damping: 25 },
},
};
export { hoverCard, popover, rotateAddIcon };
카드 컴포넌트들에 호버이벤트 확실히 주기
카드이벤트들엔 Hover시 애니메이션을 주고 싶었기때문에 motion 태그의 whileHover 옵션을 사용했다.
<motion.div whileHover='zoomedIn' variants={hoverCard}>
{children}
</motion.div>
역시 variants를 지정해서 애니메이션 재사용성을 높였다. 메인 페이지에 있는 카드 컴포넌트는 캐러셀 컴포넌트 내부에 있어서 호버시 확대되는 애니메이션을 적용해도 가장자리가 짤려 보이는 문제가 있었다. 따라서 해당 컴포넌트에도 호버이벤트를 적용하기 위해 두가지 scale을 1.02배 키우는 애니메이션, opacity를 0.75배로 조정하는 애니메이션 두 가지를 등록해두었다!
// framerAnimation.js
const hoverCard = {
zoomedIn: { scale: 1.02 },
controlOpacity: { opacity: 0.75 },
};
캐러셀에 있는 카드 컴포넌트의 경우 배경이미지가 모두 있었기 때문에, 크기를 키우는 것 보다 opacity 조정이 더 효과적일 것이라고 생각했다.
카드 추가하는 버튼엔 어떤 호버 이벤트를 줄 수 있을까?
그리고 이제 카드를 추가하는 버튼에 호버 이벤트를 추가하는 일만 남았다. 이 컴포넌트는 배경이 흰색 이었기 때문에 투명도를 조정하는 방식은 적절하지 않아보였다. 그리고 다른 카드들과 다른 종류의 것이기 때문에 카드와 같은 이벤트를 적용하는 것은 좀 심심했다.
그러다가 문득 + 아이콘이니까 90도로 회전시키는 애니메이션은 어떨까하는 생각이 들었다.
<motion.div
whileHover='rotate90'
variants={rotateAddIcon}
className={styles.link}
>
{children}
</motion.div>
const rotateAddIcon = {
rotate90: { rotate: 90 },
};




공식문서, 블로그 뒤져가며 프로젝트 막바지에 추가하게 된 기능이었는데, 팀원들의 반응이 좋아서 기분이 좋았던 경험이다. 내가 보기에도 조금 심심했던 인터랙션이 개선 된 것 같아서 기분이 좋았다. 또, 공식문서/블로그를 참조하며 새로운 라이브러리를 익히고 배우는 경험을 한 첫번째 경험이었다. 이 라이브러리는 이번 프로젝트에도 꼭 써보고 싶다.