❓문제 상황
게시글 상세 페이지에서 질문 본문과 답변 본문에 각각 코멘트가 달려있다. 이 코멘트가 페이지가 렌더링 되면서 동시에 답변의 갯수만큼 조회 요청을 보내는데, 문제는 swr 을 적용하면서 페이지를 클릭하거나 이런 변동이 있을때마다 다시 자동으로 요청을 보내 갱신해주기 때문에, 답변이 많아질 경우 서버에 부담이 우려되는 상황이다.
사실 원래는 답변 하나당 미리보기 코멘트 하나를 같이 응답해주고 있어서, 답변 리스트만 조회하면 펼치기 버튼을 누르기 전까지는 comments 에 대한 조회 요청이 갈 필요가 없다. 그래서 이 부분을 고쳐보려고 한다.
💡해결 과정
일단 해야할 일은 아래와 같다.
- 첫 미리보기 코멘트는 코멘트 존재 여부와 상관없이 답변 리스트에서 응답받은 데이터로 화면에 렌더링해준다.
- 펼치기 버튼을 클릭했을 때만 나머지 답변이 요청되도록 한다.
현재 상태는 CommentContainer 안에 CommentList 와 CommentTextArea 가 있고, CommentList 안에서 comments 데이터로 map 을 실행해 각 Comment 컴포넌트를 렌더링 하고 있는 상황이다.
왜 이렇게 코드를 짰냐면, 크게 두 가지 상황에 대해서 고려할 필요가 있었다.
1. 답변에 코멘트 데이터가 존재할경우 코멘트 데이터와 펼치기 버튼을 보여준다. 코멘트가 0개일 경우 코멘트를 작성할 수 있는 textArea 를 보여준다.
2. 코멘트 리스트에서 isOpen === true 상태에서는 전체 코멘트 목록과 코멘트 작성 textArea 를, 반대의 경우 코멘트 1개에 대한 미리보기만 렌더링한다.
그래서 코멘트 컨테이너와 코멘트 리스트를 분리했고, 또 그 코멘트 리스트 안에서도 코멘트가 있는 경우 코멘트의 0번째 요소만 보여지도록 작성했다.
일단 해당 문제점은 코멘트가 존재하던 존재하지 않던 무조건 코멘트에 대한 조회 요청을 해서 props로 내려준다는 점이다. 이 부분 때문에 여러번의 요청이 발생하는 것 같다. 그래서 상태가 isOpen이 되었을 때 데이터를 요청하도록 변경해주어야 할 것 같다.
그리고 코드를 다시 보면서 추가적으로 몇 가지 문제점을 발견했는데... 일단 컴포넌트 구조를 한 눈에 파악하기가 너무 어렵다. 펼침과 닫힘 상태를 표현하는 isOpen 을 전역상태로 뺀 이유가 뭔지 모르겠다;; 또 answer 응답에서 이미 commentPreview 라는 필드에 미리보기 데이터를 주고 있는데 굳이 다시 요청을 보내서 0번째 요소를 렌더링 하는게 의미 없는 중복인 것 같다. 아마 isOpen 상태를 변경했을 때 미리 보여주고 있던 첫 번째 코멘트를 숨겨서 나머지 코멘트가 자연스럽게 보이기 하기 위함이었던 것 같다.
으 그래서 일단 지금은 머리가 안돌아가서 여기까지 고민했고, 내일 리팩토링 시간에는 컴포넌트 구조도부터 다시 작성해서 좀 더 효율적으로 바꿔보려고 한다.
✨결과
const CommentList = ({ comments }: CommentList) => {
const [isOpen, setIsOpen] = useState(false);
return isOpen ? (
<>
{comments.map((comment: CommentResp) => (
<Comment
key={comment.commentId}
commentId={comment.commentId}
articleId={comment.articleId}
content={comment.content}
createdAt={comment.createdAt}
lastModifiedAt={comment.lastModifiedAt}
userInfo={comment.userInfo}
avatar={comment.avatar}
answerId={comment.answerId}
/>
))}
<button className="text-base" onClick={() => setIsOpen(false)}>
{`닫기 `}
<FontAwesomeIcon icon={faChevronUp} />
</button>
</>
) : (
<>
<Comment
key={comments[0].commentId}
commentId={comments[0].commentId}
articleId={comments[0].articleId}
content={comments[0].content}
createdAt={comments[0].createdAt}
lastModifiedAt={comments[0].lastModifiedAt}
userInfo={comments[0].userInfo}
avatar={comments[0].avatar}
answerId={comments[0].answerId}
/>
{comments.length > 1 && (
<button className="text-base" onClick={() => setIsOpen(true)}>
{`펼치기 `}
<FontAwesomeIcon icon={faChevronDown} />
</button>
)}
</>
);
};
export const CommentContainer = ({ answerId }: CommentContainerProps) => {
// 현재 게시글 id
const router = useRouter();
const { articleId } = router.query;
// 답변 코멘트 || 질문 코멘트에 따른 요청 url
const url = answerId
? `/api/answers/${answerId}/comments`
: `/api/articles/${articleId}/comments`;
// 코멘트 조회 요청 로직
const { data: comments } = useFetch(url) || [];
return (
<>
<h3 className="text-xl font-bold">{comments?.length || 0} 코멘트</h3>
<section className="space-y-8 pt-4">
{comments?.length > 0 && <CommentList comments={comments} />}
<CommentTextArea answerId={answerId} />
</section>
</>
);
};
열리고 닫히는 상태의 위치를 리스트 컴포넌트로 옮겼다. 위에서 전역 상태로 뺀 이유를 전혀 몰랐었는데, 전역 상태로 관리하지 않아도 전혀 문제가 없었다. 오히려 전역 상태로 관리했을 경우 한 컴포넌트에서 상태를 변경했을 때 모든 컴포넌트의 상태가 변하는 문제점이 있었다. 이런 점을 제외하면 굳이 전역상태 + props 로 전달할 필요가 없어서 수정했다.
'프로젝트 > 모락모락' 카테고리의 다른 글
모락모락 리팩토링 - 리팩토링 과정 기록 (디렉토리 구조, 아키텍처, 중복코드, 버그 픽스 등) (0) | 2023.02.13 |
---|---|
[Project] 모락모락 프로젝트 5주차 회고😸 (0) | 2022.12.04 |
[Project] 모락모락 프로젝트 4주차 회고😸 (0) | 2022.12.04 |
[Project] 모락모락 프로젝트 1,2,3 주차 회고 (중간 회고 ^^😸) (0) | 2022.11.20 |
[Project]스택오버플로우 클론코딩 3주차 회고 (0) | 2022.11.06 |