Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: 리스트 상세 페이지 기능 구현(리스트 삭제, 존재하지 않는 리스트 처리, 유저 정보에 따른 ui 처리) 및 리팩토링 #31

Merged
merged 85 commits into from
Feb 15, 2024

Conversation

Nahyun-Kang
Copy link
Contributor

@Nahyun-Kang Nahyun-Kang commented Feb 14, 2024

개요

  • 개요는 변경 사항 및 관련 이슈에 대해 간단하게 작성해주세요.
  • 해당 PR에 대한 리뷰어와 라벨을 생성해 주세요.

작업 사항

  • QA 전에 필요한 전반적인 기능을 수정, 리팩토링하였습니다.

기능 구현

  • 리스트 삭제 모달 추가
  • 존재하지 않는 리스트 (404) 에러 처리
  • 콜라보레이터일 경우에도 리스트 수정이 가능하도록 처리(리스트 삭제는 disabled)
  • 유저 로그인 정보에 따른 비회원, 회원 ui 분기 처리
  • 공용 헤더 반영
  • 댓글 및 답글 생성, 삭제, 조회 기능 구현
  • 댓글 무한스크롤, 스켈레톤 ui
  • 논리 삭제된 댓글(즉, 답글이 남아있는 댓글)에 대한 처리

리팩토링 관련

  • 1차 mvp QA 내용 반영
  • 전반적인 디자인 여백 수정
  • 필요없는 코드 삭제
  • 타입 정리(_api 부분과 유진님께서 정리해주신 타입 반영)
  • 코드에 대한 주석 추가
  • css 공용 색상 반영

참고 사항 (optional)

  • QA 전에 최대한 할 수 있는 것까지는 해보았는데 놓친 부분이 있을 수 있습니다..🥹

관련 이슈 (optional)


스크린샷


리뷰어에게

  • @kanglocal 바텀시트 부분 disabled 처리를 위해 코드가 추가되었고, 리스트 상세 조회 api에 listId가 따로 없어서 ListDetailInner 부분에 일부 코드를 추가해주었습니다..! 확인해주시고 수정할 부분 있으면 말씀해주시면 감사드리게습니다!🙇‍♀️

@Nahyun-Kang Nahyun-Kang added Feat 구현 Refactor 리팩토링 Design UI 수정 Style 코드 스타일 변경 labels Feb 14, 2024
Copy link

vercel bot commented Feb 14, 2024

@Nahyun-Kang is attempting to deploy a commit to the Eujin Ahn's projects Team on Vercel.

A member of the Team first needs to authorize it.

@@ -34,6 +34,7 @@ export default function TempLayout({ children }: { children: ReactNode }) {
<QueryClientProvider client={queryClient}>
<div id="modal-root" />
<div>{children}</div>
<ToastContainer />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다시 복구해놓았습니다..🥹 죄송합니다..담부턴 꼼꼼히 체크할게용

Copy link
Contributor

@Eugene-A-01 Eugene-A-01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

84개의 커밋..
제인생에 이런 숫자를 다시 볼 수 있을까요
귀하네요..✨

Copy link
Contributor

@seoyoung-min seoyoung-min left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님 정말,,, 커밋수 보고도 놀랐지만 코드 보고도 너무 놀랐어요..!
정말 엄청 많은 경우의 수와 상황이 있을 것 같은데, 너무 고생하셨고 감사합니다🙇‍♀️🥹💗


async function createComment({ listId, comment }: CreateCommentType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➡️ 화살표 함수로 바꿔야 할 것 같아요~!


//리스트 상세 페이지 리스트 조회 api
async function getComments({ listId, cursorId }: GetCommentsType) {
const params = new URLSearchParams({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우와 이렇게 객체형식으로 넣어주면 쿼리 형태로 바꿔 주는가봐요!! 배워갑니다!!💗
+) 댓글 받아오는 사이즈를 5개로 정하신 이유가 (단순) 궁금합니다!!👀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(저도 소현님 PR로 어깨넘어 배운건데 감사합니다 ㅎㅎ👍) 5개로 한 이유는 무한스크롤이 잘 되는지 확인하기 위해 5개로 설정한 것이었는데 IA 보고 공통 개수로 수정해두겠습니다..!! 발견 감사드려용..👍

Comment on lines +79 to +92
export const disabledSheetItemWrapper = style({
':hover': {
backgroundColor: '#ffffff',
},
});

export const disabledSheetItem = style({
cursor: 'not-allowed',
selectors: {
[`${sheetItemWrapper}:hover &`]: {
color: '#e9e9e9',
},
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요기도 공용색상 사용해도 좋을 것 같아요! 🎨

var.color.white
var.color.gray5

import BottomSheet from '@/app/[userNickname]/[listId]/_components/BottomSheet/BottomSheet';
import ModalPortal from '@/components/ModalPortal';
import BottomSheet from '@/app/user/[userId]/list/[listId]/_components/BottomSheet/BottomSheet';
import ModalPortal from '@/components/modal-portal';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modal-portal을 케밥으로 해주신 이유가 무엇인지 궁금합니다!!🙇‍♀️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 서영님 혹시 모달포탈이 케밥으로 되어있는 부분이 어디일까용..?! 🥲

Comment on lines +17 to +25
<Image
src={item.profileImageUrl}
className={styles.profileImage}
alt="사용자 프로필 이미지"
width={25}
height={25}
style={{
objectFit: 'cover',
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 아는 바로는 obejectFit 속성은 Image컴포넌트 사이작 fill일때 적용이 되는 것 같아요~! 부모div를 만들어 사이즈 지정 & position:relative 주고, Image 컴포넌트는 fill 속성으로 대체하면 어떨까요??

Suggested change
<Image
src={item.profileImageUrl}
className={styles.profileImage}
alt="사용자 프로필 이미지"
width={25}
height={25}
style={{
objectFit: 'cover',
}}
<div className={styles.profileWrapper} > {/*프로필이미지 부모. width=25, height=25, position:relative*/}
<Image
src={item.profileImageUrl}
className={styles.profileImage}
alt="사용자 프로필 이미지"
fill
style={{
objectFit: 'cover',
}}
</div>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오매낫 사용법을 제대로 모르고 사용하고 있었군용.. 🫠 정말 감사합니다.. 당장 바꾸겠습니다!

Comment on lines +56 to +61
const comments = useMemo(() => {
const totalCount = commentsData ? commentsData.pages[commentsData.pages.length - 1].totalCount : 0;
const commentsList = commentsData ? commentsData.pages.flatMap(({ comments }) => comments) : [];
return { commentsList, totalCount };
}, [commentsData]);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useMemo를 이럴때 이렇게 쓰는거군요! 배워갑니다🥹👍👍
flatMap이라는 건 처음 보네요!
와 저에게는 엄청 어렵습니다! 이따 직접 여쭙겠습니다 🥰
(완료~! 감사합니다 🙇‍♀️)

setCommentId(id);
};

//댓글 폼 사용(추후 리액트 훅폼으로 수정해 볼 예정)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어서 리액트 훅 폼의 세계로...⭐️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우왓 스켈레톤 까지!!

<button className={styles.buttonResetStyle} onClick={handleHistoryButtonClick}>
<HistoryButton alt="히스토리 버튼" />
</button>
{/* {리스트 관리 버튼은 리스트 오너일 때만 보이게 하기} */}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 너므 사소하지만 주석 "리스트 오너, 콜라보레이터"로 바꿔도 좋을 것 같아요!!

Comment on lines +37 to +43
<SwiperSlide
key={item.id}
className={styles.swiperSlide}
onClick={() => {
router.push(`/user/${item.ownerId}/list/${item.id}`);
}}
>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍👍

@Nahyun-Kang Nahyun-Kang merged commit 9e6128b into 8-Sprinters:dev Feb 15, 2024
0 of 2 checks passed
@Nahyun-Kang Nahyun-Kang deleted the feature/list-detail-outer-api branch February 15, 2024 08:24
Copy link
Contributor

@ParkSohyunee ParkSohyunee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님, 리뷰 늦게 드려 죄송합니다..
댓글/답글 로직, 권한, UI, 인터랙션 등 디테일한 부분이 많았을텐데 구현하시느라 정말 고생많으셨습니다!! 👍🥰 덕분에 많은 부분 얻어갑니다!! 감사합니다. 🙇‍♀️

Comment on lines +7 to +15
`/lists/${listId}/comments`,
{
content: comment,
},
{
headers: {
'Content-Type': 'application/json',
},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님, axios 요청보낼때 content와 headers를 따로 명시하신 이유가 궁금합니다!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요청이 안 가지길래 요 문제인 줄 알고 추가해봤는데 아니었더라구요... 삭제해두겠습니다!!

Comment on lines +3 to +6
interface CreateReplyType {
listId: number | undefined;
commentId: number | undefined | null;
data: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined는 옵셔널로 수정해도 좋을 것 같습니다!!

Comment on lines +9 to +12
async function createReply({ listId, commentId, data }: CreateReplyType) {
if (commentId === null) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님 궁금한점이 commentId가 null일 수도 있는 것인가요?? 댓글이 삭제된 경우를 의미하는 것인가용??

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 저 commentId는 댓글 작성중일 때를 의미합니당..! 하나의 폼에서 댓글 / 답글 요청을 다 보내다보니 답글 작성중일 때 어떤 댓글에 대하여 답글을 작성중인지 정보가 필요해서 setState로 관리해주고 있었습니다..! 근데 변수명이 오해의 소지가 있는 것 같아서 CurrenCommentId 요런 느낌으로 고쳐봐야겠습니다!

Comment on lines +9 to +14
async function deleteComment({ listId, commentId }: DeleteCommentType) {
const response = await axiosInstance.delete(`/lists/${listId}/comments/${commentId}`);
return response.data;
}

export default deleteComment;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete 후에 response로 받는 데이터가 있는 것인지 궁금합니다!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

흑흑 코드 복붙의 흔적이 여기저기(Post 요청에서 가져온 것 같습니당).. 깔끔하게 정리를 못해서 부끄럽습니다.. 감사합니다!!

Comment on lines +4 to +5
//댓글 삭제 api
async function deleteList(listId: string | undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님, undefined는 옵셔널로 처리해 주는 것이 더 좋을 것 같은데 나현님 생각은 어떠신가요..? 🤔

Comment on lines +111 to +122
//댓글/답글 폼 submit 함수
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!userId) {
return;
}
if (commentId && activeNickname) {
createReplyMutation.mutate();
return;
}
createCommentMutation.mutate();
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님, 조건에 따른 댓글/답글 로직이 복잡하셨을 것 같은데 해당 컴포넌트에서 깔끔하게 정리해 주신 것 같습니다!👍👍🏄‍♀️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사실 소현님 PR 보고 많이 참고했습니다..!!🥰 제 교재같은 소현님 PR..

Comment on lines +34 to +35
enabled: !!params?.listId,
retry: 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍👍

Comment on lines +38 to +43
//리스트 생성자 제외한 사람들만 콜라보레이터들로 설정
const filteredCollaboratorsList = list?.collaborators.filter((item: UserProfileType) => item?.id !== list.ownerId);
//리스트 오너가 아니고 콜라보레이터인 경우에 권한을 설정하기 위한 변수
const isCollaborator: boolean | undefined =
list?.collaborators.some((item: UserProfileType) => item?.id === userId) && userId !== Number(params?.userId);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와우! 이렇게 조건에따라 변수에 먼저 할당하는 부분 너무 좋은 것 같습니다!☺️
+. 사소하지만 변수명 collaborators와 list가 중복된 느낌이라서 filteredCollaborators 로만 이름 지어줘도 좋을 것 같습니다.
+. some을 사용하신 점 좋네요!

Comment on lines +47 to +58

if (error && error?.message.includes('404')) {
return (
<Modal handleModalClose={handleSetOff}>
<Modal.Title>이 리스트는 삭제 또는 비공개 처리 되었어요.</Modal.Title>
<Modal.Button onCancel={handleSetOff} onClick={handleConfirmButtonClick}>
확인
</Modal.Button>
</Modal>
);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님 이 부분이 리스트 없을때 커스텀 404인 것인가요? 👍
(+. 내일 자세하게 여쭤봐도 될까요?? ㅎㅎ)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵..!! 페이지로 따로 뺄까 고민하다가 로직이 이게 맞는지 확신이 들지 않아서 요렇게 썼습니당... 더 좋은 방향으로 바꿔볼 수 있는 피드백이 있다면 언제든지 말씀해주세요..!!🥹

Comment on lines +3 to +6
const useInfiniteScroll = (handler: () => void) => {
const ref = useRef<HTMLDivElement | null>(null);

//scroll로 한 번 도전해봤는데 ref wrapper 자체가 clientHeight보다 작으면 문제가 생겨서 document로 걸어주었습니다 ㅠ
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

나현님 혹시 해당 hook은 어디서 사용되는 것인가용? 다른 코드에서 못본것 같아서 확인차 여쭤봅니다!!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

소현님 훅 만드신 거 보자마자 버린 파일인데 삭제를 못했네요..🥲 얼른 잽싸게 지우겠습니다!!

@Nahyun-Kang
Copy link
Contributor Author

Nahyun-Kang commented Feb 16, 2024

@seoyoung-min @ParkSohyunee @Eugene-A-01 시간내주셔서 꼼꼼하게 리뷰해주셔서 감사합니다!! 소중한 의견들 모두 반영해서 다음 PR 때 올리도록 하겠습니다🙇‍♀️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design UI 수정 Feat 구현 Refactor 리팩토링 Style 코드 스타일 변경
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants