-
Notifications
You must be signed in to change notification settings - Fork 0
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
토이프로젝트 8조: 채팅 서비스 8LACK #2
base: main
Are you sure you want to change the base?
Conversation
[#34] 나와의 채팅방 생성
[#51] 그룹채팅방 검색 및 정렬 기능 추가
[#47] React Query를 이용한 실시간 업데이트 (그룹채팅방)
[#15] 사용자 리스트 즐겨찾기, 개인 채팅창 생성 기능 구현
[#38] 소켓 동시 연결 시 접속 끊기는 이슈 (임시)해결
feat: 즐겨찾기 보완
[#114] 디자인 QA
[#113] 메모이제이션 적용, 웹팩 번들링, ui 수정
docs: README 추가
docs: footer 수정
…on-bar 네비게이션 바 스타일 오류를 수정한다.
Feat: Styled-component 적용 및 Layout 생성
TALK-20--feat/layout/nav 하단 내비게이션 바 추가
Feat: rating 및 mainboard 생성
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.
우선 UI가 정말 깔끔하네요~~ 컨벤션도 맞추고, 가독성있고 맥락에 맞는 로직도 명확한 것 같아요!(중간중간 lodash나 삼항조건식 등등의 코멘트를 남기긴 했지만, 정답은 없고 팀원간 협업하기 좋게 사람의 관점에서 이해가 되는 코드, 가독성이 좋고 맥락이 잘 보이면 그게 최선의 코드라고 생각이 들어요)
웹소켓 사용하면서 많이 고민하며 개발하신 게 느껴집니다.
그리고 useAuthCheck 등 커스텀훅은 잘 만드신 것 같아요. 로그인 여부를 체크해서 라우팅을 달리한다든지에도 이런 식으로 쓸 수 있습니다.
추후에 반응형도 고려해보면 좋을 것 같습니다!
고생하셨습니다👍
+) 회고 내용도 보니 팀내 협업이 잘 이루어진 것 같아서도 보기 좋네요!
@@ -0,0 +1,16 @@ | |||
--- |
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.
github활용 너무 좋습니다~
function App() { | ||
return ( | ||
<UserPublicRoute> | ||
<> | ||
<ServerSocketProvider> | ||
<StyledContainer> | ||
<Toast /> | ||
<Navigation /> | ||
<SideBar /> | ||
<Outlet /> | ||
</StyledContainer> | ||
</ServerSocketProvider> | ||
</> | ||
</UserPublicRoute> | ||
); | ||
} |
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.
컴포넌트 수준만 렌더하고,
provider는 최상위 root 에서 감싸도 될 것 같네요! (위계 통일)
try { | ||
const response = await axios.post( | ||
`${SERVER_URL}/login`, | ||
{id: id, password: pw}, |
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.
이렇게 동일한 파라미터명-변수 일때는
{id: id, password: pw}, | |
{id, password: pw}, |
이렇게 줄여써도 됩니다! 웹스톰이나 이런 에디터에서는 아예 이렇게 하라고 워닝 주기도 하더라고요.
const Avatars: React.FC<IAvatars> = ({users, isPrivate}) => { | ||
if (isPrivate) { | ||
const user = users?.[0]; | ||
return <Avatar src={user?.picture} alt={user?.username} />; | ||
} else { | ||
return ( | ||
<AvatarsContainer> | ||
{users?.slice(0, 4).map((user, index) => ( | ||
<Avatar key={user.id} src={user.picture} alt={user.username} /> | ||
))} | ||
</AvatarsContainer> | ||
); | ||
} | ||
}; |
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.
isPrivate이 어떤의미인가요?
isPrivate플래그에 따라 다른 ui를 렌더하는 경우라면 렌더하는 부분에서 갈라주면 될 것 같아요.
컴포넌트의 렌더/ 로직 등 정의하는 공간을 구분하는 것도 맥락 파악에 용이하기도 하고요~
const Avatars: React.FC<IAvatars> = ({users, isPrivate}) => { | |
if (isPrivate) { | |
const user = users?.[0]; | |
return <Avatar src={user?.picture} alt={user?.username} />; | |
} else { | |
return ( | |
<AvatarsContainer> | |
{users?.slice(0, 4).map((user, index) => ( | |
<Avatar key={user.id} src={user.picture} alt={user.username} /> | |
))} | |
</AvatarsContainer> | |
); | |
} | |
}; | |
const Avatars: React.FC<IAvatars> = ({users, isPrivate}) => { | |
const user = useMemo(() => users?.[0], [users]) // 요럴때 메모이제이션을 하는 거에요! | |
return ( | |
isPrivate ? <Avatar src={user?.picture} alt={user?.username}/> | |
: <AvatarsContainer> | |
{users?.slice(0, 4).map((user, index) => ( | |
<Avatar key={user.id} src={user.picture} alt={user.username}/> | |
))} | |
</AvatarsContainer> | |
) | |
} |
const Drawer: React.FC<IDrawer> = ({isOpen, onClose, children}) => { | ||
const backdrop = { | ||
visible: {opacity: 1}, | ||
hidden: {opacity: 0}, | ||
}; | ||
|
||
const drawer = { | ||
visible: { | ||
x: 0, | ||
transition: { | ||
type: 'spring', | ||
stiffness: 300, | ||
damping: 30, | ||
}, | ||
}, | ||
hidden: { | ||
x: '100%', | ||
transition: { | ||
type: 'spring', | ||
stiffness: 300, | ||
damping: 30, | ||
}, | ||
}, | ||
}; | ||
|
||
return ReactDOM.createPortal( | ||
<AnimatePresence> | ||
{isOpen && ( | ||
<> | ||
<MotionBackdrop variants={backdrop} initial="hidden" animate="visible" exit="hidden" onClick={onClose} /> | ||
<MotionDrawer variants={drawer} initial="hidden" animate="visible" exit="hidden"> | ||
{children} | ||
</MotionDrawer> | ||
</> | ||
)} | ||
</AnimatePresence>, | ||
document.getElementById('drawer-root') as HTMLElement, | ||
); | ||
}; |
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.
요거는 createPortal 으로 만든 것 보니 모달처럼 뒤에 떠있는 것 같은데요,
돔을 지워야할 때는 없는지 나중에 고려해봐도 좋을 것 같아요~
|
||
export default function Navigation() { | ||
const [isModalOpen, setIsModalOpen] = useState(false); | ||
const [isLoggedIn, setIsLoggedIn] = useRecoilState(loginState); |
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.
recoil이 깔끔한 편이더라고요. 상태관리 스택으로 좋은 선택같아요~
<StyledCategoryContainer | ||
onClick={() => { | ||
setIsModalOpen(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.
요런건 코드가 길지 않아서 한줄로 나와도 될 것 같아요.
에디터마다 다르지만 printWidth 이런 값으로 설정에서 지정할 수 있으니 활용해보세요!
newSocket.on('join', (data: NewUser) => { | ||
const joinMessage = | ||
Array.isArray(data.joiners) && data.joiners.length > 0 | ||
? typeof data.joiners[0] === 'string' | ||
? data.joiners.map(joiner => ({ | ||
id: `join-${joiner}-${uuidv4()}`, | ||
text: `${joiner}님이 입장했습니다.`, | ||
userId: 'system', | ||
createdAt: new Date(), | ||
})) | ||
: data.joiners.map(joiner => ({ | ||
id: `join-${joiner.id}-${uuidv4()}`, | ||
text: `${joiner.id}님이 입장했습니다.`, | ||
userId: 'system', | ||
createdAt: new Date(), | ||
})) | ||
: []; |
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.
lodash 등을 활용하면 복잡하지 않게 할 수 있을 것 같아요.
꼭 삼항조건식이 아니고,
경우를 나누어 메시지 값을 정의해놓고 사용해도 되고요.
@@ -0,0 +1,15 @@ | |||
import { ReportHandler } from 'web-vitals'; | |||
|
|||
const reportWebVitals = (onPerfEntry?: ReportHandler) => { |
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 (isLoggedIn) return <Navigate to="/" />; | ||
if (!isLoggedIn) return children; |
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 (isLoggedIn) return <Navigate to="/" />; | |
if (!isLoggedIn) return children; | |
return ( | |
isLoggedIn ? | |
<Navigate to="/"> | |
: <>{children}</> |
프로젝트 소개
8lack 은 다양한 사람들과 다양한 주제로 이야기를 나눠볼 수 있는 채팅 서비스입니다.
다양한 사람들과 자유롭게 소통을 즐길 수 있으며, 그룹 채팅을 통해 사용자들이 서로의 경험을 공유하고 정보를 교환할 수 있는 커뮤니티입니다.
관련 링크
개발 기간
2023.11.06 - 11.16
테스트 계정
📌 요구사항
펼치기
필수
useState
,useReducer
를 활용한 상태 관리 구현Sass
또는styled-component
를 활용한 스타일 구현react
상태를 통한 CRUD 구현custom hook
을 통한 비동기 처리 구현jwt
등의 유저 인증 시스템 (로그인, 회원가입 기능)선택
typescript
를 활용한 앱 구현📜 실행 스크립트
✨ 참여한 사람
참여 가능한 그룹채팅방 리스트
새로운 그룹채팅방 연결
채팅방 내 초대기능
로딩 및 결과 없음 상태 컴포넌트
즐겨찾기 기능
1:1 채팅 연결
마이 페이지
메인페이지 - IntroSection
유저인증 처리
유저 권한에 따른 라우팅
활동중 유저 목록 최신화
메인페이지 - CardSection
실시간 메시지 수신/송신
채팅방 내 유저 접속상태 확인
채팅방 나가기 기능
새로운 채팅방 & 유저 초대 시 알림 기능
사이드바
채팅방 리스트 실시간 업데이트 처리
메인페이지 - HeaderSection
404 페이지
💡 8lack 기능 소개
라우터
채팅 로비 접속 시, 유저 인증 처리 및 서버 소켓을 연결하였습니다.
또한 로그인한 유저가 아니면, 서비스 소개 페이지로 이동하고, 로그인한 유저일 경우 전체 페이지 조회 가능하도록 유저 권한에 따른 라우팅을 설정하였습니다.
로그인 / 회원가입 페이지
로그인 성공 시, 유저 인증 토큰 발급이 발급되고 채팅 로비로 연결됩니다.
회원가입 진행시 입력정보에 대한 유효성을 체크할 수 있는 UI 구성하였습니다. ( 중복확인, 비밀번호 검사 )
사이드바 - 자신이 속한 채팅방 리스트
현재 자신이 속해있는 개인&그룹 채팅방 리스트를 최신 메시지를 받은 순서대로 보여줍니다. 메시지를 받으면 실시간으로 리스트가 업데이트 됩니다.
또한 채팅하고 싶은 방을 클릭 시 바로 채팅에 참여할 수 있습니다.
참여 가능한 그룹채팅방 리스트
아직 내가 속해있지 않은 그룹 채팅방을 조회할 수 있습니다.
이름 및 채팅방 별 최근 채팅을 언제 했는지, 인원이 얼마나 있는지 파악 가능합니다.
또한 정렬을 통해 가나다 순, 최근 채팅 순, 인원 순으로 조회 가능하며 검색도 조합할 수 있습니다.
새로운 그룹채팅방을 만들 수 있습니다.
다른 사용자들을 조회, 추가하여 새로운 그룹채팅방을 생성할 수 있습니다.
사용자 리스트 페이지
이 사이트에 가입한 모든 사용자들을 조회하고, 이름을 검색하여 찾고 싶은 사용자를 찾을 수 있습니다.
원하는 사용자를 즐겨찾기 할 수 있고, 현재 사이트에 접속인 친구를 확인 할 수 있습니다.
또, 원하는 친구와 1:1 채팅을 시작 할 수 있습니다.
채팅 페이지
실시간으로 메시지를 주고 받을 수 있습니다. 채팅방에 연결되자마자 이전 대화 기록이 보이도록 설정했습니다.
Drawer을 열어보면 현재 채팅방 유저의 정보와 현재 접속 상태를 한눈에 파악이 가능합니다.
채팅을 주고 받을 때, 메시지별로 날짜를 그룹화해서 보이도록 했고 새로운 유저가 참여하거나 나갈 시 시스템 메시지도 표시됩니다.
새로운 채팅방이 생성됐거나 기존 채팅방에 유저를 초대할 시 알림 메시지를 받을 수 있습니다.
내 정보 조회 / 수정
자신이 원하는 사진이나 이름으로 변경 할 수 있습니다.
⚒️ 기술 스택
📌 유저 플로우
🗂️ 파일 구조
📍 컨벤션
💭 느낀점 및 회고
김민서
김특희
장수빈
정범환
박나영