@@ -6,7 +6,7 @@ import config from "../../config";
66export default function Community ( ) {
77 const navigate = useNavigate ( ) ;
88 const tabs = [ "전체" , "미해결" , "해결됨" ] ;
9- const filters = [ "최신순" , "정확도순" , "답변많은순 ", "좋아요순" ] ;
9+ const filters = [ "최신순" , "오래된순 " , "좋아요순" ] ;
1010
1111 // ✅ 서버 데이터 상태
1212 const [ posts , setPosts ] = useState ( [ ] ) ;
@@ -17,6 +17,7 @@ export default function Community() {
1717 const [ keyword , setKeyword ] = useState ( "" ) ;
1818 const [ tagKeyword , setTagKeyword ] = useState ( [ ] ) ;
1919 const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
20+ const [ lastKnownPage , setLastKnownPage ] = useState ( 1 ) ;
2021
2122 const PAGE_SIZE = 10 ;
2223
@@ -65,24 +66,34 @@ export default function Community() {
6566 } ) ;
6667
6768 // 🔄 정렬된 목록을 UI 필드로 매핑
68- const mapped = sorted . map ( ( p ) => ( {
69- id : p . id ,
70- status : p . status || "" ,
71- title : p . title ,
72- summary : ( p . content || "" ) . replace ( / < [ ^ > ] + > / g, "" ) . slice ( 0 , 120 ) ,
73- tags : p . tags || [ ] ,
74- author : p . writer || "익명" ,
75- date : p . createdAt ? new Date ( p . createdAt ) . toLocaleString ( ) : "" ,
76- likes : p . likeCount ?? 0 ,
77- comments : p . commentCount ?? 0 ,
78- } ) ) ;
69+ const mapped = sorted . map ( ( p ) => {
70+ let createdAtMs = null ;
71+ if ( p . createdAt ) {
72+ const parsed = new Date ( p . createdAt ) . getTime ( ) ;
73+ createdAtMs = Number . isFinite ( parsed ) ? parsed : null ;
74+ }
75+
76+ return {
77+ id : p . id ,
78+ status : p . status || "" ,
79+ title : p . title ,
80+ summary : ( p . content || "" ) . replace ( / < [ ^ > ] + > / g, "" ) . slice ( 0 , 120 ) ,
81+ tags : p . tags || [ ] ,
82+ author : p . writer || "익명" ,
83+ date : p . createdAt ? new Date ( p . createdAt ) . toLocaleString ( ) : "" ,
84+ createdAtMs,
85+ likes : p . likeCount ?? 0 ,
86+ comments : p . commentCount ?? 0 ,
87+ } ;
88+ } ) ;
7989
8090 setPosts ( mapped ) ;
8191 setKeyword ( "" ) ;
8292 setTagKeyword ( [ ] ) ;
8393 setSearchInput ( "" ) ;
8494 setTagInput ( "" ) ;
8595 setCurrentPage ( 1 ) ;
96+ setLastKnownPage ( 1 ) ;
8697 } catch ( e ) {
8798 if ( ! ignore ) setError ( e . message || "알 수 없는 오류" ) ;
8899 } finally {
@@ -101,7 +112,7 @@ export default function Community() {
101112 return posts ;
102113 }
103114
104- const lowerKeyword = keyword . toLowerCase ( ) ;
115+ const lowerKeyword = keyword . toLowerCase ( ) ;
105116
106117 return posts . filter ( ( post ) => {
107118 const matchesKeyword = lowerKeyword
@@ -121,6 +132,46 @@ export default function Community() {
121132 } ) ;
122133 } , [ keyword , tagKeyword , posts ] ) ;
123134
135+ const [ activeFilter , setActiveFilter ] = useState ( filters [ 0 ] ) ;
136+
137+ const sortedPosts = useMemo ( ( ) => {
138+ const sorted = [ ...filteredPosts ] ;
139+ const ensureNumber = ( value , fallback = null ) => ( Number . isFinite ( value ) ? value : fallback ) ;
140+
141+ const latest = ( a , b ) => {
142+ const aTime = ensureNumber ( a . createdAtMs , null ) ;
143+ const bTime = ensureNumber ( b . createdAtMs , null ) ;
144+ if ( aTime !== null && bTime !== null && aTime !== bTime ) return bTime - aTime ;
145+ if ( bTime !== null ) return 1 ;
146+ if ( aTime !== null ) return - 1 ;
147+ return ( b . id ?? 0 ) - ( a . id ?? 0 ) ;
148+ } ;
149+
150+ const oldest = ( a , b ) => {
151+ const aTime = ensureNumber ( a . createdAtMs , null ) ;
152+ const bTime = ensureNumber ( b . createdAtMs , null ) ;
153+ if ( aTime !== null && bTime !== null && aTime !== bTime ) return aTime - bTime ;
154+ if ( aTime !== null ) return - 1 ;
155+ if ( bTime !== null ) return 1 ;
156+ return ( a . id ?? 0 ) - ( b . id ?? 0 ) ;
157+ } ;
158+ const byLikes = ( a , b ) => ( b . likes ?? 0 ) - ( a . likes ?? 0 ) ;
159+
160+ switch ( activeFilter ) {
161+ case "오래된순" :
162+ sorted . sort ( oldest ) ;
163+ break ;
164+ case "좋아요순" :
165+ sorted . sort ( byLikes ) ;
166+ break ;
167+ case "최신순" :
168+ default :
169+ sorted . sort ( latest ) ;
170+ break ;
171+ }
172+ return sorted ;
173+ } , [ filteredPosts , activeFilter ] ) ;
174+
124175 const handleSearchSubmit = ( event ) => {
125176 event ?. preventDefault ?. ( ) ;
126177 const trimmedKeyword = searchInput . trim ( ) ;
@@ -132,6 +183,7 @@ export default function Community() {
132183 setKeyword ( trimmedKeyword ) ;
133184 setTagKeyword ( parsedTags ) ;
134185 setCurrentPage ( 1 ) ;
186+ setLastKnownPage ( 1 ) ;
135187 } ;
136188
137189 const handleReset = ( ) => {
@@ -140,6 +192,7 @@ export default function Community() {
140192 setKeyword ( "" ) ;
141193 setTagKeyword ( [ ] ) ;
142194 setCurrentPage ( 1 ) ;
195+ setLastKnownPage ( 1 ) ;
143196 } ;
144197
145198 const totalPages = useMemo ( ( ) => {
@@ -150,7 +203,10 @@ export default function Community() {
150203 useEffect ( ( ) => {
151204 if ( currentPage > totalPages ) {
152205 setCurrentPage ( totalPages ) ;
206+ setLastKnownPage ( totalPages ) ;
207+ return ;
153208 }
209+ setLastKnownPage ( currentPage ) ;
154210 } , [ currentPage , totalPages ] ) ;
155211
156212 const pageNumbers = useMemo ( ( ) => (
@@ -160,8 +216,8 @@ export default function Community() {
160216 const paginatedPosts = useMemo ( ( ) => {
161217 const start = ( currentPage - 1 ) * PAGE_SIZE ;
162218 const end = start + PAGE_SIZE ;
163- return filteredPosts . slice ( start , end ) ;
164- } , [ filteredPosts , currentPage ] ) ;
219+ return sortedPosts . slice ( start , end ) ;
220+ } , [ sortedPosts , currentPage ] ) ;
165221
166222 const jumpBy = 5 ;
167223 const goToPage = ( page ) => {
@@ -244,8 +300,22 @@ export default function Community() {
244300
245301 < div className = "filter-area" >
246302 < div className = "filter-bar" >
247- { filters . map ( ( filter , i ) => (
248- < button key = { i } className = { i === 0 ? "active" : "" } > { filter } </ button >
303+ { filters . map ( ( filter ) => (
304+ < button
305+ key = { filter }
306+ type = "button"
307+ className = { `filter-chip${ filter === activeFilter ? " active" : "" } ` }
308+ onClick = { ( ) => {
309+ setActiveFilter ( filter ) ;
310+ setCurrentPage ( ( prev ) => {
311+ const next = lastKnownPage ;
312+ if ( prev !== next ) return next ;
313+ return prev ;
314+ } ) ;
315+ } }
316+ >
317+ { filter }
318+ </ button >
249319 ) ) }
250320 </ div >
251321 < button className = "write-btn" onClick = { ( ) => navigate ( "/community/write" ) } >
0 commit comments