Skip to content

Commit 8f3006b

Browse files
committed
feat: 본인 질문만 조회 기능 추가
1 parent 194dbe8 commit 8f3006b

2 files changed

Lines changed: 125 additions & 84 deletions

File tree

frontend/src/pages/qna/QnAListPage.js

Lines changed: 67 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -132,20 +132,6 @@ const updateQuestionGroupsByCheckedEvent = (groups, eventData) => {
132132
return hasUpdatedQuestion ? regroupQuestions(updatedQuestions) : groups;
133133
};
134134

135-
const addQuestionToGroups = (groups, question) => {
136-
if (!question?.questionId) return groups;
137-
138-
const existingQuestions = [
139-
...groups.popularQuestions,
140-
...groups.unresolvedQuestions,
141-
...groups.resolvedQuestions,
142-
];
143-
const alreadyExists = existingQuestions.some(item => item.questionId === question.questionId);
144-
145-
if (alreadyExists) return groups;
146-
return regroupQuestions([question, ...existingQuestions]);
147-
};
148-
149135
const buildUnderstandingCheckFromEvent = (eventData) => ({
150136
checkId: eventData.checkId,
151137
content: eventData.content,
@@ -199,6 +185,7 @@ function QnAListPage() {
199185
});
200186

201187
// ── 필터 / 정렬 상태 ─────────────────────────────
188+
const [questionScope, setQuestionScope] = useState('all');
202189
const [filterCurious, setFilterCurious] = useState(false);
203190
const [filterUnsolved, setFilterUnsolved] = useState(false);
204191
const [sortOrder, setSortOrder] = useState('정렬');
@@ -311,47 +298,13 @@ function QnAListPage() {
311298
applyQuestionGroups(nextGroups);
312299
}, [applyQuestionGroups]);
313300

314-
const buildQuestionFromCreatedEvent = useCallback(async (eventData) => {
315-
if (!eventData?.questionId) return null;
316-
317-
// SSE 이벤트의 imageUrls 배열을 blob URL로 변환
318-
const rawUrls = eventData.imageUrls ?? [];
319-
const blobUrls = await Promise.all(
320-
rawUrls.map(async (url) => {
321-
try {
322-
const imgRes = await authFetch(url);
323-
const blob = await imgRes.blob();
324-
return URL.createObjectURL(blob);
325-
} catch {
326-
return null;
327-
}
328-
})
329-
);
301+
const handleQuestionCreatedEvent = useCallback((eventData) => {
302+
if (!eventData?.questionId) return;
330303

331-
return {
332-
questionId: eventData.questionId,
333-
content: eventData.content,
334-
imageUrls: blobUrls.filter(Boolean),
335-
isResolved: false,
336-
isPopular: false,
337-
isLiked: false,
338-
isMine: false,
339-
isNew: eventData.isNew ?? true,
340-
iLiked: false,
341-
likeCount: eventData.likeCount ?? 0,
342-
commentCount: eventData.commentCount ?? 0,
343-
previewComments: [],
344-
createdAt: eventData.createdAt,
345-
};
346-
}, []);
347-
348-
const handleQuestionCreatedEvent = useCallback(async (eventData) => {
349-
const createdQuestion = await buildQuestionFromCreatedEvent(eventData);
350-
if (!createdQuestion) return;
351-
352-
const nextGroups = addQuestionToGroups(questionGroupsRef.current, createdQuestion);
353-
applyQuestionGroups(nextGroups);
354-
}, [applyQuestionGroups, buildQuestionFromCreatedEvent]);
304+
// 생성 이벤트에는 로그인 사용자 기준 isMine 값이 없으므로,
305+
// 목록을 다시 조회해 "내 질문" 필터에서도 정확히 분류되도록 한다.
306+
void fetchQuestions(understandingIndex);
307+
}, [fetchQuestions, understandingIndex]);
355308

356309
const handleQuestionUpdatedEvent = useCallback((eventData) => {
357310
const nextGroups = updateQuestionGroupsByQuestionEvent(questionGroupsRef.current, eventData);
@@ -706,8 +659,9 @@ function QnAListPage() {
706659

707660
const displayedQuestions = (() => {
708661
let list = allQuestions;
709-
if (isStaff && filterUnsolved) list = unresolvedQuestions;
710-
if (!isStaff && filterCurious) list = allQuestions.filter(q => q.iLiked);
662+
if (questionScope === 'mine') list = list.filter(q => q.isMine);
663+
if (isStaff && filterUnsolved) list = list.filter(q => !q.isResolved);
664+
if (!isStaff && filterCurious) list = list.filter(q => q.iLiked);
711665

712666
if (sortOrder === '최신순') {
713667
list = [...list].sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
@@ -730,35 +684,56 @@ function QnAListPage() {
730684

731685
{/* ── 필터 / 정렬 행 ── */}
732686
<div className={styles.filterRow}>
733-
{isStaff ? (
734-
<label className={styles.curiousLabel}>
735-
<input type="checkbox" checked={filterUnsolved}
736-
onChange={e => setFilterUnsolved(e.target.checked)}
737-
className={styles.curiousCheckbox} />
738-
미해결 질문
739-
</label>
740-
) : (
741-
<label className={styles.curiousLabel}>
742-
<input type="checkbox" checked={filterCurious}
743-
onChange={e => setFilterCurious(e.target.checked)}
744-
className={styles.curiousCheckbox} />
745-
저도 궁금해요
746-
</label>
747-
)}
748-
<div className={styles.sortWrapper}>
749-
<button className={styles.sortBtn} onClick={() => setShowSortMenu(prev => !prev)}>
750-
{sortOrder} <SortBtn />
687+
<div className={styles.scopeTabs} role="group" aria-label="질문 조회 범위">
688+
<button
689+
type="button"
690+
className={`${styles.scopeTab} ${questionScope === 'all' ? styles.scopeTabActive : ''}`}
691+
aria-pressed={questionScope === 'all'}
692+
onClick={() => setQuestionScope('all')}
693+
>
694+
전체 질문
751695
</button>
752-
{showSortMenu && (
753-
<ul className={styles.sortMenu}>
754-
{['기본', '최신순', '저도궁금해요순'].map(option => (
755-
<li key={option} className={styles.sortOption}
756-
onClick={() => { setSortOrder(option); setShowSortMenu(false); }}>
757-
{option}
758-
</li>
759-
))}
760-
</ul>
696+
<button
697+
type="button"
698+
className={`${styles.scopeTab} ${questionScope === 'mine' ? styles.scopeTabActive : ''}`}
699+
aria-pressed={questionScope === 'mine'}
700+
onClick={() => setQuestionScope('mine')}
701+
>
702+
내 질문
703+
</button>
704+
</div>
705+
706+
<div className={styles.filterControls}>
707+
{isStaff ? (
708+
<label className={styles.curiousLabel}>
709+
<input type="checkbox" checked={filterUnsolved}
710+
onChange={e => setFilterUnsolved(e.target.checked)}
711+
className={styles.curiousCheckbox} />
712+
미해결 질문
713+
</label>
714+
) : (
715+
<label className={styles.curiousLabel}>
716+
<input type="checkbox" checked={filterCurious}
717+
onChange={e => setFilterCurious(e.target.checked)}
718+
className={styles.curiousCheckbox} />
719+
저도 궁금해요
720+
</label>
761721
)}
722+
<div className={styles.sortWrapper}>
723+
<button className={styles.sortBtn} onClick={() => setShowSortMenu(prev => !prev)}>
724+
{sortOrder} <SortBtn />
725+
</button>
726+
{showSortMenu && (
727+
<ul className={styles.sortMenu}>
728+
{['기본', '최신순', '저도궁금해요순'].map(option => (
729+
<li key={option} className={styles.sortOption}
730+
onClick={() => { setSortOrder(option); setShowSortMenu(false); }}>
731+
{option}
732+
</li>
733+
))}
734+
</ul>
735+
)}
736+
</div>
762737
</div>
763738
</div>
764739
<hr className={styles.divider} />
@@ -802,6 +777,15 @@ function QnAListPage() {
802777

803778
{/* ── 질문 목록 ── */}
804779
<div className={styles.questionList}>
780+
{displayedQuestions.length === 0 && (
781+
<div className={styles.emptyState}>
782+
{questionScope === 'mine'
783+
? '작성한 질문이 없습니다.'
784+
: (filterCurious || filterUnsolved)
785+
? '조건에 맞는 질문이 없습니다.'
786+
: '등록된 질문이 없습니다.'}
787+
</div>
788+
)}
805789
{displayedQuestions.map(question => (
806790
<div key={question.questionId}
807791
className={`${styles.questionCard} ${question.isResolved ? styles.questionCardResolved : ''}`}

frontend/src/pages/qna/QnAListPage.module.css

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,48 @@
3434
.filterRow {
3535
display: flex;
3636
align-items: center;
37-
justify-content: flex-end;
37+
justify-content: space-between;
3838
gap: 12px;
3939
padding: 8px 16px;
4040
}
4141

42+
.scopeTabs {
43+
display: inline-flex;
44+
align-items: center;
45+
padding: 2px;
46+
border: 1px solid var(--gray200);
47+
border-radius: 20px;
48+
background: var(--white);
49+
flex-shrink: 0;
50+
}
51+
52+
.scopeTab {
53+
border: 0;
54+
border-radius: 16px;
55+
background: transparent;
56+
padding: 5px 12px;
57+
color: var(--gray600);
58+
font-family: var(--font-main);
59+
font-size: 13px;
60+
cursor: pointer;
61+
white-space: nowrap;
62+
transition: background-color 0.2s, color 0.2s;
63+
}
64+
65+
.scopeTabActive {
66+
background: var(--main);
67+
color: var(--black);
68+
font-weight: 700;
69+
}
70+
71+
.filterControls {
72+
display: flex;
73+
align-items: center;
74+
justify-content: flex-end;
75+
gap: 12px;
76+
min-width: 0;
77+
}
78+
4279
.curiousLabel {
4380
display: flex;
4481
align-items: center;
@@ -230,6 +267,16 @@
230267
align-items: center;
231268
}
232269

270+
.emptyState {
271+
width: 95%;
272+
box-sizing: border-box;
273+
padding: 56px 16px;
274+
color: var(--gray600);
275+
font-family: var(--font-main);
276+
font-size: 15px;
277+
text-align: center;
278+
}
279+
233280
.questionCard {
234281
padding: 16px 18px;
235282
cursor: pointer;
@@ -769,6 +816,16 @@
769816
.filterRow {
770817
padding: 8px 4px;
771818
gap: 8px;
819+
flex-wrap: wrap;
820+
}
821+
822+
.scopeTab {
823+
padding: 4px 10px;
824+
font-size: 12px;
825+
}
826+
827+
.filterControls {
828+
gap: 8px;
772829
}
773830

774831
/* ── 이해도 바 ── */

0 commit comments

Comments
 (0)