Skip to content

Commit aeeb367

Browse files
committed
fix: 1, 2, 4, 6, 7, 14, 16, 20, 21 이슈 조치
1 parent 7424cbf commit aeeb367

File tree

10 files changed

+65
-38
lines changed

10 files changed

+65
-38
lines changed

src/hooks/useRoomSseClient.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,21 @@ export default function useRoomSseClient(onRoomEvent) {
2121
setConnected(true);
2222
};
2323

24-
eventSource.onmessage = (event) => {
24+
const handleEvent = (type) => (event) => {
2525
try {
2626
const payload = JSON.parse(event.data);
27-
console.log(event);
28-
onRoomEventRef.current?.(payload); // 예: { type: 'ROOM_CREATED', data: { ... } }
27+
console.log(`📨 ${type} 이벤트 수신:`, payload);
28+
onRoomEventRef.current?.({ type, payload });
2929
} catch (e) {
30-
console.error('❌ SSE 메시지 파싱 실패', e);
30+
console.error(`❌ ${type} 이벤트 파싱 실패`, e);
3131
}
3232
};
3333

34+
// 🎯 각각의 이벤트 타입 등록
35+
eventSource.addEventListener('CREATE', handleEvent('CREATE'));
36+
eventSource.addEventListener('UPDATE', handleEvent('UPDATE'));
37+
eventSource.addEventListener('DELETE', handleEvent('DELETE'));
38+
3439
eventSource.onerror = (err) => {
3540
console.error('❌ SSE 연결 오류:', err);
3641
// eventSource.close();

src/hooks/useStompClient.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,6 @@ export default function useStompClient(roomId, onMessage) {
125125
return {
126126
sendMessage: activeSendMessage, // 처음엔 null, 연결 완료되면 함수
127127
ready,
128+
disconnect: () => stompClient?.deactivate(),
128129
};
129130
}

src/layout/game/GameLayout.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,15 @@ const GameLayout = () => {
112112
setGameResult(payload.message.result);
113113
break;
114114
case "EXIT_SUCCESS":
115+
disconnect();
115116
navigate("/room");
116117
break;
117118
default:
118119
console.warn("알 수 없는 메시지", payload);
119120
}
120121
}, [setPlayerList, setRoomSetting, setGameSetting, setSystemNotice, setChat]);
121122

122-
const { sendMessage } = useStompClient(roomId, handleStompMessage);
123+
const { sendMessage, disconnect } = useStompClient(roomId, handleStompMessage);
123124

124125
useEffect(() => {
125126
if (sendMessage) {

src/pages/game/GamePlay.js

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import F1StartingLights from "../../layout/game/components/F1StartingLights"
44
import QuizTimer from "../../layout/game/components/QuizTimer"
55
import QuizQuestion from "../../layout/game/components/QuizQuestion"
66
import QuizResultsModal from "../../layout/game/components/QuizResultsModal"
7-
import {useRecoilValue} from "recoil";
7+
import {useRecoilState, useRecoilValue} from "recoil";
88
import {
99
gameResultAtom,
1010
questionResultAtom,
@@ -20,10 +20,10 @@ function GamePlay() {
2020
const [quizStarted, setQuizStarted] = useState(false);
2121
const [quizCompleted, setQuizCompleted] = useState(false);
2222
const [showResultsModal, setShowResultsModal] = useState(false);
23-
const questions = useRecoilValue(questionsAtom);
24-
const currentQuestion = useRecoilValue(questionStartAtom);
25-
const questionsResult = useRecoilValue(questionResultAtom);
26-
const gameResult = useRecoilValue(gameResultAtom);
23+
const [questions, setQuestions] = useRecoilState(questionsAtom);
24+
const [currentQuestion, setCurrentQuestion] = useRecoilState(questionStartAtom);
25+
const [questionsResult, setQuestionsResult] = useRecoilState(questionResultAtom);
26+
const [gameResult, setGameResult] = useRecoilState(gameResultAtom);
2727
const [visibleQuestion, setVisibleQuestion] = useState(false);
2828
const [showWinnerModal, setShowWinnerModal] = useState(false);
2929

@@ -80,14 +80,23 @@ function GamePlay() {
8080

8181
useEffect(() => {
8282
if (quizCompleted) {
83+
setCurrentQuestion(null);
84+
setGameResult(null);
85+
setQuestions(null);
86+
setQuestionsResult(null);
87+
8388
navigate(`/room/${roomId}`);
8489
}
8590
}, [quizCompleted])
8691

8792
useEffect(() => {
8893
if (gameResult) {
89-
setShowWinnerModal(false);
90-
setShowResultsModal(true);
94+
const timeout = setTimeout(() => {
95+
setShowWinnerModal(false);
96+
setShowResultsModal(true);
97+
}, 3000); // 3초 (3000ms) 후 실행
98+
99+
return () => clearTimeout(timeout);
91100
}
92101
}, [gameResult]);
93102

src/pages/quiz/QuizList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ const QuizList = () => {
158158
}
159159
return rows;
160160
}, []).map((rowItems, rowIndex) => (
161-
<Row key={rowIndex} className="mb-4 g-4 justify-content-center">
161+
<Row key={rowIndex} className="mb-4 g-4 justify-content-start">
162162
{rowItems.map((quiz, colIndex) => (
163163
<Col key={colIndex} xl={3} lg={4} md={6} sm={12} className="d-flex justify-content-center">
164164
<QuizCard

src/pages/rank/Rank.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const rankRequest = async (params) => {
2525
const Rank = () => {
2626
const [keyword, setKeyword] = useState("");
2727
const [tableRows, setTableRows] = useState([]);
28+
const [tablePages, setTablePages] = useState({ currentPage: 1, totalPages: 1 });
2829
const [params, setParams] = useQueryParam();
2930
const { data, isLoading, isFetching } = useApiQuery(
3031
['/stats/rankings', params], // queryKey에 params 포함
@@ -42,6 +43,10 @@ const Rank = () => {
4243
}
4344
})
4445
setTableRows(processedRows);
46+
setTablePages({
47+
currentPage: data.currentPage,
48+
totalPages: data.totalPages
49+
})
4550
}
4651
}, [data])
4752

@@ -107,7 +112,7 @@ const Rank = () => {
107112
</TableBackGroundCard>
108113

109114
{/* Pagination */}
110-
<PaginationNavigator currentPage={data?.currentPage} totalPages={data?.totalPages}
115+
<PaginationNavigator currentPage={tablePages.currentPage} totalPages={tablePages.totalPages}
111116
onPageChange={(page) => setParams({
112117
...params
113118
,page: page,

src/pages/room/CreateRoomModal.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import { useState } from "react"
44
import styles from "./room.module.scss"
5+
import useConfirm from "../../hooks/useConfirm";
56

67
const CreateRoomModal = ({ isOpen, onClose, onSubmit }) => {
78
const [roomName, setRoomName] = useState("")
89
const [maxUserCount, setMaxUserCount] = useState("2")
910
const [locked, setLocked] = useState(false)
10-
const [password, setPassword] = useState("")
11+
const [password, setPassword] = useState("");
12+
const {openConfirm} = useConfirm();
1113

1214
const handlePasswordChange = (e) => {
1315
setPassword(e.target.value)
@@ -22,6 +24,12 @@ const CreateRoomModal = ({ isOpen, onClose, onSubmit }) => {
2224
}
2325

2426
const handleSubmit = () => {
27+
if (locked && !password.trim()) {
28+
openConfirm({
29+
title: "비밀번호를 입력 하세요"
30+
});
31+
return;
32+
}
2533
// 방 생성 로직
2634
onSubmit({ roomName, maxUserCount, locked, password });
2735
onClose();

src/pages/room/RoomCard.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import styles from './room.module.scss';
55
const RoomCard = ({ room, onEnterRoom }) => {
66
const isFullRoom = room.currentUserCount >= room.maxUserCount;
77
const isPrivate = room.locked;
8-
8+
console.log(room);
99
const handleEnterRoom = () => {
1010
if (!isFullRoom && onEnterRoom) {
1111
onEnterRoom(room);
@@ -15,7 +15,7 @@ const RoomCard = ({ room, onEnterRoom }) => {
1515
return (
1616
<div className={styles.quizCard} onClick={handleEnterRoom}>
1717
<div className={styles.hoverCard}>
18-
<h3>{room.roomName}</h3>
18+
<h3>{room.quizTitle}</h3>
1919
<p>{room.description}</p>
2020
<p>제작자: {room.creator}</p>
2121
<p>총 문제: {room.numberOfQuestions} 문제</p>
@@ -31,7 +31,7 @@ const RoomCard = ({ room, onEnterRoom }) => {
3131
</div>
3232

3333
<div className={styles.quizInfo}>
34-
<h3 className={styles.quizTitle}>{room.title}</h3>
34+
<h3 className={styles.quizTitle}>{room.roomName}</h3>
3535
<div className={styles.quizStats}>
3636
<span className={`${styles.quizParticipants} ${isFullRoom ? styles.full : ''}`}>
3737
인원: {room.currentUserCount} / {room.maxUserCount}

src/pages/room/RoomList.js

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import axios from "axios";
1212
import {useApiMutation} from "../../hooks/useApiMutation";
1313
import {useNavigate} from "react-router-dom";
1414
import useRoomSseClient from "../../hooks/useRoomSseClient";
15+
import Spinner from "../../shared/Spinner";
1516

1617
const roomsRequest = async () => {
1718
const response = await axios.get(`/rooms`);
@@ -32,7 +33,6 @@ const RoomList = () => {
3233
const [searchTerm, setSearchTerm] = useState("");
3334
const [currentPage, setCurrentPage] = useState(1);
3435
const [rooms, setRooms] = useState([]);
35-
const [filteredRooms, setFilteredRooms] = useState([]);
3636
const [isModalOpen, setIsModalOpen] = useState(false);
3737
const [isFullModalOpen, setIsFullModalOpen] = useState(false);
3838
const [isPasswordModalOpen, setIsPasswordModalOpen] = useState(false);
@@ -41,44 +41,43 @@ const RoomList = () => {
4141
const navigate = useNavigate();
4242
const ROOMS_PER_PAGE = 8 // 한 페이지당 8개
4343

44-
const { data, refetch } = useApiQuery(
44+
const { data, isLoading } = useApiQuery(
4545
["rooms"],
4646
() => roomsRequest(),
4747
);
4848

4949
useRoomSseClient((event) => {
5050
const { type, payload } = event;
51-
console.log(type, payload)
5251
setRooms((prev) => {
5352
switch (type) {
5453
case 'CREATE':
5554
// 이미 있는 방이면 추가하지 않음 (중복 방지)
56-
if (prev.some((room) => room.roomId === payload.roomId)) {
55+
if (prev.some((room) => room.roomId === payload.payload.roomId)) {
5756
return prev;
5857
}
59-
return [...prev, payload];
58+
return [...prev, payload.payload];
6059

6160
case 'UPDATE':
6261
return prev.map((room) =>
63-
room.roomId === payload.roomId ? { ...room, ...payload } : room
62+
room.roomId === payload.payload.roomId ? { ...room, ...payload.payload } : room
6463
);
6564

6665
case 'DELETE':
67-
return prev.filter((room) => room.roomId !== payload.roomId);
66+
return prev.filter((room) => room.roomId !== payload.payload.roomId);
6867

6968
default:
7069
return prev;
7170
}
7271
});
7372
});
7473

75-
const { mutate: createRoomMutate } = useApiMutation(createRoomRequest, {
74+
const { mutate: createRoomMutate, isLoading: isCreateRoomLoading } = useApiMutation(createRoomRequest, {
7675
onSuccess: (data) => {
7776
navigate(`/room/${data.roomId}`);
7877
},
7978
});
8079

81-
const { mutate: enterRoomMutate } = useApiMutation(enterRoomRequest, {
80+
const { mutate: enterRoomMutate, isLoading: isEnterRoomLoading } = useApiMutation(enterRoomRequest, {
8281
onSuccess: (data, variables) => {
8382
// 임시로 그냥 모달 닫고 입장했다고 가정
8483
setIsPasswordModalOpen(false);
@@ -90,7 +89,6 @@ const RoomList = () => {
9089
useEffect(() => {
9190
if (data) {
9291
setRooms(data.rooms);
93-
setFilteredRooms(data.rooms);
9492
}
9593
}, [data])
9694

@@ -104,15 +102,14 @@ const RoomList = () => {
104102
}, []);
105103

106104
const handleSearch = () => {
107-
const filtered = rooms.filter((room) => room.roomName.toLowerCase().includes(searchTerm.toLowerCase()));
108-
setFilteredRooms(filtered);
105+
setRooms((prev) => prev.filter((room) => room.roomName.toLowerCase().includes(searchTerm.toLowerCase())));
109106
setCurrentPage(1); // 검색 시 첫 페이지로 이동
110107
setSearchOn(true);
111108
}
112109

113110
const handleReset = () => {
114111
setSearchTerm('');
115-
setFilteredRooms(rooms);
112+
setRooms(data.rooms);
116113
setCurrentPage(1); // 검색 시 첫 페이지로 이동
117114
setSearchOn(false);
118115
};
@@ -142,16 +139,17 @@ const RoomList = () => {
142139
}
143140

144141
// 현재 페이지에 표시할 방들 계산
145-
const totalPages = Math.ceil(filteredRooms.length / ROOMS_PER_PAGE)
142+
const totalPages = Math.ceil(rooms.length / ROOMS_PER_PAGE)
146143
const startIndex = (currentPage - 1) * ROOMS_PER_PAGE
147144
const endIndex = startIndex + ROOMS_PER_PAGE
148-
const currentRooms = filteredRooms.slice(startIndex, endIndex)
145+
const currentRooms = rooms.slice(startIndex, endIndex)
149146

150147
// 빈 카드로 채우기 (레이아웃 안정화를 위해)
151148
const emptyCards = Array(Math.max(0, ROOMS_PER_PAGE - currentRooms.length)).fill(null)
152149

153150
return (
154151
<div className={styles.mainContainer}>
152+
<Spinner show={isLoading || isEnterRoomLoading || isCreateRoomLoading} />
155153
{/* Main Content */}
156154
<main className={styles.mainContent}>
157155
{/* Search Section */}
@@ -188,7 +186,7 @@ const RoomList = () => {
188186
{searchTerm && searchOn && (
189187
<div className={styles.searchInfo}>
190188
<p>
191-
"{searchTerm}" 검색 결과: {filteredRooms.length}개의 방
189+
"{searchTerm}" 검색 결과: {rooms.length}개의 방
192190
</p>
193191
</div>
194192
)}
@@ -205,13 +203,13 @@ const RoomList = () => {
205203
</section>
206204

207205
{/* 검색 결과가 없을 때 */}
208-
{filteredRooms.length === 0 && searchTerm && (
206+
{rooms.length === 0 && searchTerm && (
209207
<div className={styles.noResults}>
210208
<p>검색 결과가 없습니다.</p>
211209
<button
212210
onClick={() => {
213211
setSearchTerm("")
214-
setFilteredRooms(dummyRooms)
212+
setRooms(data.rooms)
215213
setCurrentPage(1)
216214
}}
217215
>

src/shared/table/FlexibleTableHeader.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ const FlexibleTableHeader = ({ header, openColumn, handleAlign, handleSort, togg
6060
</div>
6161
{header.column.getCanResize() && (
6262
<div
63-
onMouseDown={header.getResizeHandler()}
64-
onTouchStart={header.getResizeHandler()}
63+
// onMouseDown={header.getResizeHandler()}
64+
// onTouchStart={header.getResizeHandler()}
6565
className={`${style.resizer} ${
6666
header.column.getIsResizing() ? style.isResizing : ''
6767
}`}

0 commit comments

Comments
 (0)