-
Notifications
You must be signed in to change notification settings - Fork 11
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
[4주차] 이규호 미션 제출합니다. #16
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시험기간이었는데 과제까지 정말 수고하셨습니다! 제가 애먹었던 부분들을 깔끔하게 해결하셔서 코드 리뷰하면서 많이 배워가요. 화면 구현 상에서 디테일적인 요소들을 많이 발견해서 "이 부분은 코드가 어떻게 되어있을까" 하는 기대감을 갖고 코드 리뷰했던 것 같습니다ㅎㅎ
interface FooterProps { | ||
page: string; | ||
} | ||
//page에 따라 버튼색 변환 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
page에 따라 색 변환을 이렇게 바로바로 지정할 수도 있었네요! 저는 따로 상태 관리하는 state를 선언했을 것 같은데 이렇게 바로바로 하는 게 깔끔하고 좋은 것 같습니다. 배워가요!!👍👍
//페이지가 불릴 때, 현재 user도 재설정 | ||
setNowUserId(roomUsers[0]); | ||
setTargetUserId(roomUsers[1]); | ||
}, []); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
페이지가 불러올 때 현재 user 재설정 하는 방식 좋네요! 저는 채팅방 나가는 버튼 누를 때 재설정할 수 있게 했었는데, 이렇게 useEffect 사용해서 불러오는 방식도 정말 좋은 것 같습니다. 😊
export const chatListByIdState = atomFamily({ | ||
key: "chatListByIdState", | ||
default: (chatRoomId: number) => { | ||
const chatRoom = chatData.chattings.find( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chatting 출력에도 recoil을 사용한 점 좋은 것 같습니다! 저는 atom만 썼었는데, atomFamily로 더 다양한 관리를 할 수 있다는 걸 이렇게 또 배워갑니다.
const allToggle = () => { | ||
setFavorite(false); | ||
}; | ||
return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 이 기능 정말 좋다고 생각했어요! 즐겨찾기 상태가 업데이트되면 알림 표시가 뜨고, 이를 확인하면 그 표시가 없어진 게 디테일한 부분까지 챙기신 게 느껴져서 이용하는 입장에서 재밌었어요. 저도 이 기능 활용해서 조금 더 디벨롭해보고 싶습니다 :)
@@ -0,0 +1,61 @@ | |||
export const chatBubbleTime = (chatDate: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 날짜 띄우는 부분에서 애를 많이 먹었는데, 이렇게 따로 파일을 빼니 훨씬 깔끔하고 좋네요. 날짜 표현 방식에 대한 다양한 경우를 모두 조건에 넣어주신 것 같아서 보고 많이 배웠습니다!
{dateChangeFormat(prevDate)} | ||
</Caption1> | ||
) : null} | ||
</DateCaption> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전 채팅과 비교해서 시간을 띄울 조건을 판단하는 코드를 이렇게 짤 수 있네요. 저도 비슷한 형식으로 tsx 안에서 map을 돌리며 함수를 넘겨주는 방식을 시도해봤었는데, 리렌더링이 너무 많이 된다고,, 에러를 띄워버려서 다시 갈아엎어야 하나 생각했었거든요🥲 구현 성공하신 코드 참고해서 조금 더 수정해봐야할 것 같습니다! 좋은 예시 보여주셔서 감사해요!
value={input} | ||
onChange={handleOnChange} | ||
></input> | ||
</StyledForm> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 디자인 QA로 input 창 안에서 줄바꿈이 가능했으면 좋겠다는 연락을 주셔서 알게 된 점이 있는데, 도움이 될까 해서 공유합니다! input 부분을 textarea로 설정하면 줄바꿈에 대한 옵션을 줄 수 있어서 좋더라구요. 따로 설정을 통해 shift + enter 누르면 줄바꿈, enter 누르면 전송 이런 식으로 좀 더 편리하게 채팅을 보낼 수 있습니당
} | ||
} | ||
touchStartX.current = null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모바일일 때는 터치로 설정 가능하게 빼두신 점 디테일 최고입니다👍👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 진짜 대박 ..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 프론트 운영진 배성준입니다!!!!!!!~!!
디자인도 이쁜데 이걸 그대로 구현하시다니~~~ 제 폰은 분명 갤럭시인데 아이폰 쓰는줄알았어요~~
즐겨찾기 기능 너무 신기하게 코드리뷰했습니다 ㅎㅎㅎ 좋아요 디테일까지 너무 좋아요( 이거 라임맞춘거임)
가독성이나 코딩스타일 관련해서 몇가지 코멘트 남겼습니다~~~참고만해주세요
이번 과제 정말 고생하셨습니다 다음과제도 기대만땅할게요~~
import { ReactComponent as People } from "../assets/icons/People.svg"; | ||
import { ReactComponent as Chat } from "../assets/icons/Chat.svg"; | ||
import { ReactComponent as Setting } from "../assets/icons/Setting.svg"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
svg들을 ReactComponent로 불러와서 사용하신 점 인상깊어요 !! 이렇게 여러개의 파일을 import해서 가져오면 너무 길어져서
react svg파일 관리 한번 읽어보시고 index.ts
생성해서 관리하시는 것도 참고해주세요!!
if (users[userId].name.includes(input)) { | ||
return true; | ||
} else { | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (users[userId].name.includes(input)) { | |
return true; | |
} else { | |
return false; | |
} | |
return users[userId].name.includes(input); |
이렇게 줄여서 사용할 수 있을 것 같아요!
if (searchInput === "") { | ||
return ( | ||
<ChatCardListWrapper> | ||
{chatList.map((value, index) => { | ||
return <ChatCard index={index} />; | ||
})} | ||
</ChatCardListWrapper> | ||
); | ||
} else { | ||
return ( | ||
<ChatCardListWrapper> | ||
{chatList.map((value, index) => { | ||
return nameSearch(value.userList[1]) ? ( | ||
<ChatCard index={index} /> | ||
) : null; | ||
})} | ||
</ChatCardListWrapper> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기는
if (searchInput === "") { | |
return ( | |
<ChatCardListWrapper> | |
{chatList.map((value, index) => { | |
return <ChatCard index={index} />; | |
})} | |
</ChatCardListWrapper> | |
); | |
} else { | |
return ( | |
<ChatCardListWrapper> | |
{chatList.map((value, index) => { | |
return nameSearch(value.userList[1]) ? ( | |
<ChatCard index={index} /> | |
) : null; | |
})} | |
</ChatCardListWrapper> | |
); | |
} | |
return ( | |
<ChatCardListWrapper> | |
{chatList.map((value, index) => { | |
if (searchInput === "" || nameSearch(value.userList[1])) { | |
return <ChatCard index={index} />; | |
} | |
return null; | |
})} | |
</ChatCardListWrapper> | |
); |
이렇게 중복을 없앨 수 있을 것 같아요
scrollbar-width: none; /* Firefox */ | ||
-ms-overflow-style: none; /* Internet Explorer/Edge */ | ||
&::-webkit-scrollbar { | ||
display: none; /* Chrome, Safari*/ | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
파이어폭스 엣지 크롬 사파리 모두 스크롤바없는거 확인했습니다 :)
import styled from "styled-components"; | ||
import "../style/color.css"; | ||
export const Heading1 = styled.div` | ||
color: ${(props) => props.color || "var(--white)"}; | ||
font-size: 1.5rem; | ||
font-style: normal; | ||
font-weight: 700; | ||
line-height: 1.375rem; /* 91.667% */ | ||
letter-spacing: -0.0255rem; | ||
`; | ||
export const Heading2 = styled.div` | ||
color: ${(props) => props.color || "var(--white)"}; | ||
font-size: 1.375rem; | ||
font-style: normal; | ||
font-weight: 700; | ||
line-height: 1.375rem; /* 100% */ | ||
letter-spacing: -0.0255rem; | ||
`; | ||
export const Body1 = styled.div` | ||
color: ${(props) => props.color || "var(--white)"}; | ||
font-size: 1.125rem; | ||
font-style: normal; | ||
font-weight: 700; | ||
line-height: 1.375rem; /* 122.222% */ | ||
letter-spacing: -0.0255rem; | ||
`; | ||
export const Body2 = styled.div` | ||
color: ${(props) => props.color || "var(--white)"}; | ||
font-size: 1.125rem; | ||
font-style: normal; | ||
font-weight: 500; | ||
line-height: 1.5rem; /* 133.333% */ | ||
letter-spacing: -0.0255rem; | ||
`; | ||
export const Caption1 = styled.div` | ||
color: ${(props) => props.color || "var(--white)"}; | ||
font-size: 0.875rem; | ||
font-style: normal; | ||
font-weight: 500; | ||
line-height: 1.375rem; /* 157.143% */ | ||
`; | ||
export const Caption2 = styled.div` | ||
color: ${(props) => props.color || "var(--white)"}; | ||
font-size: 0.875rem; | ||
font-style: normal; | ||
font-weight: 300; | ||
line-height: 1.375rem; /* 157.143% */ | ||
`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
공통적으로 쓰는 폰트를 이렇게 props만 넘겨서 잘 사용하신 것 같아요!!!!굿굿ㄱ숙숫
const date = new Date(chatDate); | ||
const hours = date.getHours(); | ||
const minutes = date.getMinutes(); | ||
const isAM = hours < 12 ? true : false; | ||
const hours12 = hours < 13 ? hours : hours - 12; | ||
return `${isAM ? "오전" : "오후"} ${hours12}:${ | ||
minutes < 10 ? "0" + minutes : minutes | ||
}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const date = new Date(chatDate); | |
const hours = date.getHours(); | |
const minutes = date.getMinutes(); | |
const isAM = hours < 12 ? true : false; | |
const hours12 = hours < 13 ? hours : hours - 12; | |
return `${isAM ? "오전" : "오후"} ${hours12}:${ | |
minutes < 10 ? "0" + minutes : minutes | |
}`; | |
const date = new Date(chatDate); | |
const hours = date.getHours(); | |
const minutes = date.getMinutes(); | |
const isAM = hours < 12; | |
const hours12 = hours < 13 ? hours : hours - 12; | |
const formattedTime = `${isAM ? "오전" : "오후"} ${hours12}:${minutes < 10 ? "0" + minutes : minutes}`; | |
return formattedTime; |
여기는 이렇게 isAM 에 대해선 hours>12
가 이미 boolean 값이고, 마지막엔 가독성 측면에서 좀 수정해봤습니다!!!
export const chatCardDateFormat = (date: string) => { | ||
const dateObj = new Date(date); | ||
const nowDate = new Date(); | ||
if ( | ||
// 같은 날일때 시간출력 (오전 00:00) | ||
dateObj.getFullYear() === nowDate.getFullYear() && | ||
dateObj.getMonth() === nowDate.getMonth() && | ||
dateObj.getDate() === nowDate.getDate() | ||
) { | ||
return chatBubbleTime(dateObj.toString()); | ||
} else if ( | ||
// 어제일 때 어제 출력 (어제) | ||
dateObj.getFullYear() === nowDate.getFullYear() && | ||
dateObj.getMonth() === nowDate.getMonth() && | ||
dateObj.getDate() === nowDate.getDate() - 1 | ||
) { | ||
return `어제`; | ||
} else if ( | ||
// 같은 해일 때 날짜 출력 (00월 00일) | ||
dateObj.getFullYear() === nowDate.getFullYear() | ||
) { | ||
return `${dateObj.getMonth() + 1}월${" "}${dateObj.getDate()}일`; | ||
} else if ( | ||
// 다른 해일 때 년도 포함 날짜 출력 (0000.00.00) | ||
dateObj.getFullYear() !== nowDate.getFullYear() | ||
) { | ||
return `${dateObj.getFullYear()}.${" "}${dateObj.getMonth() + 1}.${" "} | ||
${dateObj.getDate()}`; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const chatCardDateFormat = (date: string) => { | |
const dateObj = new Date(date); | |
const nowDate = new Date(); | |
if ( | |
// 같은 날일때 시간출력 (오전 00:00) | |
dateObj.getFullYear() === nowDate.getFullYear() && | |
dateObj.getMonth() === nowDate.getMonth() && | |
dateObj.getDate() === nowDate.getDate() | |
) { | |
return chatBubbleTime(dateObj.toString()); | |
} else if ( | |
// 어제일 때 어제 출력 (어제) | |
dateObj.getFullYear() === nowDate.getFullYear() && | |
dateObj.getMonth() === nowDate.getMonth() && | |
dateObj.getDate() === nowDate.getDate() - 1 | |
) { | |
return `어제`; | |
} else if ( | |
// 같은 해일 때 날짜 출력 (00월 00일) | |
dateObj.getFullYear() === nowDate.getFullYear() | |
) { | |
return `${dateObj.getMonth() + 1}월${" "}${dateObj.getDate()}일`; | |
} else if ( | |
// 다른 해일 때 년도 포함 날짜 출력 (0000.00.00) | |
dateObj.getFullYear() !== nowDate.getFullYear() | |
) { | |
return `${dateObj.getFullYear()}.${" "}${dateObj.getMonth() + 1}.${" "} | |
${dateObj.getDate()}`; | |
} | |
}; | |
export const chatCardDateFormat = (date: string) => { | |
const dateObj = new Date(date); | |
const nowDate = new Date(); | |
if (dateObj.toDateString() === nowDate.toDateString()) { | |
return chatBubbleTime(dateObj.toString()); | |
} else if (dateObj.toDateString() === new Date(nowDate - 24 * 60 * 60 * 1000).toDateString()) { | |
return '어제'; | |
} else if (dateObj.getFullYear() === nowDate.getFullYear()) { | |
return `${dateObj.getMonth() + 1}월 ${dateObj.getDate()}일`; | |
} else { | |
return `${dateObj.getFullYear()}. ${dateObj.getMonth() + 1}. ${dateObj.getDate()}`; | |
} | |
}; |
toDateString()함수로 년월일 한번에 비교할 수 있습니다!! 선호하는 스타일에 따라 다를 것 같아요 ㅎㅎ
const copy = [...users]; | ||
if (copy[props.userIndex].favorite === 0) { | ||
copy[props.userIndex] = { | ||
...copy[props.userIndex], | ||
favorite: 1, | ||
}; | ||
} else if (copy[props.userIndex].favorite === 1) { | ||
copy[props.userIndex] = { | ||
...copy[props.userIndex], | ||
favorite: 0, | ||
}; | ||
} | ||
//현재 즐겨찾기 상태일 때는 업데이트 점 X | ||
if (props.favorite === false) { | ||
setGreenDot(true); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const copy = [...users]; | |
if (copy[props.userIndex].favorite === 0) { | |
copy[props.userIndex] = { | |
...copy[props.userIndex], | |
favorite: 1, | |
}; | |
} else if (copy[props.userIndex].favorite === 1) { | |
copy[props.userIndex] = { | |
...copy[props.userIndex], | |
favorite: 0, | |
}; | |
} | |
//현재 즐겨찾기 상태일 때는 업데이트 점 X | |
if (props.favorite === false) { | |
setGreenDot(true); | |
} | |
const updatedUsers = [...users]; | |
updatedUsers[props.userIndex] = { | |
...updatedUsers[props.userIndex], | |
favorite: updatedUsers[props.userIndex].favorite === 0 ? 1 : 0, | |
}; | |
if (!props.favorite) { | |
setGreenDot(true); | |
} |
copy라는 변수명은 어떠한걸 복사했는지 잘 알 수 없어서 한번 변수명과 코드를 좀 줄여봤습니다!
const chatNumber = chatList.findIndex( | ||
(chatRoom) => chatRoom.userList[1] === props.userIndex + 1 | ||
); | ||
console.log(chatNumber); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
배포시에 로그는 지워주면 좋습니다~!!~!~~~
} | ||
} | ||
touchStartX.current = null; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이거 진짜 대박 ..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
규호님 3주차에 이어 코드리뷰를 하게 돼서 더 잼썼습니당ㅎㅎ 지난주에 이어 즐겨찾기, 색상등의 구현기능이 많아서 재밌게 코드리뷰했습니다! 코드가 깔끔하고, 재사용이 가능하게 분리가 잘 되어 있어서 보면서 많이 배웠습니다. 디자이너분과 소통하시면서 여러 기능 구현하신 것 같아요 👍
시험기간이라 바쁘셨을텐데 고생 많으셨어요~~!!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저는 이번 과제에서 전역상태관리를 하지 못했는데, 이렇게 관리하니까 확실히 현재 user, 나머지 users 관리가 효율적이네요~! 저도 다음 과제부터는 사용해보아야겠습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utils 폴더에 날짜 표현 기능들을 따로 빼주신게 좋은것같아요! 재사용되는 비슷한 함수들을 이렇게 모아놓으니 더 좋은것 같습니다. 배워갑니당~~
import { Body1 } from "../../style/font"; | ||
import { ReactComponent as Link } from "../../assets/icons/Link.svg"; | ||
import { ReactComponent as Next } from "../../assets/icons/Next.svg"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (initializeChat === null) { | ||
localStorage.setItem( | ||
"chat" + String(Number(id)), | ||
JSON.stringify(chatListById) | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
각 채팅방별로 이렇게 데이터 저장하는 구조 효율적인 것 같아요~~
//즐겨찾기 추가 시 초록점 생성 toggle | ||
export const greenDotState = atom({ | ||
key: "greenDotState", | ||
default: false, | ||
}); | ||
//이름 찾기 input | ||
export const nameSearchState = atom({ | ||
key: "nameSearchState", | ||
default: "", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
즐겨찾기 부분도 이렇게 관리하는 거 좋은 것 같네요~!!
배포
Chat
사용법
main 화면
messenger_main_desktop.mp4
messenger_main_mobile.mp4
chat 화면
messenger_chat.mp4
setting 화면
messenger_setting.mp4
Key Questions
링크
링크
링크
이 외에도 카톡 등으로 많은 소통을 했고, 디자이너 분께서 바로바로 반영해주셔서 구현할 때 수월했습니다!
SPA, Routing
React는 Single Page Application으로 html이 하나만 존재하는 단일 페이지이다. 따라서 웹사이트를 구성하는 페이지가 여러개라면 페이지마다 경로를 설정해줘야한다.
경로마다 해당하는 페이지를 출력하는 것을 Routing이라고 한다. 이는 Routing 기능을 제공하는
React-Router
라이브러리를 통해 사용할 수 있다.상태관리
저번 3주차 과제와 마찬가지로 recoil 전역 상태 관리 라이브러리를 사용하여 구현했다.
후기
디자이너 분이랑 소통하면서 추가하고 싶었던 기능이 많은데, 시험기간 이슈로 전부 구현하지 못해서 개인적으로 더 구현해보고 싶은 과제입니다! 사용성을 고려하다보니, 채팅방이 최근 시간 순으로 정렬돼야한다던지, 좋아요 버튼, 즐겨 찾기 버튼은 무조건 하나만 띄워야한다던지 예외처리할게 점점 많아졌는데 처리하지 못한게 정말 아쉽습니다🥲 아직 부족한 점이 한참 많다는 걸 깨닫기도 하고 구현하는데 재밌는 점이 많았던 과제였습니다! 다들 수고하셨습니다!
피드백 반영