diff --git a/src/features/chat/components/ChatComponents.tsx b/src/features/chat/components/ChatComponents.tsx
new file mode 100644
index 00000000..c328ba53
--- /dev/null
+++ b/src/features/chat/components/ChatComponents.tsx
@@ -0,0 +1,31 @@
+'use client';
+
+import { useState } from 'react';
+import { sendMessage } from '../utils/socket';
+
+const ChatComponent = () => {
+ const [message, setMessage] = useState('');
+ const roomId = 44;
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ if (message.trim()) {
+ sendMessage(roomId, message);
+ setMessage('');
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default ChatComponent;
diff --git a/src/features/chat/components/chat-card/ChatCard.tsx b/src/features/chat/components/chat-card/ChatCard.tsx
index 36666cc7..4d20f415 100644
--- a/src/features/chat/components/chat-card/ChatCard.tsx
+++ b/src/features/chat/components/chat-card/ChatCard.tsx
@@ -32,7 +32,7 @@ function ChatCardBox({
return (
-
- 채팅에 활발히
-
- 참여해 보세요!
-
-
- );
-}
-
-export default ChatHeader;
diff --git a/src/features/chat/container/BookClubChatContainer.tsx b/src/features/chat/container/BookClubChatContainer.tsx
index f1f17226..a84a95a7 100644
--- a/src/features/chat/container/BookClubChatContainer.tsx
+++ b/src/features/chat/container/BookClubChatContainer.tsx
@@ -2,123 +2,35 @@
import React from 'react';
import ChatCard from '@/features/chat/components/chat-card/ChatCard';
-
-interface BookClubChatData {
- imageUrl: string;
- isHost: boolean;
- title: string;
- currentParticipants: number;
- lastMessage: string;
- lastMessageTime: string;
- unreadCount: number;
-}
+import { bookClubs } from '@/api/book-club/react-query';
+import { useQuery } from '@tanstack/react-query';
+import { BookClubProps } from '@/features/chat/components/chat-card/types/variantChatCard';
+import Link from 'next/link';
export default function BookClubChatContainer() {
- const mockBookClubChats: BookClubChatData[] = [
- {
- imageUrl: '/images/profile.png',
- isHost: true,
- title: '철학으로 보는 현대사회',
- currentParticipants: 8,
- lastMessage: '다음 모임은 니체의 차라투스트라를 읽어볼까요?',
- lastMessageTime: '11:30',
- unreadCount: 5,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: false,
- title: 'SF 소설 읽기 모임',
- currentParticipants: 12,
- lastMessage: '듄 시리즈 완독하신 분들 계신가요?',
- lastMessageTime: '09:15',
- unreadCount: 2,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: true,
- title: '심리학 북클럽',
- currentParticipants: 6,
- lastMessage: '이번주 토론 주제는 프로이트의 꿈의 해석입니다.',
- lastMessageTime: '어제',
- unreadCount: 0,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: false,
- title: '시 읽는 청춘들',
- currentParticipants: 15,
- lastMessage: '이상의 시를 함께 감상해보아요',
- lastMessageTime: '어제',
- unreadCount: 7,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: false,
- title: '경제경영 독서모임',
- currentParticipants: 10,
- lastMessage: '이번 주 도서: 넛지',
- lastMessageTime: '2일 전',
- unreadCount: 1,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: true,
- title: '세계문학 산책',
- currentParticipants: 7,
- lastMessage: '카프카 변신 읽고 오세요!',
- lastMessageTime: '3일 전',
- unreadCount: 4,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: false,
- title: '에세이 교환일기',
- currentParticipants: 4,
- lastMessage: '무라카미 하루키의 에세이집 추천합니다',
- lastMessageTime: '1주일 전',
- unreadCount: 0,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: true,
- title: '미스터리 소설 탐독',
- currentParticipants: 9,
- lastMessage: '애거사 크리스티 특집 시작합니다',
- lastMessageTime: '1주일 전',
- unreadCount: 12,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: false,
- title: '인문학 스터디',
- currentParticipants: 11,
- lastMessage: '유발 하라리의 사피엔스 함께 읽어요',
- lastMessageTime: '2주일 전',
- unreadCount: 3,
- },
- {
- imageUrl: '/images/profile.png',
- isHost: true,
- title: '고전문학 독파',
- currentParticipants: 6,
- lastMessage: '돈키호테 완독 인증하신 분들 계신가요?',
- lastMessageTime: '2주일 전',
- unreadCount: 8,
- },
- ];
+ const { queryKey, queryFn } = bookClubs.myJoined({ order: 'DESC' });
+ const { data, isLoading, error } = useQuery({
+ queryKey,
+ queryFn,
+ });
+
+ const bookClubChats = data?.data?.bookClubs || [];
+
+ if (isLoading) return
로딩중...
;
+ if (error) return
에러가 발생했습니다
;
return (
- {mockBookClubChats.map((chat, index) => (
- console.log('채팅방 클릭'),
- }}
- />
+ {bookClubChats.map((chat: BookClubProps, id: number) => (
+
+
+
))}
diff --git a/src/features/chat/utils/socket.ts b/src/features/chat/utils/socket.ts
index 9caadc74..82ab4ad9 100644
--- a/src/features/chat/utils/socket.ts
+++ b/src/features/chat/utils/socket.ts
@@ -1,7 +1,7 @@
import SockJS from 'sockjs-client';
import { Client } from '@stomp/stompjs';
-import { getMyJoined } from '@/features/profile/api/myJoinedApi';
import { BookClub } from '@/types/bookclubs';
+import apiClient from '@/lib/utils/apiClient';
let stompClient: Client | null = null;
@@ -17,33 +17,32 @@ export interface ChatMessage {
export const initializeSocket = async (token: string) => {
try {
- const { bookClubs } = await getMyJoined({
- order: 'DESC',
- size: 10,
- page: 1,
+ const response = await apiClient.get('/book-clubs/my-joined', {
+ params: {
+ order: 'DESC',
+ size: 10,
+ page: 1,
+ },
});
+ const { bookClubs } = response.data;
+
const client = new Client({
webSocketFactory: () =>
new SockJS(`${process.env.NEXT_PUBLIC_API_URL}/ws?token=${token}`),
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
- debug: function (str) {
- console.log('STOMP: ' + str);
- },
});
client.onConnect = () => {
+ console.log('소켓 연결 성공');
bookClubs.forEach((club: BookClub) => {
- const subscription = client.subscribe(
- `/topic/group-chat/${club.id}`,
- (message) => {
- const chatMessage: ChatMessage = JSON.parse(message.body);
- console.log(`모임 ${club.title}의 새 메시지 수신:`, chatMessage);
- },
- );
- console.log(`모임 ${club.id} 구독 완료:`, subscription.id);
+ console.log('구독 시도:', club.id);
+ client.subscribe(`/topic/group-chat/${club.id}`, (message) => {
+ const chatMessage: ChatMessage = JSON.parse(message.body);
+ console.log(`모임 ${club.title}의 새 메시지 수신:`, chatMessage);
+ });
});
};
From 33dc630951c8543be054d643d5182b940403e85d Mon Sep 17 00:00:00 2001
From: Sungu Kim <108677235+haegu97@users.noreply.github.com>
Date: Tue, 31 Dec 2024 14:41:33 +0900
Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=92=84[Design]=20=EB=92=A4=EB=A1=9C?=
=?UTF-8?q?=EA=B0=80=EA=B8=B0=20=EB=B2=84=ED=8A=BC,=20=ED=96=84=EB=B2=84?=
=?UTF-8?q?=EA=B1=B0=20=EB=A9=94=EB=89=B4=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80=20#245?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/icons/GoBackIcon.tsx | 24 ++++++++++++++++++++++++
public/icons/HamburgerMenuIcon.tsx | 30 ++++++++++++++++++++++++++++++
src/app/chat/[id]/page.tsx | 29 +++++++++++++++++++++++------
3 files changed, 77 insertions(+), 6 deletions(-)
create mode 100644 public/icons/GoBackIcon.tsx
create mode 100644 public/icons/HamburgerMenuIcon.tsx
diff --git a/public/icons/GoBackIcon.tsx b/public/icons/GoBackIcon.tsx
new file mode 100644
index 00000000..766f9918
--- /dev/null
+++ b/public/icons/GoBackIcon.tsx
@@ -0,0 +1,24 @@
+function GoBackIcon({
+ width = 14,
+ height = 14,
+ strokeColor = '#B4B5B6',
+ ...props
+}) {
+ return (
+
+ );
+}
+
+export default GoBackIcon;
diff --git a/public/icons/HamburgerMenuIcon.tsx b/public/icons/HamburgerMenuIcon.tsx
new file mode 100644
index 00000000..3d5dbb25
--- /dev/null
+++ b/public/icons/HamburgerMenuIcon.tsx
@@ -0,0 +1,30 @@
+function HamburgerMenuIcon({
+ width = 18,
+ height = 14,
+ strokeColor = '#B4B5B6',
+ ...props
+}) {
+ return (
+
+ );
+}
+
+export default HamburgerMenuIcon;
diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx
index 4f3d9702..a80aa851 100644
--- a/src/app/chat/[id]/page.tsx
+++ b/src/app/chat/[id]/page.tsx
@@ -6,6 +6,9 @@ import { usePathname } from 'next/navigation';
import { GroupedMessage } from '@/features/chat-room/types/chatBubbleList';
import ChatCard from '@/features/chat/components/chat-card/ChatCard';
import ParticipantCounter from '@/components/participant-counter/ParticipantCounter';
+import IconButton from '@/components/icon-button/IconButton';
+import GoBackIcon from '../../../../public/icons/GoBackIcon';
+import HamburgerMenuIcon from '../../../../public/icons/HamburgerMenuIcon';
const groupedMessagesExample: GroupedMessage[] = [
{
@@ -42,12 +45,26 @@ function ChatRoomPage() {
console.log('chatId: ', chatId);
return (
- <>
+
-
-
-
채팅
-
+
+
+
+
}
+ onClick={() => console.log('채팅 버튼 클릭')}
+ className="bg-gray-light-02"
+ />
+
채팅
+
+
+
+ }
+ onClick={() => console.log('메뉴 열기 버튼 클릭')}
+ className="bg-gray-light-02"
+ />
+
{}}
/>
- >
+
);
}
From 0a50818ad3fe2eb036494e9e9ae8c0c659b0ce07 Mon Sep 17 00:00:00 2001
From: Sungu Kim <108677235+haegu97@users.noreply.github.com>
Date: Fri, 3 Jan 2025 10:50:19 +0900
Subject: [PATCH 05/10] =?UTF-8?q?=E2=9C=A8[Feat]=20=EC=B1=84=ED=8C=85=20?=
=?UTF-8?q?=EA=B8=B0=EB=8A=A5,=20=EC=B5=9C=EC=8B=A0=20=EB=A9=94=EC=8B=9C?=
=?UTF-8?q?=EC=A7=80=20=EB=AF=B8=EB=A6=AC=EB=B3=B4=EA=B8=B0=20=EA=B0=9C?=
=?UTF-8?q?=EB=B0=9C=20#245?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
next.config.ts | 5 +-
src/app/chat/[id]/page.tsx | 154 +++++++++++++-----
src/app/chat/page.tsx | 4 +-
.../chat-bubble-list/ChatBubbleList.tsx | 6 +-
.../components/ChatMessage.tsx | 9 +-
.../components/SystemMessage.tsx | 2 +-
.../chat-room/hooks/useMessageRenderer.ts | 7 +-
.../chat-room/types/chatBubbleList.ts | 12 +-
.../chat/container/BookClubChatContainer.tsx | 62 +++++--
src/features/chat/utils/chatRoom.ts | 10 ++
src/features/chat/utils/socket.ts | 119 ++++++++++++--
11 files changed, 304 insertions(+), 86 deletions(-)
create mode 100644 src/features/chat/utils/chatRoom.ts
diff --git a/next.config.ts b/next.config.ts
index 1be1526f..51ca252f 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -2,7 +2,10 @@ import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
images: {
- domains: ['sprint-fe-project.s3.ap-northeast-2.amazonaws.com'],
+ domains: [
+ 'sprint-fe-project.s3.ap-northeast-2.amazonaws.com',
+ 'codeit-bookco.s3.ap-northeast-2.amazonaws.com',
+ ],
},
instrumentation: {
enabled: true,
diff --git a/src/app/chat/[id]/page.tsx b/src/app/chat/[id]/page.tsx
index a80aa851..2fe8d1b6 100644
--- a/src/app/chat/[id]/page.tsx
+++ b/src/app/chat/[id]/page.tsx
@@ -1,51 +1,124 @@
'use client';
-import React from 'react';
+import React, { useEffect, useState } from 'react';
import ChatBubbleList from '@/features/chat-room/container/chat-bubble-list/ChatBubbleList';
import { usePathname } from 'next/navigation';
-import { GroupedMessage } from '@/features/chat-room/types/chatBubbleList';
import ChatCard from '@/features/chat/components/chat-card/ChatCard';
import ParticipantCounter from '@/components/participant-counter/ParticipantCounter';
import IconButton from '@/components/icon-button/IconButton';
import GoBackIcon from '../../../../public/icons/GoBackIcon';
import HamburgerMenuIcon from '../../../../public/icons/HamburgerMenuIcon';
-
-const groupedMessagesExample: GroupedMessage[] = [
- {
- date: '2024년 3월 15일',
- messages: [
- {
- type: 'join',
- date: '2024-03-15T14:29:00',
- user: '김철수',
- },
- {
- type: 'chat',
- date: '2024-03-15T14:30:00',
- sender: '김철수',
- senderId: '123',
- content: '안녕하세요!',
- profileImage: '/images/profile.png',
- },
- {
- type: 'chat',
- date: '2024-03-15T14:31:00',
- sender: '이영희',
- senderId: '456',
- content: '환영합니다!',
- profileImage: '/images/profile.png',
- },
- ],
- },
-];
+import MessageInput from '@/components/input/message-input/MessageInput';
+import {
+ ChatHistoryResponse,
+ ChatMessage,
+ getChatHistory,
+ sendMessage,
+ subscribeToChat,
+} from '@/features/chat/utils/socket';
+import {
+ ChatMessageType,
+ GroupedMessage,
+ SystemMessageType,
+} from '@/features/chat-room/types/chatBubbleList';
function ChatRoomPage() {
const pathname = usePathname();
const chatId = pathname?.split('/').pop() || '';
- console.log('chatId: ', chatId);
+ const [message, setMessage] = useState('');
+ const [chatHistory, setChatHistory] = useState
({
+ historyResponses: [],
+ });
+
+ useEffect(() => {
+ const loadChatHistory = async () => {
+ try {
+ const history = await getChatHistory(Number(chatId));
+ setChatHistory(history);
+ } catch (error) {
+ console.error('채팅 히스토리 로딩 실패:', error);
+ }
+ };
+
+ const handleNewMessage = (message: ChatMessage) => {
+ setChatHistory((prev: any) => {
+ if (!prev?.historyResponses?.length) {
+ return {
+ historyResponses: [
+ {
+ date: new Date().toISOString(),
+ messages: [message],
+ },
+ ],
+ };
+ }
+
+ return {
+ ...prev,
+ historyResponses: [
+ ...prev.historyResponses.slice(0, -1),
+ {
+ ...prev.historyResponses[prev.historyResponses.length - 1],
+ messages: [
+ ...prev.historyResponses[prev.historyResponses.length - 1]
+ .messages,
+ message,
+ ],
+ },
+ ],
+ };
+ });
+ };
+
+ loadChatHistory();
+ const subscription = subscribeToChat(Number(chatId), handleNewMessage);
+
+ return () => {
+ subscription.unsubscribe();
+ };
+ }, [chatId]);
+
+ const handleMessageChange = (e: React.ChangeEvent) => {
+ setMessage(e.target.value);
+ };
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ if (message.trim()) {
+ sendMessage(Number(chatId), message);
+ setMessage('');
+ }
+ };
+
+ const convertToGroupedMessage = (
+ history: ChatHistoryResponse,
+ ): GroupedMessage[] => {
+ return history.historyResponses.map((response) => ({
+ date: response.date,
+ messages: response.messages.map((msg) => {
+ if (msg.type === 'CHAT') {
+ return {
+ type: 'CHAT',
+ id: msg.id,
+ date: msg.date,
+ userId: msg.userId,
+ userNickname: msg.userNickname,
+ content: msg.content,
+ } as ChatMessageType;
+ } else {
+ return {
+ type: msg.type,
+ id: msg.id,
+ date: msg.date,
+ user: msg.userNickname,
+ } as SystemMessageType;
+ }
+ }),
+ }));
+ };
return (
-