'use client'; import './style.scss'; import { useState, useEffect, useMemo, useCallback } from 'react'; import { FaqCategory, FaqItem } from '@/types/response/page/faq'; import { fetchApi } from '@/lib/utils/client'; import { FaqCategoryResponse, FaqItemsResponse } from '@/types/response/page/faq'; import NavTab from '@/app/(main)/support/navTab'; import Loading from '@/app/component/Loading'; // 텍스트에서 키워드를 로 감싸서 highlight function highlightText(text: string, keyword: string): React.ReactNode { if (!keyword.trim()) { return text; } const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(`(${escaped})`, 'gi'); const parts = text.split(regex); return parts.map((part, i) => regex.test(part) ? {part} : part ); } // HTML 문자열에서 태그를 제거하고 텍스트만 추출 function stripHtml(html: string): string { return html.replace(/<[^>]*>/g, ''); } // HTML 문자열 내에서 키워드를 highlight (태그 내부는 건드리지 않음) function highlightHtml(html: string, keyword: string): string { if (!keyword.trim()) { return html; } const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(`(${escaped})`, 'gi'); // 태그 외부의 텍스트에만 적용 return html.replace(/(<[^>]*>)|([^<]+)/g, (_match, tag, text) => { if (tag) { return tag; } return text.replace(regex, '$1'); }); } export default function View() { const [loading, setLoading] = useState(true); const [categories, setCategories] = useState([]); const [items, setItems] = useState([]); const [activeCategory, setActiveCategory] = useState(''); const [keyword, setKeyword] = useState(''); const [searchKeyword, setSearchKeyword] = useState(''); // 초기 데이터 로드 useEffect(() => { Promise.all([ fetchApi('/api/faq/categories'), fetchApi('/api/faq/items', { method: 'POST', body: { Code: '' } }), ]).then(([catRes, itemRes]) => { if (catRes.success && catRes.data) { setCategories(catRes.data.list ?? []); } if (itemRes.success && itemRes.data) { setItems(itemRes.data.list ?? []); } }).finally(() => { setLoading(false); }); }, []); const handleCategoryClick = useCallback((code: string) => { setActiveCategory(code); }, []); const handleSubmit = useCallback((e: React.FormEvent) => { e.preventDefault(); setSearchKeyword(keyword); setActiveCategory(''); }, [keyword]); const handleReset = useCallback(() => { setKeyword(''); setSearchKeyword(''); setActiveCategory(''); }, []); // 필터링된 FAQ 항목 const filteredItems = useMemo(() => { let filtered = items; // 카테고리 필터 if (activeCategory) { filtered = filtered.filter(item => item.categoryCode === activeCategory); } // 키워드 필터 (질문 + 답변 내용에서 검색) if (searchKeyword.trim()) { const lowerKeyword = searchKeyword.toLowerCase(); filtered = filtered.filter(item => { const questionMatch = item.question.toLowerCase().includes(lowerKeyword); const answerMatch = item.answer ? stripHtml(item.answer).toLowerCase().includes(lowerKeyword) : false; return questionMatch || answerMatch; }); } return filtered; }, [items, activeCategory, searchKeyword]); return ( <>

자주 묻는 질문

{loading && } {/* 검색 */}
setKeyword(e.target.value)} placeholder='궁금한 점을 검색해 보세요.' /> {searchKeyword && ( )}
  • {categories.map((cat) => (
  • ))}


{/* 검색 결과 안내 */} {searchKeyword && (

"{searchKeyword}" 검색 결과 {filteredItems.length}

)} {/* FAQ 목록 */} {!loading && (
{filteredItems.length > 0 ? ( filteredItems.map((item) => (
{item.categorySubject} {highlightText(item.question, searchKeyword)}
{item.answer ? (
) : (

답변이 준비 중입니다.

)}
)) ) : (
{searchKeyword ?

검색 결과가 없습니다.

:

등록된 FAQ가 없습니다.

}
)}
)}
); }