Skip to content

Commit

Permalink
[Feat] 채팅방 생성및 요청 기능 (#127)
Browse files Browse the repository at this point in the history
* feat: 채팅 생성 기능

* refactor: 게더링 카드 유저 아이디 props 설정

* feat: 채팅 채팅방 목록

* feat: node global 설정

* feat: 채팅방 참여

* feat: 채팅 보내기 기능

* remove: 안쓰는 파일 삭제
  • Loading branch information
joarthvr authored Feb 11, 2025
1 parent 16097af commit 17f3f79
Show file tree
Hide file tree
Showing 25 changed files with 885 additions and 307 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@hookform/resolvers": "^3.9.1",
"@nivo/core": "^0.88.0",
"@nivo/pie": "^0.88.0",
"@stomp/stompjs": "^7.0.0",
"@tanstack/react-query": "^5.60.6",
"@tanstack/react-query-devtools": "^5.60.6",
"@types/react-datepicker": "^7.0.0",
Expand All @@ -49,6 +50,7 @@
"react-slick": "^0.30.2",
"sass": "^1.81.0",
"slick-carousel": "^1.8.1",
"sockjs-client": "^1.6.1",
"sweetalert2": "^11.14.5",
"yup": "^1.4.0",
"zustand": "^5.0.1"
Expand Down Expand Up @@ -80,6 +82,7 @@
"@types/react": "^18.3.11",
"@types/react-dom": "^18.3.1",
"@types/react-slick": "^0.23.13",
"@types/sockjs-client": "^1.5.4",
"@typescript-eslint/eslint-plugin": "^8.12.2",
"@typescript-eslint/parser": "^8.12.2",
"@vitejs/plugin-react": "^4.3.3",
Expand Down
25 changes: 21 additions & 4 deletions src/app/ModalProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,40 @@ const MODAL_COMPONENTS = {
} as const;

const ModalProvider = ({ children }: PropsWithChildren) => {
const { isOpen, modalKey, selectedUser, close } = useModalStore(
const { isOpen, modalKey, selectedUser, targetId, close } = useModalStore(
useShallow(state => ({
modalKey: state.modalKey,
isOpen: state.isOpen,
selectedUser: state.selectedUser,
targetId: state.targetId,
close: state.actions.close,
})),
);
const state = useModalStore.getState();
console.log('state:', state);

const ModalComponent = modalKey ? MODAL_COMPONENTS[modalKey] : null;
console.log('isOpen:', isOpen);

const ModalComponent = modalKey ? MODAL_COMPONENTS[modalKey] : null;
console.log('selectedUser:', selectedUser);
console.log('targetId:', targetId);
const renderModal = () => {
if (!isOpen || !ModalComponent) return null;

// ContactModal인 경우 username props 전달
// ContactModal인 경우 username과 targetId props 전달
if (modalKey === 'contact') {
return <ModalComponent isOpen={isOpen} onClose={close} username={selectedUser} />;
// if (!targetId) {
// console.warn('targetId is required for ContactModal');
// return null;
// }
return (
<ModalComponent
isOpen={isOpen}
onClose={close}
targetId={targetId}
username={selectedUser}
/>
);
}

// 다른 모달의 경우 기존대로 렌더링
Expand Down
25 changes: 23 additions & 2 deletions src/features/chatting/api/chatting.api.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import type { PostCreateChatRoomResponse } from './chatting.dto';
import type { ChatCategory } from './types';

import type { NotificationType } from '@/features/notification/notification.type';
import api from '@/shared/api/baseApi';

export const postCreateChatRoom = (chatCategory: NotificationType, targetId: number) =>
export const postCreateChatRoom = (chatCategory: ChatCategory, targetId: number) =>
api
.post<PostCreateChatRoomResponse>('/chat-room', { chatCategory, targetId })
.then(res => res.data)
.catch(error => {
console.error('채팅방 생성 요청 실패:', error.response?.data);
console.error('요청 데이터:', { chatCategory, targetId });
throw error;
});
export const getChatRoomList = () => api.get('/chat-room/me').then(res => res.data);
export const participateChatRoom = (chatRoomId: number) =>
api
.post<PostCreateChatRoomResponse>(`/chat-room/participation/${chatRoomId}`)
.then(res => res.data);

export const getChatHistory = (chatRoomId: number, size: number = 3, lastSendAt?: string) => {
const params = new URLSearchParams({
size: size.toString(),
...(lastSendAt && { lastSendAt }),
});

return api.get(`/chat-room/chats/${chatRoomId}?${params}`).then(res => res.data);
};
export const leaveChatRoom = (chatRoomId: number) =>
api.delete(`/chat-room/leave/${chatRoomId}`).then(res => res.data);
18 changes: 18 additions & 0 deletions src/features/chatting/api/chatting.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,21 @@ export interface PostCreateChatRoomResponse {
data: number;
timeStamp: string;
}
export interface ChatRequest {
content: string;
imgUrls?: {
imgUrl: string;
}[];
}

// 응답 형식
export interface ChatResponse {
chatRoomId: number;
email: string;
profileImg: string;
content: string;
imgUrls?: {
imgUrl: string;
}[];
sendAt: string;
}
114 changes: 102 additions & 12 deletions src/features/chatting/api/chatting.hook.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,113 @@
import { useMutation } from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useInfiniteQuery } from '@tanstack/react-query';

import { postCreateChatRoom } from './chatting.api';
import {
getChatRoomList,
participateChatRoom,
postCreateChatRoom,
getChatHistory,
leaveChatRoom,
} from './chatting.api';
import type { ChatCategory, ChatListResponse } from './types';

import type { NotificationType } from '@/features/notification/notification.type';
import { customToast } from '@/shared/ui';

export const useCreateChatRoom = () =>
useMutation({
mutationFn: ({
chatCategory,
targetId,
}: {
chatCategory: NotificationType;
targetId: number;
}) => postCreateChatRoom(chatCategory, targetId),
onSuccess: async () => {
mutationFn: ({ chatCategory, targetId }: { chatCategory: ChatCategory; targetId: number }) =>
postCreateChatRoom(chatCategory, targetId),
onSuccess: async response => {
console.log('채팅방 생성 결과:', response); // 응답 데이터 확인
await customToast({ text: '채팅방이 생성되었습니다.', timer: 3000, icon: 'success' });
},
onError: async () => {
await customToast({ text: '채팅방 생성에 실패하였습니다', timer: 3000, icon: 'error' });
},
});

export const useChattingList = () => {
return useQuery<ChatListResponse>({
queryKey: ['chatRooms'],
queryFn: async () => {
try {
const response = await getChatRoomList();
console.log('채팅방 목록 응답:', response); // 디버깅용
return response;
} catch (error) {
console.error('채팅방 목록 조회 실패:', error);
throw error;
}
},
// 실패 시 재시도 방지
retry: false,
// 빈 데이터 초기값 설정
initialData: {
data: {
chatRooms: [],
},
timeStamp: new Date().toISOString(),
},
});
};

// 채팅방 참여 훅
export const useChatRoomParticipation = () =>
useMutation({
mutationFn: (chatRoomId: number) => participateChatRoom(chatRoomId),
onSuccess: async response => {
console.log('채팅방 참여 결과:', response);
await customToast({ text: '채팅방에 참여하였습니다.', timer: 3000, icon: 'success' });
},
onError: async () => {
await customToast({ text: '채팅방 참여에 실패하였습니다', timer: 3000, icon: 'error' });
},
});

export const useChatHistory = (chatRoomId: number, size: number = 20) => {
return useInfiniteQuery({
queryKey: ['chatHistory', chatRoomId],
queryFn: async ({ pageParam }) => {
try {
const response = await getChatHistory(chatRoomId, size, pageParam);
return response.data;
} catch (error) {
console.error('채팅 히스토리 조회 실패:', error);
throw error;
}
},
getNextPageParam: lastPage => {
if (!lastPage.hasNext) return undefined;
return lastPage.lastSendAt;
},
initialPageParam: undefined,
select: data => ({
pages: data.pages,
pageParams: data.pageParams,
// 모든 채팅 메시지를 하나의 배열로 합치기
allChats: data.pages.flatMap(page => page.chats),
}),
});
};
export const useLeaveChatRoom = () => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: (chatRoomId: number) => leaveChatRoom(chatRoomId),
onSuccess: async () => {
// 채팅방 목록 쿼리 무효화하여 새로고침
queryClient.invalidateQueries({ queryKey: ['chatRooms'] });
// 성공 토스트 메시지
await customToast({
text: '채팅방에서 나갔습니다.',
timer: 3000,
icon: 'success',
});
},
onError: async () => {
await customToast({
text: '채팅방 나가기에 실패했습니다.',
timer: 3000,
icon: 'error',
});
},
});
};
Loading

1 comment on commit 17f3f79

@github-actions
Copy link

Choose a reason for hiding this comment

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

⚡ Lighthouse report for http://localhost:3000/

Category Score
🔴 Performance 31
🟠 Accessibility 70
🟠 Best Practices 79
🟠 SEO 83

Detailed Metrics

Metric Value
🔴 First Contentful Paint 40.8 s
🔴 Largest Contentful Paint 79.7 s
🔴 Total Blocking Time 630 ms
🟠 Cumulative Layout Shift 0.178
🔴 Speed Index 40.8 s

Please sign in to comment.