업무에서 페이지 슬라이딩 애니메이션을 구현할 일이 생겼는데 개발 중에 예상하지 못한 문제가 많이 발생했고, 해결하면서 배운 것들이 많았다.
프레임워크는 Next.js를 사용하고 요구사항은 다음과 같다.
[요구사항]
- 개발할 페이지는 아이템 목록 페이지, 아이템 상세 페이지
- 목록 페이지에서 상세 페이지 이동 시 목록 페이지의 스크롤 위치, 상태값 등 유지해야 함 (돌아왔을 때 모든 것이 그대로여야 함)
- 상세 페이지는 화면 오른쪽 끝에서 왼쪽으로 슬라이딩으로 부드럽게 와야 함
- 상세 페이지가 슬라이딩될 때 목록 페이지가 겹쳐서 보여야 함.
- 목록 페이지에서 상세 페이지로 이동한 뒤, 다시 뒤로가기를 통해 목록 페이지로 이동했다면 목록 페이지에서는 상세 페이지로 앞으로가기는 불가능(브라우저 이벤트 자체를 막아야 함)
[해결 과정]
1. 목록 페이지, 상세 페이지 총 페이지를 pages 폴더에 2개로 나눠서 생긴 문제
Next.js로 구현하다 보니 처음엔 pages 폴더에 페이지를 2개 각각 생성하였는데 이 때문에 큰 문제가 발생했다.
Next.js 12버전이기 때문에 페이지 이동마다 모든 상태가 초기화되고 새로 렌더링 되는데
이렇게 되면 페이지 이동 간에 상태를 유지하기 위해 리덕스 같은 전역 스토어에 모든 상태를 저장시켜야 했고 페이지 안에 상태값에 따라 플로팅으로 인해 레이아웃 시프트 현상이 있기 때문에 사용자에게 시각적으로 불편함을 줄 수 있는 문제가 있었다.
그래서 고민 끝에 하나의 페이지로 변경했고 원하던 UX를 달성했다.
하나의 페이지로 구현하면 router를 사용하지 못하는 것 아닌가 하는 걱정이 있었지만
페이지 구분은 쿼리 파라미터로 하고 next/router는 이런 경우를 위해 shallow 옵션과 scroll 옵션 같은 것들을 지원하기 때문에 라우팅 인터페이스가 변경되는 일은 없었다.
2. ios webview에서 렌더링 성능이 떨어지는 문제
상세 페이지로 라우팅 될 때 목록 페이지에서 상세페이지를 플로팅 시켜서 천천히 화면 오른쪽 끝에서 왼쪽으로 슬라이딩 되는 애니메이션이 요구사항이었다.
css translateX로 큰 문제 없이 구현했지만 크롬에서는 성능 문제가 없었지만 Ios webview에서는 애니메이션이 부드럽지 않고 끊겨서 보이는 문제가 있었다.
이걸 해결하기 위해 css 대신 자바스크립트로 해결했다.
const slidingPopupPageRef = useRef(null);
const ANIMATION_SLIDING_DURATION = 300;
useEffect(() => {
let startTime;
const slideAnimation = (timestamp) => {
if (!startTime) startTime = timestamp;
const progress = timestamp - startTime;
if (progress < ANIMATION_SLIDING_DURATION) {
const translateAmount = `-${progress / (ANIMATION_SLIDING_DURATION / 100)}vw`;
slidingPopupPageRef.current.style.transform = `translateX(${translateAmount})`;
window.requestAnimationFrame(slideAnimation);
} else {
slidingPopupPageRef.current.style.transform = `translateX(-100vw)`;
}
};
if (loanApplyId) {
window.requestAnimationFrame(slideAnimation);
}
}, [loanApplyId]);
return (
<div ref={slidingPopupPageRef}>
<DetailPage />
</div>
);
브라우저 API인 requestAnimationFrame을 이용했는데 일반적으로 setInterval로 구현하는 것보다 콜백 실행을 보장하여 애니메이션이 부드럽게 보이도록 해준다. 이 API를 직접 사용할 필요는 없다. react-spring, framer 같은 애니메이션 라이브러리들이 기본적으로 이 API를 이용하여 좀 더 사용하기 쉽게 해준다.
3. ios 스와이프 액션 시 이전 페이지 스냅샷이 보이지 않는 문제
이건 ios 개발자와 협업하여 해결했다.
wkwebview에서는 ios swipe 액션을 비활성화 할 수 있는데 비활성화된 상태로 웹뷰에서 페이지가 넘어가면 이전 페이지의 스냅샷이 보이지 않는 이슈가 있다. 이건 next/router의 routerChangeStart 이벤트를 이용해 페이지가 떠날 때만 스와이프 액션을 켜는 식으로 해결했다.
useEffect(() => {
// 처음 페이지 진입 시 스와이프 비활성화
NativeController.setUseBrowserBackAction({ enable: Boolean(loanApplyId) });
const handleTurnSwipeOnOff = () => {
// 라우팅 시작 시 스와이프 활성화
NativeController.setUseBrowserBackAction({ enable: true });
};
router.events.on('routeChangeStart', handleTurnSwipeOnOff);
return () => {
router.events.off('routeChangeStart', handleTurnSwipeOnOff);
};
}, [loanApplyId]);
'업무' 카테고리의 다른 글
React hook 리팩토링 하기 (xstate) (0) | 2023.03.03 |
---|---|
react-query에서 Throttling API 호출 구현하기 (leading, trailing) (0) | 2023.02.22 |
BFF 활용하여 프론트엔드만을 위한 API 만들기 (0) | 2022.11.01 |
requestIdleCallback을 활용한 렌더링 성능 개선 경험 (1) | 2022.10.07 |
[Next.js] Script 추가하기 (채널톡 추가하기, next/script) (0) | 2022.09.28 |