Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/chat/bottomMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const BottomMenu = ({
const { messages } = useSSEStore();
const [isOwner, setIsOwner] = useState(false);

messages.forEach((message) => {
if (message.topic === 'match_room_created') {
messages.forEach((eventMessage) => {
if (eventMessage.message.topic === 'match_room_created') {
const userId = localStorage.getItem('userId');
setIsOwner(userId === String(message.roomMasterId));
setIsOwner(userId === String(eventMessage.message.roomMasterId));
}
});

Expand Down
13 changes: 10 additions & 3 deletions src/pages/mathcing/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@ import Timer from '@/components/matchingInfo/TImer';
import useSSEStore from '@/store/useSSEStore';
import useTimerStore from '@/store/useTimerStore';
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

const MatchingInfoPage = () => {
const { reset } = useTimerStore();
const { initializeSSE, messages } = useSSEStore();
const navigate = useNavigate();

const [roomCapacity, setRoomCapacity] = useState<number>(0);
const [roomStatus, setRoomStatus] = useState<'searching' | 'matching'>(
'searching',
);
const [roomId, setRoomId] = useState<number | null>(null);

useEffect(() => {
initializeSSE();
}, [initializeSSE]);

useEffect(() => {
messages.forEach((message) => {
switch (message.topic) {
messages.forEach((eventMessage) => {
switch (eventMessage.eventType) {
case 'match_member_joined':
setRoomCapacity((prev) => Math.max(prev + 1, 4));
break;
Expand All @@ -30,6 +33,7 @@ const MatchingInfoPage = () => {

case 'match_room_created':
setRoomCapacity((prev) => Math.max(prev + 1, 4));
setRoomId(eventMessage.message.roomId);
setRoomStatus('matching');
break;

Expand Down Expand Up @@ -66,7 +70,10 @@ const MatchingInfoPage = () => {
</div>
{roomStatus === 'matching' && (
<div className=" w-full mb-4">
<Button className="w-full" onClick={() => reset()}>
<Button
className="w-full"
onClick={() => navigate(`/chat/${roomId!}`)}
>
채팅방
</Button>
</div>
Expand Down
50 changes: 32 additions & 18 deletions src/store/useSSEStore.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { EventSourcePolyfill } from '@/utils/EventSourcePolyfill';
import { MatchingEvent, MessagesArray } from 'gachTaxi-types';
import { MatchingEvent, MessagesArray, EventType } from 'gachTaxi-types';
import { create } from 'zustand';

interface SSEState {
sse: EventSourcePolyfill | null;
messages: MessagesArray;
messages: MessagesArray[];
initializeSSE: () => void;
closeSSE: () => void;
}
Expand All @@ -19,14 +19,14 @@ const useSSEStore = create<SSEState>((set, get) => ({
const accessToken = localStorage.getItem('accessToken');

if (!accessToken) {
console.error('엑세스 토큰이 없습니다!');
console.error('엑세스 토큰이 없습니다! SSE를 시작할 수 없습니다.');
return;
}

set((state): Partial<SSEState> => {
if (state.sse) {
console.log('이미 구독 중이므로 재구독을 방지합니다.');
return state; // 기존 상태 유지
console.log('🔄 이미 SSE 구독 중이므로 재구독을 방지합니다.');
return state;
}

const sse = new EventSourcePolyfill(
Expand All @@ -39,25 +39,36 @@ const useSSEStore = create<SSEState>((set, get) => ({

sse.onmessage = (event: MessageEvent) => {
const rawData = event.data.trim();
const eventLines = rawData.split('\n');

if (!rawData.startsWith('data:')) {
return;
}
let eventType: EventType = 'init'; // 기본값 설정
let jsonData = '';

eventLines.forEach((line: string) => {
if (line.startsWith('event:')) {
eventType = line.slice(6).trim() as EventType;
} else if (line.startsWith('data:')) {
jsonData = line.slice(5).trim();
}
});

if (!jsonData) return;

const jsonString = rawData.slice(5).trim();
try {
const formatedData: MatchingEvent = JSON.parse(jsonString);
set((state) => ({ messages: [...state.messages, formatedData] }));
const parsedData: MatchingEvent = JSON.parse(jsonData);
set((state) => ({
messages: [...state.messages, { eventType, message: parsedData }],
}));
} catch (error) {
console.error('JSON 파싱 중 오류가 발생했습니다. : ', error);
console.error('⚠️ JSON 파싱 오류 발생:', error);
}
};

sse.onerror = () => {
console.error('SSE 에러 발생, 연결 종료 후 재연결 시도');
console.error(
'🚨 SSE 연결 오류 발생! 연결을 종료하고 5초 후 재연결을 시도합니다.',
Copy link
Member

Choose a reason for hiding this comment

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

ㅋㅋㅋㅋ 콘솔 에러 메시지가 가독성이 완전 👍🏻

);
sse.close();

// ✅ 상태 업데이트 (재연결 가능하도록 sse: null 설정)
set({ sse: null });

setTimeout(() => {
Expand All @@ -68,13 +79,16 @@ const useSSEStore = create<SSEState>((set, get) => ({
return { sse };
});

console.log('SSE 구독 시작');
console.log('SSE 구독 시작');
},

closeSSE: () => {
set((state) => {
state.sse?.close();
return { sse: null, messages: [] };
if (state.sse) {
console.log('🔌 SSE 연결 종료');
state.sse.close();
}
return { sse: null, messages: [] }; // messages 초기화 유지 필요 시 수정 가능
});
},
}));
Expand Down
13 changes: 12 additions & 1 deletion src/types/match.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ declare module 'gachTaxi-types' {
| MatchRoomCancelledEvent
| MatchRoomCompletedEvent;

type EventType =
| 'init'
| 'match_room_completed'
| 'match_room_cancelled'
| 'match_member_cancelled'
| 'match_member_joined'
| 'match_room_created';

// messages 배열 타입 정의
export type MessagesArray = MatchingEvent[];
export interface MessagesArray {
eventType: EventType;
message: MatchingEvent;
}
}