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

토이프로젝트2_5조_smartalk #6

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

토이프로젝트2_5조_smartalk #6

wants to merge 326 commits into from

Conversation

gahyuun
Copy link

@gahyuun gahyuun commented Nov 17, 2023

smartalk

실시간 채팅 서비스

개발기간: 2023.11.08~2023.11.16

smartalk 바로가기

smartalk 깃허브 바로가기


🔖 프로젝트 소개

smartalk은 실시간 대화를 가능하게 하는 효과적인 채팅 플랫폼입니다.

사용자들은 손쉽게 채팅방을 생성하고 다른 사용자들을 초대하여 실시간으로 의견을 공유할 수 있습니다
smartalk은 사용자들의 효율적인 커뮤니케이션을 지원합니다

테스트 계정

  • id : user1
  • password : userPassword

💻 기능 소개

채팅 페이지

소켓-메시지

  • 채팅 리스트
    이전 대화 목록을 불러오고 실시간으로 사용자가 보낸 메시지를 띄웁니다
    이때 실시간 메시지가 보내질 때 , 이전 대화 목록이 많을 때 스크롤이 하단으로 이동하도록 제어하였습니다
    또한 채팅 메시지 input값이 빈 값일 땐 go 버튼을 disabled 시켰습니다

  • 초대하기, 나가기
    이미 참여한 사람은 제외한 나머지 전체 유저를 불러와 초대할 수 있습니다.
    초대 이후에는 실시간으로 참여 목록을 볼 수 있습니다.
    채팅방 내 사용자가 초대되거나 나가면 toast 알람을 띄웠습니다
    나간 이후에는 나의 채팅방에서 삭제하며, 채팅방 내에서는 나간 사람이 실시간으로 사라집니다.

  • 초대 모달
    이미 참여한 사람은 제외한 나머지 전체 유저를 불러와 초대할 수 있습니다.
    socket.io를 이용해서 초대 이후에는 실시간으로 참여 목록을 볼 수 있습니다.

사이드바 & toast

  • 새로운 채팅방 추가 toast
    inviteToast

서버에 새로운 채팅방이 개설되거나, 타인에게 초대를 받은 경우에도 'AA방이 추가되었습니다.’라는 toast를 띄웁니다.

  • 나의 채팅방
    React-Query, socket.io를 사용해 참여하기, 초대하기, 나가기를 감지하고 실시간으로 나의 채팅방을 불러옵니다.


회원가입

join

react-hook-form을 이용하여 id,password,name 모두 유효성 검사를 진행하고 id는 중복 확인 버튼을 통해 중복된 아이디를 체크하였습니다
중복확인과 유효성 검사를 모두 마친 후 백엔드에게 사용자 정보를 post하는 로직으로 구현하였습니다



채팅방 목록 / 채팅방 만들기

Nov-16-2023 23-50-16

  • 채팅방 이름을 #을 기준으로 나누어 제목과 카테고리로 분리하였습니다. 이를 통해, 이름과 카테고리로 검색이 가능하게 만들었습니다.
  • react query를 사용하여 채팅방 목록과 모달 내부의 유저 리스트를 캐싱하였고, 채팅방을 만들면 전체 채팅방 목록을 다시 받아오게 하였습니다.


로그인 하기

  • 로그인 시 access & refreshToken을 이용하여 추후 모든 api 연동 및 소켓 등의 사용에 interceptor를 활용한 axios 비동기 통신 방법을 이용해 진행했습니다.
  • 유효성 검증을 통해, token 값이 존재하지 않는다면 로그인 페이지로 라우팅되고, 만료기간이 지나게 되면 자동으로 토큰값을 갱신해 줍니다.

🔨기술 스택

Stack
언어
디자인
라이브러리
협업툴
개발 환경

필수 구현 사항

  • useState 또는 useReducer를 활용한 상태 관리 구현
    • useState, recoil을 활용한 상태 관리 구현
  • Sassstyled-componentemotionChakra UItailwind CSS 등을 활용한 스타일 구현
    • Chakra UI를 활용한 스타일 구현
  • react 상태를 통한 CRUD 구현
    • ( create - 채팅방 생성 , read - 채팅방 조회 , update - 채팅방에 유저 초대하기 , delete - 채팅방에서 유저 나가기)
  • 상태에 따라 달라지는 스타일 구현
    • private 상태에 따라 달라지는 채팅방 스타일 ( 사이드바 )
    • 카테고리에 따라 달라지는 채팅방 스타일
    • 회원가입시 아이디 중복 확인 여부에 따라 달라지는
  • custom hook을 통한 비동기 처리 구현
    • hooks 폴더에 비동기 처리를 위한 custom hook 존재
  • 유저인증 시스템(로그인, 회원가입) 구현
    • 로그인, 회원가입 구현 완료
  • jwt등의 유저 인증 시스템 (로그인, 회원가입 기능)
    • 유저 인증 시스템 구현 완료
  • 소켓을 이용한 채팅 구현
    • 소켓을 이용한 채팅 구현 완료

📘Convention


🙋‍♀️Contributors

FE: 박가현 FE: 최재훈 FE: 이재준 FE: 박은영
-회원가입
-실시간 메세지 수신/송신 구현
-이전 채팅 목록 보여주기 구현
-채팅방에 참가하거나 나간 사용자
실시간으로 보여주기
-메모이제이션을 통한 컴포넌트 렌더링 최적화
-api 호출 로직 재설계를 통해 특정 유저 조회 성능 향상
-로그인 구현 (JWT 토큰 이용 및 interceptor 설정
- 전체 유저 목록 조회
- 카테고리 컨테이너 구현 및
슬라이드 기능
- 카테고리 목록 선택 시
필터 기능을 통한 목록 렌더링
-전체 채팅 목록
-서버 내 모든 채팅 및 내가 속한 채팅 리스트 보여주기
-채팅방 클릭시
해당 채팅방 이동
-채팅방 이름과 카테고리로 검색하기
채팅방 생성
-이름과 카테고리 설정
-나를 제외한 인원 추가
채팅 참여 목록 sidebar
-socket.io로 실시간 참여 유저 목록 구현
-초대, 나가기, 참여하기
-나의 채팅방 sidebar
-실시간 내 채팅방 불러오기 구현
-메인 화면에서 새로운 채팅방 추가 toast알림
메인화면에서 카드 클릭 시 참여하기 구현

🤲느낀 점

가현

  • 소켓을 처음 사용해보았고 그 과정 속에서 어려움도 있었지만 팀원들과 다양한 의견을 나누고 서로 협업하며 문제들을 해결할 수 있었습니다. 또한 개발 기간이 짧은 만큼 빠른 기간 내 성장할 수 있었던 것 같습니다

재준

  • 협업을 하면서 다양한 문제를 마주할 수 있었고, 함께 그 과정을 해결해나가는 순간이 재미있었습니다. 처음엔 여유롭다고 생각했던 프로젝트가 생각보다 오래 걸려서 추가적으로 해보고 싶었던 기능을 많이 못해서 아쉽습니다. 다음에는 조금 더 효율적이고 빠르게 문제를 해결해서 더 완성도 있는 프로젝트를 해내고 싶습니다.

재훈

  • 오랜만에 해본 개발 프로젝트여서, 처음에 많이 버벅거렸지만 팀원들과 소통하면서, PR을 보고 배워나가며 다시 개발의 감을 찾은 프로젝트였습니다. 또한 구현해본 적 없던 기능을 구현하면서 앞으로 있을 프로젝트에 대한 자신감도 얻었습니다.

은영

  • 백엔드와의 협업 프로젝트 이전에 간단히나마 백엔드와의 통신을 접해보고, 사용해보지 않았던 socket.io와 react-query 등, 기타 라이브러리도 직접 사용해보며 단기간에 배울 수 있었던 뜻 깊은 시간이었습니다. 또, 이전 프로젝트와 다른 새로운 팀원분들과 또 다른 새로운 방법으로 프로젝트하는 게 생각보다 훨씬 색달라 재밌었고 정말 좋은 경험으로 느껴졌습니다. 그리고 서버 에러를 경험하며 나의 코드가 문제인지 서버 또는 백엔드 코드의 문제인지 구분하는 방법을 터득하게 된 것 같아 뿌듯하기도 합니다.

Gaoridang and others added 30 commits November 10, 2023 11:18
Feat: 채팅 카드 컴포넌트 제작
Design: 전체유저조회 사이드바 레이아웃 구현
refactor: 공통 타입,상수,instance 리팩토링
Fix: channel 관련 import 오류 수정 및 이름 변경
Feat: 채팅 메시지 보내기 기능 구현
SKY-PEY and others added 23 commits November 16, 2023 22:52
Fix: 채팅방 목록 검색 오류 수정
design: 전체 레이아웃 및 스크롤 기능 추가
dbstjrals pushed a commit that referenced this pull request Nov 17, 2023
…on-bar

네비게이션바 및 라우팅 오류를 수정한다.
JeongMin83 pushed a commit that referenced this pull request Nov 17, 2023
jseo9732 pushed a commit that referenced this pull request Nov 18, 2023
Yamyam-code pushed a commit that referenced this pull request Nov 18, 2023
Feat: 메인 및 서브 레이아웃 적용
noSPkeepgoing added a commit that referenced this pull request Nov 18, 2023
Feat: add global variable
LeHiHo added a commit that referenced this pull request Nov 18, 2023
로그인, 회원가입 구현
Copy link

@JungHyeonSeo-GGQ JungHyeonSeo-GGQ left a comment

Choose a reason for hiding this comment

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

2주간 토이프로젝트 진행하시느라 고생많으셨습니다~

Comment on lines +41 to +55
const isValidTitle = checkChannelName(channel.title);
if (!isValidTitle.isValid) {
alert(isValidTitle.errorMessage);
return;
}

const channelData = {
name: createChannelNameWithCategory(channel.title, channel.category),
users,
isPrivate,
};

mutation.mutate(channelData);
onClose();
setChannel({ title: '', category: '기타' });

Choose a reason for hiding this comment

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

해당 코드를 보면 무조건 mutation 이 성공한다는거를 가정한 코드인거같습니다
api 가 늘 문제가 없다면 좋겠지만 서버가 죽거나, 혹은 에러가 발생할수도있습니다 그럴때는 대비하기 위해 mutaion 자체에 error 를 잡아주시거나, 해당 함수에서 try/catch로 에러를 잡아시는게 좋습니다

또한 async 함수로 만들었다면 mutate 가 아닌 mutateAsync를 사용하시는걸 추천들비니다 해당 부분에서는 async가 불필요해 보입니다

<Flex mt="4" align="center">
<Stack direction="column" spacing="0" h="100vh">
{userData.userNames.map((userName, index) => (
<Flex key={index} mt="4">

Choose a reason for hiding this comment

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

리액트에서는 엘리먼트 key를 index로 잡는것을 추천하지 않습니다, 해당 index는 컴포넌트 단위가 아닌 페이지 단위를 기준으로 잡아주시는게 좋으시고 최대한 페이지 단위에서 고유한 key를 잡으시는걸 추천드립니다

const Chats = () => {
const { id } = useParams();
const navigate = useNavigate();
if (!id) return <></>;

Choose a reason for hiding this comment

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

id가 없을때는 fragment가 아닌 null를 리턴해주시는게 더 좋으시고
더 정확하게 구현하기 위해서는 id가 없을때는 이전 페이지로 이동하게 해주시는게 사용자 경험을 더 높일수있습니다


const sendChat = () => {
if (value === '') return;
const socket = getSocket(chatId);

Choose a reason for hiding this comment

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

해당 부분을 보면 sendChat 를 할때마다 소켓에 인스턴스를 계속 생성하고있습니다
이부분은 조금 불필요해서 보이고 사실 해당 채팅방에 소켓에 인스턴스는 채팅방에 들어왔을때 한번만 생성을 해주고 나갈때 disconnect 와 인스턴스를 제거해주면 될꺼같습니다

socket.emit(SOCKET.MESSAGE_TO_SERVER, value, (error: Error) => {
if (error) alert('알 수 없는 오류입니다');
});
if (socket.connected) socket.disconnect();

Choose a reason for hiding this comment

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

또한 이부분이 약간 의문이 드는점인데 아직 채팅방에 나가지 않은 상태인데 소켓 연결을 제거하는 부분은 조금 불필요해 보이지 않을까싶습니다

watch={watch}
setIsChecking={setIsChecking}
/>
<JoinInputBox

Choose a reason for hiding this comment

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

제가 테스트하면서 많이 놀랐던 부분입니다 패스워드를 입력했는데 입력한 패스워드가 바로 보여지고있습니다
이러한 실수는 절때로 하면 안되시고 이러한 사소한 부분 한두개가 서비스의 퀄리티를 낮추는거기때문에 최대한 없도록 해주시면 좋을꺼같습니다

const navigate = useNavigate();

const onSubmit: SubmitHandler<JoinForm> = (event) => {
if (isChecking === null) {

Choose a reason for hiding this comment

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

해당 코드는 !isChecking 으로 간소화 할수있을꺼같습니다


const onSubmit: SubmitHandler<LoginForm> = async (data) => {
try {
const result: LoginResToken = await login(data);

Choose a reason for hiding this comment

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

해당 부분은 코드컨벤션 통일을 위해 useMutation으로 해주시면 더 좋을꺼같습니다

localStorage.setItem('refreshToken', result.refreshToken);
alert('로그인에 성공하셨습니다');

const userId = await getAuthUser();

Choose a reason for hiding this comment

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

마찬가지로 이부분도 api를 바로 호출하는대신 useQuery 와 useQuery 에 select 옵션을 사용해주시면 코드의 더 깔끔해지고 관심사 분리가 될꺼같습니다

Comment on lines +21 to +22
navigate(`chats/${channelId}`);
location.reload();

Choose a reason for hiding this comment

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

만약 페이지 이동시 강제로 페이지가 새로고침이 되어야 되는 상황이면 해당 두줄에 코드 보다 window.location.href 가 더 효율적입니다

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.

5 participants