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주차] 김다희 미션 제출합니다. #17

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

Dahn12
Copy link

@Dahn12 Dahn12 commented May 3, 2024

🔗 배포 링크

https://react-messenger-19th.vercel.app/

👩‍💻 느낀점

상태관리와 typescript까지 동시에 하느라 꽤나 힘들었던 과제였습니다. 상태관리는 redux를 주로 사용한다고 해서 redux로 도전해 보다가 너무 어려워서 recoil로 다시 리팩토링 하게 되었습니다.. 🥲 그래서 아직 채팅방을 모두 구현하지는 못했지만, 스터디 전까지 최대한 완성해 보겠습니다..!채팅 리스트에서 채팅방으로 라우팅하는 부분도 많이 어려웠지만, 해결하게되어 다행이라고 생각합니다..ㅎㅎ

🔑 key questions

Routing이란?
사용자가 요청한 URL에 따라 해당 URL에 맞는 페이지를 보여주는 것

  1. link
  1. Relative
    a. 계층 구조에 상대적이다.
    b.상대 경로 표현이 가능하므로, ./.. 과 같은 표현도 사용이 가능하다.

  2. preventScrollReset
    a.페이지 중간에 있는 컨텐츠 내부에서 tab 목록을 누르는 것과 같은 시도를 할 때, 기존의 Link 컴포넌트였다면 클릭 시 스크롤이 초기화되어 페이지 가장 위로 이동하게 된다.
    b.그러나 이 속성을 true로 설정해주면 이를 방지할 수 있다!!

  3. state
    useLocation 훅과 연계하여 특정 state를 넘겨주는 것도 가능하다.

  1. useNavigate
  1. replace
    기본값은 false이고, true로 설정한다면 이동 후 뒤로가기가 불가능해진다.

  2. state
    Link와 마찬가지로 state를 전달해줄 수 있다.

SPA란?
SPA란 Single Page Application의 약자이다.
기존 웹 서비스는 요청시마다 서버로부터 리소스들과 데이터를 해석하고 화면에 렌더링하는 방식이다.
SPA형태는 브라우저에 최초에 한번 페이지 전체를 로드하고, 이후부터는 특정 부분만 Ajax를 통해 데이터를 바인딩하는 방식이다.
스크린샷 2024-05-03 오후 11 31 51

상태관리란?
데이터를 관리하는 방법. 여러 컴포넌트간에 데이터 전달과 이벤트 통신을 한 곳에서 관리하는 것.

상태관리가 필요한 이유

  1. 데이터가 바뀌어도 페이지가 렌더링 되지 않게 하기 위해
  2. 상태(state)들이 복잡하게 얽혀있다면, 상호간에 의존성이 많아지게 되어서 UI가 어떻게 변하는지 알기 어렵기 때문에 효율적인 관리가 필요하다.

redux와 recoil

스크린샷 2024-05-03 오후 11 35 51 스크린샷 2024-05-03 오후 11 36 22

Copy link
Member

@ddhelop ddhelop left a comment

Choose a reason for hiding this comment

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

컴포넌트도 적절하게 쪼개고, 코드도 깔끔하게 작성해서 리뷰하기 편했어요!
열심히 한 흔적이 있네요..! 과제 고생많았습니다!

Copy link
Member

Choose a reason for hiding this comment

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

icon들은 svg 파일로 저장하는 것이 최적화에 도움이 되서 최대한 svg를 이용하는 것이 좋아요 :)_



const SearchIcon = styled.button`
${TopBarIconsStyle}
Copy link
Member

Choose a reason for hiding this comment

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

동일한 속성이 이런식으로 커스텀했던 스타일 컴포넌트를 불러와서 사용하는 방법이 있네요. 좋은 방법 배워갑니다 ~!

Copy link
Member

Choose a reason for hiding this comment

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

유저 데이터랑 채팅 데이터를 한번에 관리하는 것도 좋은 방법 같아요.

Comment on lines +40 to +58
{/*const [chatData, setChatData] = useRecoilState<ChatType>(chatDataState);

useEffect(() => {
if (chatRoomJson) {
setChatData(chatRoomJson);
}
}, []);

console.log(chatData);


//스크롤 구현
const chatEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [chatData]);


*/}
Copy link
Member

Choose a reason for hiding this comment

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

코드에 여러 주석 처리된 섹션이 있으며, 이들이 현재 사용되지 않거나 향후 사용을 위해 남겨두었다면, 각 부분에 대한 명확한 주석을 추가하거나 필요 없다면 제거하는 것이 좋습니다.!

Copy link
Member

Choose a reason for hiding this comment

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

채팅 데이터 변경시 스크롤이 구현되는 것은 이런느낌으로 수정하면 될 것 같아요

Suggested change
{/*const [chatData, setChatData] = useRecoilState<ChatType>(chatDataState);
useEffect(() => {
if (chatRoomJson) {
setChatData(chatRoomJson);
}
}, []);
console.log(chatData);
//스크롤 구현
const chatEndRef = useRef<HTMLDivElement>(null);
useEffect(() => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [chatData]);
*/}
useEffect(() => {
const scrollToBottom = () => {
chatEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
scrollToBottom();
}, [chatRoomJson?.chat]); // 의존성 배열을 chatRoomJson.chat으로 변경
<div ref={chatEndRef} style={{ width: "100%", height: "1px" }}/>

Comment on lines +10 to +24
// 현재 시간 표시
const [time, SetTime] = useState<string>('');
useEffect(() => {
updateTime(); // 마운트 될 때 마다 시간 불러옴

const interval = setInterval(updateTime, 60000); // 1분마다 갱신

// 메모리 누수 방지..
return () => clearInterval(interval);
}, []);

const updateTime = () =>{
const currentTime = dayjs().format('HH:mm'); // 시:분 형식으로 포맷팅
SetTime(currentTime);
};
Copy link
Member

Choose a reason for hiding this comment

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

저는 과제에서 현재 시간에 대한 기능을 구현안했는데, 이런식으로 코드를 구성하면 깔끔하게 할 수 있군요..👍

Comment on lines +12 to +14
<SearchIcon> <img src = {searchIcon}/> </SearchIcon>
<VectorIcon><img src = {vectorIcon}/></VectorIcon>
<FolderIcon><img src = {folderIcon}/></FolderIcon>
Copy link
Member

Choose a reason for hiding this comment

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

png 파일도 이미지 경로를 컴포넌트화시켜서 연결하면 코드가 깔끔해지네요. 좋은 방법같습니다

Copy link

@youdame youdame left a comment

Choose a reason for hiding this comment

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

다희님 고생 많으셨습니다! 남겨드린 리뷰에서 더 궁금한 점이 생긴다면 언제든지 호출해주세요 :)

<ServiceTab/>
<AddGroup/>
<FriendsListsWrapper>
<FriendsTitle>친구118</FriendsTitle>
Copy link

Choose a reason for hiding this comment

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

이 부분 숫자 하드코딩하기 보다 json 파일에서 가져온 데이터 배열의 길이로 처리했다면 더 좋았을 거 같아요!

Comment on lines +37 to +45
<ScrollWrapper>
{friendsData.map((friend: friendsDataType) => (
<FriendList
key={friend.f_id}
f_name={friend.f_name}
f_profile={friend.f_profile}
/>
))}
</ScrollWrapper>
Copy link

Choose a reason for hiding this comment

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

FriendsListPage에서 각 사람을 클릭했을 때 그 사람과 나눈 채팅방으로 이동되는 방식으로 구현했다면 더 챌린징했을 거 같네요!

<BackButton>
<Link to = "/"><img src = {backButton}/></Link>
</BackButton>
<MyProfileImgWrapper>{u_profile? <img src={u_profile}/> : <img src={profileUpload}/>}</MyProfileImgWrapper>
Copy link

Choose a reason for hiding this comment

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

<img src={u_profile || profileUpload} alt="Profile" />

이런 식으로도 작성할 수 있답니다!

Comment on lines +29 to +61
<div>
<h4>이름</h4>
<span>{u_name}</span>
</div>
<img src={showIcon}/>
</UserInfoWrapper>
<UserInfoWrapper>
<div>
<h4>상태메세지</h4>
<span>{u_message? u_message : "상태메세지를 입력해보세요"}</span>
</div>
<img src={showIcon}/>
</UserInfoWrapper>
<UserInfoWrapper>
<div>
<h4>전화번호</h4>
<span>{"+82 "+ u_phoneNum}</span>
</div>
<img src={showIcon}/>
</UserInfoWrapper>
<UserInfoWrapper>
<div>
<h4>인스타그램</h4>
<span>{u_insta}</span>
</div>
<a href={"https://www." + u_insta}><img src={linkIcon}/></a>
</UserInfoWrapper>
<UserInfoWrapper>
<div>
<h4>GIT HUB</h4>
<span>{u_github}</span>
</div>
<a href={"https://www." + u_github}><img src={linkIcon}/></a>
Copy link

Choose a reason for hiding this comment

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

const BASE_URL = "https://www.";

function UserInfoField({ title, value, hasLink, icon }) {
  return (
    <UserInfoWrapper>
      <div>
        <h4>{title}</h4>
        <span>{value}</span>
      </div>
      {hasLink ? (
        <a href={`${BASE_URL}${value}`}>
          <img src={icon} />
        </a>
      ) : (
        <img src={icon} />
      )}
    </UserInfoWrapper>
  );
}

저라면 이런 식으로 반복되는 코드를 줄이기 위해 컴포넌트를 하나 만든 다음 prop으로 다른 UI를 보여줄 수 있게 처리했을 거 같아요:)

Copy link

Choose a reason for hiding this comment

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

이 파일 네이밍이 잘못된 거 같아요..! 그래서 프리티어가 잘 작동 안되고 있는 거 같습니다. .prettierrc로 변경해보세요!

Comment on lines +8 to +14
interface ChatDataType {
r_id : number
r_name : string
isGroup : boolean
r_profile? : string
chat: { c_id: number; sender: string; receiver: string; value: string; time: string }[];
}
Copy link

Choose a reason for hiding this comment

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

중복된 타입들이 계속해서 새로 선언되고 있는데요! types 폴더에서 필요한 타입들을 정의해둔 후 export해서 사용한다면 계속해서 같은 코드를 작성하지 않아도 된답니다:) 이미 atoms에서 정의해둔 것들이 계속 사용되고 있는 거 같아서 그 파일에서 export를 해줘도 되지만 다른 파일에서도 사용되는 타입들은 types 폴더의 파일에 저장해두는 게 일반적이에요!

Comment on lines +22 to +24
{chatData.map((chatInfo : ChatDataType) =>(
<Link to={`/chatting-page/${chatInfo.r_id}`} style={{ textDecoration: 'none', color: 'inherit' }}>
<ChatRoomLIst
Copy link

Choose a reason for hiding this comment

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

이 페이지에서 채팅방 하나를 클릭하면 url이 chatting-list-page에서 chatting-page/1 이렇게 변경되는데요. 다희님께서 작성해주신 url path는 리소스 지향적이기보다는 페이지 의존적인 거 같아요. 웹에서 주로 사용하는 rest-api 원칙에 따라 수정해보시길 권장드립니다! 레퍼런스 하나 남기고 가요. rest api란?

리소스 지향 및 URL의 통일성과 예측 가능성

REST API는 리소스 기반의 접근 방식을 취해요. 리소스는 명사로 표현되어야 하며, URL은 해당 리소스의 위치를 나타내는 데 중점을 둡니다. 하지만, 'chatting-list-page'와 'chatting-page/1'은 리소스가 아닌 페이지를 가리키는 것처럼 보이네요. 또한 URL은 가능한 한 예측 가능하고 일관성 있어야 하는데요. 따라서 'chatting-list-page'에서 'chatting-page/1'로의 변경되는 것보단 아래와 같은 구조를 따르는 게 더 좋겠죠?

개선된 URL 구조 예시

  • 리소스 목록 조회: /chats - 모든 채팅방 목록 조회
  • 특정 리소스 조회: /chats/1 - ID가 1인 채팅방 조회

Copy link

Choose a reason for hiding this comment

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

현재 인풋 창이 보이질 않네요 ㅠ ㅠ
이미지를 많이 사용해주셨는데, 웹 접근성을 높이기 위해 alt 속성을 작성하는 걸 잊지 말아주세요!
alt 속성

Comment on lines +1 to +8
import styled from "styled-components"
import { css } from "styled-components"
import addIcon from "../../assets/icons/add.png"
import cameraIcon from "../../assets/icons/camera.png"
import galleryIcon from "../../assets/icons/gallery.png"
import recordIcon from "../../assets/icons/record.png"
import stickerIcon from "../../assets/icons/sticker.png"

Copy link

Choose a reason for hiding this comment

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

../../ 이런 식으로 상대경로를 사용하게 되면 depth가 깊어짐에 따라 가독성을 해치는 경우가 대부분인데요. tsconfig의 baseURl을 src로만 설정해줘도 절대 경로를 사용할 수 있어요!

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.

3 participants