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

[4주차] 이규호 미션 제출합니다. #16

Open
wants to merge 55 commits into
base: master
Choose a base branch
from

Conversation

kyuhho
Copy link

@kyuhho kyuhho commented Nov 3, 2023

배포

Chat

사용법

main 화면

  • 우클릭(데스크탑), 우측으로 슬라이드(모바일) 시 즐겨찾기 추가 버튼이 생성됩니다. 추가 시 즐겨찾기 창에 추가 표시가 생기고 즐겨찾기 창에서 추가한 목록을 확인할 수 있습니다.
  • 우클릭(데스크탑), 좌측으로 슬라이드(모바일) 시 즐겨찾기 추가 버튼이 없어집니다.
  • 우측 화살표 클릭 시 해당하는 채팅방으로 이동합니다.
messenger_main_desktop.mp4
messenger_main_mobile.mp4

chat 화면

  • 기획 취지에 맞게 채팅 상대방의 퇴근 여부가 표기됩니다.
  • 검색창을 통해 이름을 통한 채팅 검색을 사용할 수 있습니다.
  • 채팅방의 가장 마지막 채팅이 표기되고, 마지막 채팅의 시간이 해당 채팅방 card에 표기됩니다. 올해의 경우 월일, 오늘인 경우 몇시 몇분, 어제인 경우는 어제, 년도가 바뀌면 년도까지 표기됩니다.
  • 채팅 버블 더블클릭 시 좋아요 버튼을 이용할 수 있습니다. (모바일 동일)
messenger_chat.mp4

setting 화면

  • 인스타그램 링크 시 디자이너 인스타, 이메일 링크 시 프론트 인스타로 넘어가게 구현했습니다.
messenger_setting.mp4

Key Questions

  • 디자이너로부터 받은 QA 목록
image

링크

image

링크

image

링크

이 외에도 카톡 등으로 많은 소통을 했고, 디자이너 분께서 바로바로 반영해주셔서 구현할 때 수월했습니다!

  • SPA, Routing

    React는 Single Page Application으로 html이 하나만 존재하는 단일 페이지이다. 따라서 웹사이트를 구성하는 페이지가 여러개라면 페이지마다 경로를 설정해줘야한다.
    경로마다 해당하는 페이지를 출력하는 것을 Routing이라고 한다. 이는 Routing 기능을 제공하는 React-Router 라이브러리를 통해 사용할 수 있다.

  • 상태관리

    저번 3주차 과제와 마찬가지로 recoil 전역 상태 관리 라이브러리를 사용하여 구현했다.

후기

디자이너 분이랑 소통하면서 추가하고 싶었던 기능이 많은데, 시험기간 이슈로 전부 구현하지 못해서 개인적으로 더 구현해보고 싶은 과제입니다! 사용성을 고려하다보니, 채팅방이 최근 시간 순으로 정렬돼야한다던지, 좋아요 버튼, 즐겨 찾기 버튼은 무조건 하나만 띄워야한다던지 예외처리할게 점점 많아졌는데 처리하지 못한게 정말 아쉽습니다🥲 아직 부족한 점이 한참 많다는 걸 깨닫기도 하고 구현하는데 재밌는 점이 많았던 과제였습니다! 다들 수고하셨습니다!

피드백 반영

  • 모바일에서 상하단 잘리는 문제 해결
  • 날짜 관련 함수들 util 폴더로 분리
  • 터치 시 사용자 바뀜 기능 클릭 범위 축소

Copy link

@mod-siw mod-siw left a 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에 따라 버튼색 변환
Copy link

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]);
}, []);
Copy link

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(
Copy link

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 (
Copy link

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) => {
Copy link

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>
Copy link

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>
Copy link

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 누르면 전송 이런 식으로 좀 더 편리하게 채팅을 보낼 수 있습니당

textarea

}
}
touchStartX.current = null;
}
Copy link

Choose a reason for hiding this comment

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

모바일일 때는 터치로 설정 가능하게 빼두신 점 디테일 최고입니다👍👍

Choose a reason for hiding this comment

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

이거 진짜 대박 ..

Copy link

@westofsky westofsky left a comment

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 +5
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";

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 생성해서 관리하시는 것도 참고해주세요!!

Comment on lines +20 to +24
if (users[userId].name.includes(input)) {
return true;
} else {
return false;
}

Choose a reason for hiding this comment

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

Suggested change
if (users[userId].name.includes(input)) {
return true;
} else {
return false;
}
return users[userId].name.includes(input);

이렇게 줄여서 사용할 수 있을 것 같아요!

Comment on lines +26 to +44
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>
);
}

Choose a reason for hiding this comment

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

여기는

Suggested change
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>
);

이렇게 중복을 없앨 수 있을 것 같아요

Comment on lines +52 to +56
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer/Edge */
&::-webkit-scrollbar {
display: none; /* Chrome, Safari*/
}

Choose a reason for hiding this comment

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

파이어폭스 엣지 크롬 사파리 모두 스크롤바없는거 확인했습니다 :)

Comment on lines +1 to +48
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% */
`;

Choose a reason for hiding this comment

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

공통적으로 쓰는 폰트를 이렇게 props만 넘겨서 잘 사용하신 것 같아요!!!!굿굿ㄱ숙숫

Comment on lines +2 to +9
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
}`;

Choose a reason for hiding this comment

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

Suggested change
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 값이고, 마지막엔 가독성 측면에서 좀 수정해봤습니다!!!

Comment on lines +32 to +61
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()}`;
}
};

Choose a reason for hiding this comment

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

Suggested change
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()함수로 년월일 한번에 비교할 수 있습니다!! 선호하는 스타일에 따라 다를 것 같아요 ㅎㅎ

Comment on lines +37 to +52
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);
}

Choose a reason for hiding this comment

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

Suggested change
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);

Choose a reason for hiding this comment

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

배포시에 로그는 지워주면 좋습니다~!!~!~~~

}
}
touchStartX.current = null;
}

Choose a reason for hiding this comment

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

이거 진짜 대박 ..

Copy link

@dhshin98 dhshin98 left a comment

Choose a reason for hiding this comment

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

규호님 3주차에 이어 코드리뷰를 하게 돼서 더 잼썼습니당ㅎㅎ 지난주에 이어 즐겨찾기, 색상등의 구현기능이 많아서 재밌게 코드리뷰했습니다! 코드가 깔끔하고, 재사용이 가능하게 분리가 잘 되어 있어서 보면서 많이 배웠습니다. 디자이너분과 소통하시면서 여러 기능 구현하신 것 같아요 👍
시험기간이라 바쁘셨을텐데 고생 많으셨어요~~!!

Copy link

Choose a reason for hiding this comment

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

저는 이번 과제에서 전역상태관리를 하지 못했는데, 이렇게 관리하니까 확실히 현재 user, 나머지 users 관리가 효율적이네요~! 저도 다음 과제부터는 사용해보아야겠습니다!

Copy link

Choose a reason for hiding this comment

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

utils 폴더에 날짜 표현 기능들을 따로 빼주신게 좋은것같아요! 재사용되는 비슷한 함수들을 이렇게 모아놓으니 더 좋은것 같습니다. 배워갑니당~~

Comment on lines +3 to +5
import { Body1 } from "../../style/font";
import { ReactComponent as Link } from "../../assets/icons/Link.svg";
import { ReactComponent as Next } from "../../assets/icons/Next.svg";
Copy link

Choose a reason for hiding this comment

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

저도 코드리뷰 받은 부분 정리한 것 같이 공유드릴게요!
baseUrl 을 설정하면 절대경로가 가능해진다고 합니다! 참고해보셔도 좋을 것 같아요~~

image

Comment on lines +35 to +39
if (initializeChat === null) {
localStorage.setItem(
"chat" + String(Number(id)),
JSON.stringify(chatListById)
);
Copy link

Choose a reason for hiding this comment

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

각 채팅방별로 이렇게 데이터 저장하는 구조 효율적인 것 같아요~~

Comment on lines +39 to +47
//즐겨찾기 추가 시 초록점 생성 toggle
export const greenDotState = atom({
key: "greenDotState",
default: false,
});
//이름 찾기 input
export const nameSearchState = atom({
key: "nameSearchState",
default: "",
Copy link

Choose a reason for hiding this comment

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

즐겨찾기 부분도 이렇게 관리하는 거 좋은 것 같네요~!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants