✨ Feat: 커뮤니티 게시글 CRUD 구현 및 UI 전반 개선 - #55
Conversation
- 커뮤니티 게시판 요청/응답 타입 추가 - 게시글 CRUD 및 임시저장 API 함수 추가 - 커뮤니티 에러/상태 상수 및 임시저장 스토리지 키 추가 - feature index에서 커뮤니티 도메인 기반 구조 export - 게시글 상세 및 임시저장 게시글용 react-query key 추가 `DoDo-Project#53`
- 커뮤니티 게시판 query/mutation 훅 추가 - 공통 게시글 에디터 폼 상태 및 유효성 검사 추가 - 임시저장 세션 키 유지를 위한 localStorage 헬퍼 추가 - useBoardEditorForm에 게시글 작성/수정/임시저장 에디터 플로우 구현 - feature index에서 커뮤니티 훅 및 폼 유틸리티 export `DoDo-Project#53`
- 작성/수정 플로우에서 공통으로 사용하는 게시글 에디터 폼 UI 추가 - 로딩 및 에러 상태를 포함한 커뮤니티 게시글 작성/수정 페이지 추가 - `/community/new` 및 `/communit`y/`/edit` 보호 라우트 연결 - 에디터 진입 CTA를 포함하도록 커뮤니티 랜딩 페이지 개선 - 기존 카드 기반 페이지 패턴에 맞춰 폼 스타일 유지 `DoDo-Project#53`
- 로딩 및 에러 상태를 포함한 게시글 상세 페이지 추가 - 재사용 가능한 게시글 상세 콘텐츠 UI 및 삭제 확인 다이얼로그 추가 - /community/ 보호 라우트 연결 - 게시글 상세 화면에서 수정 및 삭제 액션 연결 `DoDo-Project#53`
- 프로필 카드와 피드형 섹션을 포함하도록 커뮤니티 랜딩 페이지 재디자인 - 재사용 가능한 커뮤니티 피드 카드 및 프로필 카드 컴포넌트 추가 - 피드 중심 커뮤니티 레이아웃에 맞춰 게시글 상세 화면 스타일 개선 - API 연동 전 댓글 목록 및 댓글 입력 영역 placeholder 추가 - 안정적인 렌더링을 위해 커뮤니티 상세 화면 및 삭제 다이얼로그 문구 정리 `DoDo-Project#53`
- GET /boards API 연결 (useBoardList, 무한 스크롤) - 인기 게시물(likeCount 상위 3개) + 최근 게시물(4열 그리드) 섹션 분리 - CommunityFeedCard 개선: preview, nickname, viewCount, 상대 시간 표시 - 내 활동 페이지 추가 (/community/my): 내 게시글 / 내 댓글 탭 UI (API 미연결) - temp-save API 스펙 반영: boardId 쿼리 파라미터 지원 - BoardListItem, BoardListResponse 타입 추가 - CommunityProfileCard에 내 활동 진입 버튼 추가 `DoDo-Project#53`
- 커뮤니티 전용 공통 레이아웃 컴포넌트 추가 - 홈/내 활동 페이지를 카드형 구조로 통일 - 프로필 사이드 패널 스타일 개선 및 액션 버튼 정리 - 커뮤니티 홈/내 활동 문구 한글 정리 `DoDo-Project#53`
- 커뮤니티 공통 레이아웃을 작성 페이지 스타일로 재정리 - 홈/내 활동/상세 화면을 흰색 카드 중심 구조로 통일 - 게시글 상세를 시안 기반 본문/댓글/입력 흐름으로 재구성 - 작성/수정 화면 한글 문구 및 상태 안내 정비 `DoDo-Project#53`
- 임시저장 요청에 imageFileUrls 배열 필드 호환 추가 - 임시저장 복원 시 imageFileUrls 우선 처리하도록 수정 - 단일 imageFileUrl 응답도 함께 지원해 기존 스펙과 호환 유지 `DoDo-Project#53`
- CommunityFeedCard 내부 깨진 한글 문자열과 아이콘 참조 오류 수정 - 게시물 목록 구분선, 간격, 타이포그래피 조정 - 인기 게시물 TOP 배지 제거로 목록 UX 단순화 - 최근 게시물 스켈레톤 간격을 실제 리스트 레이아웃과 맞춤 `DoDo-Project#53`
- 상세 페이지 상단 헤더와 뒤로가기 버튼 제거 - 게시글 상세 응답의 profileUrl을 작성자 프로필 이미지에 연결 - 제목 크기는 줄이고 본문 크기는 키워 가독성 개선 - 대표 이미지 크기를 축소하고 여러 장 이미지는 갤러리 형태로 노출 - 댓글 안내 문구와 페이지네이션 목업 제거 - 댓글 입력창을 화면 하단에 고정되도록 변경 - 댓글 입력 영역에 현재 사용자 프로필 이미지 연결 - 댓글 등록 버튼을 서비스 흐름에 맞는 형태로 정리 `DoDo-Project#53`
본문: - 커뮤니티 메인 레이아웃을 목록형 피드 중심으로 재구성하고 사이드바 sticky 적용 - 인기 게시물과 최근 게시물 섹션의 간격, 구분선, 썸네일 배치를 UX 기준으로 개선 - 게시글 작성/수정 화면을 커뮤니티 톤에 맞게 단순화하고 액션 영역 재배치 - 임시 저장을 하단 액션 바로 이동하고 이미지 첨부 플로우는 유지 - 임시 저장 시 이미지가 누락되던 문제를 수정하고 다중 이미지 복원 처리 개선 - 게시글 상세 레이아웃을 정리하고 profileUrl 연결 및 다중 이미지 노출 지원 - 현재 사용자 프로필이 반영된 고정형 댓글 입력 목업 추가 - 상세/목록 화면의 한글 카피와 인터랙션 톤을 더 일관되게 정리 - 게시글 수정 후 상세 및 목록 화면에 즉시 반영되도록 react-query 캐시 갱신 처리 - 커뮤니티 관련 변경 파일의 인코딩 깨짐 및 문자열 문제 정리 `DoDo-Project#53`
|
@sooloin is attempting to deploy a commit to the sooloin's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Code Review
This pull request implements the community board feature, adding pages and components for creating, editing, viewing, deleting, and listing community posts, along with temporary draft saving and custom React Query hooks. Feedback on these changes highlights several improvement opportunities: ensuring the board list query is invalidated upon post deletion to prevent cache inconsistency, updating the backend response type to include actual like and comment counts instead of using mock formulas, unifying query keys in useBoardList, removing redundant query invalidations in useUpdateBoard to avoid unnecessary refetches, and adding safety checks for date parsing in CommunityFeedCard to prevent rendering errors.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| onSuccess: (_, variables) => { | ||
| void queryClient.removeQueries({ | ||
| queryKey: queryKeys.boards.detail(variables.boardId), | ||
| }); | ||
| }, |
There was a problem hiding this comment.
게시글을 삭제한 후 목록 페이지(/community)로 이동하지만, useDeleteBoard에서 전체 게시글 목록 쿼리(['boards'])를 무효화(invalidate)하지 않고 있습니다. 이로 인해 삭제된 게시글이 목록에 계속 남아있는 캐시 불일치 현상이 발생할 수 있으므로, 성공 시 목록 쿼리를 무효화해주어야 합니다.
onSuccess: (_, variables) => {
void queryClient.removeQueries({
queryKey: queryKeys.boards.detail(variables.boardId),
});
void queryClient.invalidateQueries({
queryKey: ['boards'],
});
},| const likeCount = Math.max(12, Math.round(board.viewCount * 1.7)); | ||
| const commentCount = Math.max(MOCK_COMMENTS.length, Math.round(board.viewCount / 3)); |
| boards: { | ||
| list: (params?: { page?: number; size?: number }) => | ||
| ['boards', 'list', params?.page ?? 0, params?.size ?? 12] as const, | ||
| detail: (boardId: number) => ['boards', boardId, 'detail'] as const, | ||
| tempSaved: (sessionKey: string) => ['boards', 'temp-save', sessionKey] as const, | ||
| }, |
There was a problem hiding this comment.
정의된 queryKeys.boards.list가 useBoardList에서 사용되지 않고 있으며, useBoardList에서는 ['boards', 'list-infinite']라는 별도의 키를 하드코딩하여 사용하고 있습니다. 쿼리 키의 일관된 관리를 위해 queryKeys에 무한 스크롤 목록용 키를 추가하고 이를 useBoardList에서 사용하도록 수정하는 것이 좋습니다.
boards: {
list: (params?: { page?: number; size?: number }) =>
['boards', 'list', params?.page ?? 0, params?.size ?? 12] as const,
listInfinite: () => ['boards', 'list-infinite'] as const,
detail: (boardId: number) => ['boards', boardId, 'detail'] as const,
tempSaved: (sessionKey: string) => ['boards', 'temp-save', sessionKey] as const,
},| void queryClient.invalidateQueries({ | ||
| queryKey: queryKeys.boards.detail(variables.boardId), | ||
| }); | ||
|
|
||
| void queryClient.invalidateQueries({ | ||
| queryKey: ['boards'], | ||
| }); |
There was a problem hiding this comment.
setQueryData를 통해 상세 데이터를 수동으로 즉시 업데이트하고 있음에도 불구하고, 바로 아래에서 queryKeys.boards.detail과 ['boards'] 전체를 무효화하고 있습니다. 이로 인해 setQueryData로 업데이트한 캐시가 무시되고 즉시 불필요한 네트워크 요청(refetch)이 발생하게 됩니다. 상세 쿼리 무효화를 제거하고, 목록 쿼리만 특정하여 무효화하는 것이 효율적입니다.
| void queryClient.invalidateQueries({ | |
| queryKey: queryKeys.boards.detail(variables.boardId), | |
| }); | |
| void queryClient.invalidateQueries({ | |
| queryKey: ['boards'], | |
| }); | |
| void queryClient.invalidateQueries({ | |
| queryKey: ['boards', 'list-infinite'], | |
| }); |
| const date = new Date(iso).getTime(); | ||
| const diff = now - date; |
There was a problem hiding this comment.
iso 문자열이 올바르지 않거나 브라우저에서 파싱할 수 없는 형식인 경우, new Date(iso).getTime()이 NaN을 반환하여 최종적으로 화면에 'Invalid Date'가 렌더링될 수 있습니다. 안전한 렌더링을 위해 파싱 실패 시 예외 처리를 추가하는 것이 좋습니다.
| const date = new Date(iso).getTime(); | |
| const diff = now - date; | |
| const date = new Date(iso).getTime(); | |
| if (Number.isNaN(date)) { | |
| return ''; | |
| } | |
| const diff = now - date; |
- 커뮤니티 게시물 목록 간격을 넓히고 인셋 구분선 방식으로 리스트 가독성 개선 - 무한 스크롤 게시글 목록 쿼리 키를 queryKeys 기준으로 통일 - 게시글 삭제 후 목록 캐시 무효화, 수정 후 무한 목록 갱신 처리 추가 - 게시글 수정 시 상세 캐시를 setQueryData로 즉시 동기화 - 상대 시간 포맷에서 잘못된 날짜 입력에 대한 안전 처리 추가 - 게시글 상세 응답의 likeCount, commentCount를 우선 사용하도록 보완 `DoDo-Project#53`
📄 작업 내용 (Description)
🔗 관련 이슈 (Related Issues)
✅ 체크리스트 (Checklist)
Style)Test)📸 스크린샷 (Screenshots)
💬 기타 사항 (Etc)
확인 부탁드립니다.