728x90
초기 로직
✨게시글 댓글 페이지 구현을 하면서 limit 파라미터로 지정한 개수보다 댓글 수가 많아질 때 사용할 무한 스크롤에 대해 생각해보게 되었다.
🔖작성한 로직에 대한 고민
- 난 상세 페이지와 그 안의 댓글 컴포넌트를 따로 분리했기 때문에, 상세 페이지에서 가져오는 API 호출 로직을 통해 백엔드 서버에 구현되어 있는
nextCursor
값 또한 가져와 사용하고 싶었다. - 하지만 분리되어 있기도 하고 이걸 어떤식으로 구현해야 할지 감이 잘 오지 않았다. 🫨 해결한 과정들을 하나씩 작성해보고자 한다. 👍
// 상세 페이지 로직
const ArticleWithComment = () => {
...
const [comments, setComments] = useState<CommentResponse[]>([]);
const [article, setArticle] = useState<ArticleResponse | null>(null);
const getComments = async (id: string) => {
const { list } = await getArticleComments(id, 10);
setComments(list);
};
const getSingleArticle = async (id: string) => {
const data: ArticleResponse = await getArticle(id);
setArticle(data);
};
const setNewComment = (comment: CommentResponse) => {
setComments((prevComments) => [comment, ...prevComments]);
};
useEffect(() => {
if (id) {
getComments(id);
getSingleArticle(id);
}
}, [id]);
// 이런 식으로 값을 props로 내려주는 형태
return (
...
<ArticleComment comments={comments} setNewComment={setNewComment} />
...
);
// 댓글 컴포넌트 일부
function ArticleComment({ comments, setNewComment }: Props) {
...
return (
...
{comments.length !== 0 ? (
comments.map((comment) => {
return <CommentList key={comment.id} {...comment} />;
})
) : (
<Image
className={styles["no-comment-img"]}
src="/images/Articles/no-comment.png"
alt="댓글이 없을 때 출력되는 이미지"
width={151}
height={195}
/>
)}
);
}
✨useIntersectionObserver 훅 구현
- 우선 남아있는 댓글을 더 불러오기 위한 트리거를 만들어주기 위해, boolean 값을 리턴해주는 intersectionObserver 훅을 만들었다.
- intersectionObserver는 웹 API로, 요소가 뷰포트나 지정된 부모 요소와 교차하는지 여부를 비동기적으로 관찰할 수 있게 해준다.
- 난 이를 useEffect와 함께 사용하여 인자로 지정한 요소의 ref값을 받아, 지정한 요소를 관찰할때마다 true값이 된 isInteresting을 리턴하도록 구현했다.
// intersectionObserver
import { RefObject, useEffect, useState } from "react";
export default function useIntersectionObserver(ref: RefObject<HTMLDivElement>) {
const [isIntersecting, setIsIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) setIsIntersecting(true);
else setIsIntersecting(false);
});
},
{ threshold: 0.3 }
);
if (ref.current) observer.observe(ref.current);
return () => {
if (ref.current) observer.unobserve(ref.current);
};
}, [ref]);
return isIntersecting;
}
✨useIntersectionObserver 훅 사용
- 다음으로 훅을 사용할 컴포넌트를 정해야했다. 먼저 API를 호출하고 있는 상세 페이지에 먼저 사용하보려 했지만.. 따로 빼둔 댓글 컴포넌트가 댓글 리스트 요소말고도 다른 여러 요소를 포함하고 있어 사용 위치가 애매해졌다.
- 따라서 댓글 컴포넌트에 직접 사용하고, 상세 페이지의 관련 함수들을 props로 내려줘서 이를 사용 가능하도록 구현했다.
<ArticleComment
comments={comments}
setNewComment={setNewComment}
cursor={cursor}
getComments={getComments}
loading={loading}
/>
// 댓글 컴포넌트
const loadingRef = useRef(null);
const isInteresting = useIntersectionObserver(loadingRef);
useEffect(() => {
if (isInteresting && cursor !== null) {
getComments(cursor);
}
}, [isInteresting]);
❗발생한 문제
- 첫번째 문제는 API를 통해 list로 받은 배열을 바로 상태배열에 넣고 있어서 새로 받아온 배열을 넣을 시, 기존 배열에 배열을 또 넣는 꼴이라 에러가 발생했던 문제였다.
- 하지만 비교적 간단하게 스프레드 문법을 사용해서 list 배열을 분해하여 넣어주었더니 잘 동작했다.
const getComments = async (cursor: number | null) => {
const { list, nextCursor } = await getArticleComments(id, LIMIT, cursor);
if (list) {
setComments((prevComments) => [...prevComments, list]); // 요게 기존 로직
setComments((prevComments) => [...prevComments, ...list); // 새로 만든 로직
}
setCursor(nextCursor);
}
};
- 두번째 문제는 prevComments를 통해 바로 list를 넣다보니, 처음 list를 할당할 때 prevComments 때문에 중복되어 list가 두번 들어가는 것과 같이 보이는 경우가 생겼다.
isFirstLoad
라는 상태값을 만들고, 조건식을 통해 처음에만 list를 온전히 할당, isFirstLoad를 false로 만들고 다음부터 prevComments를 사용하는 로직으로 변경했다.
const [isFirstLoad, setIsFirstLoad] = useState(true);
const getComments = async (cursor: number | null) => {
const { list, nextCursor } = await getArticleComments(id, LIMIT, cursor);
if (list) {
setLoading(false);
if (isFirstLoad) {
setComments(list);
setIsFirstLoad(false);
} else {
setComments((prevComments) => [...prevComments, ...list]);
}
setCursor(nextCursor);
}
};
🏷️ 추가적으로 고민해볼 만한 사항
추가적으로 loading 상태값을 만들어 로딩되는 동안 페칭하도록 했는데, 댓글 수가 적어 불러오는 속도가 너무 빨라 잘 체감은 되지 않는다. 그리고 로직 자체를 잘 작성한 건지도 잘 모르겠어서 계속해서 사용자 경험을 긍정적으로 유도하기 위해 계속 고민해보는 것이 좋을 듯 하다.
그리고 로직 자체도 잘 짠 건지 모르겠어서 계속 고민해보는 것이 좋겠다.
우째하긴~ 이렇게 하믄 된다! 👍
'Front-End Study > 이건 우째해야 할까..' 카테고리의 다른 글
PATCH 메서드를 사용한 수정 로직 구현... 이건 우째 해야할까.. (4) | 2024.07.14 |
---|