프로젝트 작업을 하던 도중, 게시글의 댓글 목록에 Pagination을 적용하게 되었다. 공통 컴포넌트로 만들었던 Pagination 컴포넌트를 사용했는데 이 컴포넌트는 페이지 이동을 할 때마다 스크롤 위치를 최상단으로 이동하도록 동작하고 있었다.
그런데 이 동작은 Pagination을 게시글 목록 조회 같은 곳에서 사용하면 자연스럽지만, 게시글 영역의 아래에 위치하는 댓글 목록 조회에서 사용하니 조금 불편하다는 생각이 들었다. 특히 모바일 같은 경우에는 여러 페이지가 있을 때 다음 페이지 이동을 하려면 스크롤을 꽤 많이 내려야 했다. 또한 이는 게시글 본문 내용이 길어지면 더 심해질 것이다.
그래서 페이지 이동 시, '댓글 목록 최상단으로 스크롤 위치를 이동하면 어떨까?' 하는 생각을 했다. 게시글 영역의 높이가 고정되지 않았기 때문에 window.scrollY()와 같은 값을 통한 계산으로 스크롤 위치를 이동시키는 것은 쉽지 않을 것 같다고 생각했다. 마침 이전에 팀원이 useRef와 scrollIntoView로 추가 버튼을 누를 때마다 리스트 아래 새 아이템을 만들고 해당 위치로 이동하는 기능을 구현한 적이 있었다. 그래서 나도 useRef와 scrollIntoView로 스크롤 이동을 구현해 보기로 했다.
(@/components/Pagination.tsx)
interface PaginationProps {
currentPage: number; // 현재 페이지
...
scrollTarget?: React.RefObject<HTMLElement>; // 페이지 이동 시 스크롤 이동할 target
}
export default function Pagination({ currentPage, ... , scrollTarget }: PaginationProps) {
(...)
const handlePage = () => {
if (scrollTarget && scrollTarget.current) {
scrollTarget.current.scrollIntoView();
return;
}
window.scrollTo(0, 0);
};
return (
<div>
...
</div>
);
}
scrollIntoView에는 여러 옵션이 있으니 사용 전 공식 문서를 보는 것을 추천한다.
scrollTarget인 댓글 목록 컴포넌트
import { useRef } from 'react';
import Pagination from '@/components/Pagination';
export default function Comments({ postId }: { postId: number }) {
const scrollTargetRef = useRef<HTMLDivElement>(null);
(...)
return (
<div ref={scrollTargetRef} className='px-20 pt-20'>
<p className='font-semibold text-zinc-600 text-sm mb-16'>
댓글{' '}
<span className='text-primary-400'>{!isLoading && totalElements}</span>
</p>
<div>
{list.map(comment => (
<div key={comment.commentId} className='border-b py-16'>
<CommentCard
postId={postId}
comment={comment}
isMine={isAdminRole || user?.email === comment.writerEmail}
/>
</div>
))}
<Pagination
currentPage={page}
totalElements={totalElements}
scrollTarget={scrollTargetRef}
/>
</div>
...
</div>
</div>
);
}
이렇게만 하면 될 줄 알았는데, 묘하게 스크롤이 이동된 위치가 내 의도와는 맞지 않았다. 왜인가 했더니 상단에 position: fixed인 헤더가 있었기 때문이다! 헤더 높이를 생각하지 않고 댓글 목록을 viewport 최상단으로 올려버려서 헤더에 일부분이 가려지는 것이었다. 어떻게 해결해야 하나 찾아보던 중 mdn 문서에서 scroll-margin 옵션을 사용하면 사용자 정의 spacing을 설정할 수 있다는 내용을 발견했다.
scroll-margin 옵션이 없으면 기본적으로, scrollIntoView를 했을 때, 스크롤 가능한 상위 항목의 위쪽 (또는 아래쪽) 끝으로 이동된다고 한다. scrollTargetRef 요소에 scroll-margin-top을 헤더 높이만큼 설정해 주었더니 내가 원하는 대로 동작했다!
<div ref={scrollTargetRef} className='px-20 pt-20 md:scroll-mt-110 scroll-mt-80'>
이제 댓글 목록의 Pagination에서 페이지 이동 시 댓글 목록의 최상단으로 이동한다. 게시글 영역을 제외한 댓글 영역이 바로 보여서 페이지별 댓글 목록을 좀 더 편하게 조회할 수 있다.
참고
https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
'Develop > React' 카테고리의 다른 글
[React] Sentry, ErrorBoundary로 오류 대응하기 (0) | 2024.10.10 |
---|---|
Zustand 사용해 보기 (+ Redux와 비교) (0) | 2024.06.27 |
[React] 라이브러리 없이 Toast 구현하기 (2) | 2023.11.02 |
[React] ios 환경에서 input, textarea 화면 확대 방지하기 (+ 웹 접근성) (0) | 2023.10.13 |
[React] useReducer (0) | 2023.09.11 |