-
Notifications
You must be signed in to change notification settings - Fork 2
Feat: 투표 작성 페이지 생성 #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
📦 번들 분석 결과📊 번들 크기 요약
🔍 주요 청크 파일 (크기순)🤖 자동 생성된 번들 분석 리포트 |
⚡ Lighthouse 성능 분석 결과📊 전체 평균 점수
📈 측정 현황
📄 페이지별 상세 분석🏠 커뮤니티 페이지:
|
| 지표 | 점수 |
|---|---|
| 🚀 Performance | 69점 |
| ♿ Accessibility | 78점 |
| ✅ Best Practices | 100점 |
| 🔍 SEO | 100점 |
📊 상세 분석 보기
👥 창업자 페이지: /main/founder
| 지표 | 점수 |
|---|---|
| 🚀 Performance | 75점 |
| ♿ Accessibility | 87점 |
| ✅ Best Practices | 100점 |
| 🔍 SEO | 100점 |
📊 상세 분석 보기
🏡 홈 페이지: /main/home
| 지표 | 점수 |
|---|---|
| 🚀 Performance | 75점 |
| ♿ Accessibility | 91점 |
| ✅ Best Practices | 100점 |
| 🔍 SEO | 100점 |
📊 상세 분석 보기
🗺️ 지도 페이지: /main/maps
| 지표 | 점수 |
|---|---|
| 🚀 Performance | 75점 |
| ♿ Accessibility | 87점 |
| ✅ Best Practices | 100점 |
| 🔍 SEO | 100점 |
📊 상세 분석 보기
👤 프로필 페이지: /main/profile
| 지표 | 점수 |
|---|---|
| 🚀 Performance | 75점 |
| ♿ Accessibility | 88점 |
| ✅ Best Practices | 100점 |
| 🔍 SEO | 100점 |
📊 상세 분석 보기
🔗 전체 상세 분석 결과
📄 측정된 페이지
- /main/community
- /main/founder
- /main/home
- /main/maps
- /main/profile
모든 페이지에서 성능 측정이 완료되었습니다.
🤖 자동 생성된 Lighthouse 성능 리포트
DreamPaste
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생하셨습니다! 업데이트된 백엔드 API 및 타입을 반영하고, 디자인 개선을 진행하면 잘 마무리 될 것 같습니다!
apps/web/src/app/main/community/votesboard/[votesboardId]/edit/page.tsx
Outdated
Show resolved
Hide resolved
| <Select.Content> | ||
| <Select.Item value="1d">1일 후 마감</Select.Item> | ||
| <Select.Item value="3d">3일 후 마감</Select.Item> | ||
| <Select.Item value="7d">7일 후 마감</Select.Item> | ||
| </Select.Content> | ||
| </Select.Portal> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
마감 기간은 2주까지 있었던걸로 기억합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2주 추가하였습니다.
| // 백엔드 요구사항에 맞춰 인덱스 표기법 사용 | ||
| votePostCreateRequest.voteOptions.forEach((value, index) => { | ||
| formData.append(`voteOptions[${index}].content`, value.content); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
인덱스 표기법으로 접근하면 직접 문자열로 넣어줘야 하는군요..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FormData는 객체 구조를 이해하지 못해서 배열이나 객체 형태로 만드려면 문자열로 직접 조립해서 넣어줘야한다고 하네요~
| queryClient.invalidateQueries({ | ||
| queryKey: [`/community/votesboard/${voteboardId}`], | ||
| }); | ||
| queryClient.invalidateQueries({ | ||
| queryKey: ['/community/votesboard'], | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
orval로 생성된 쿼리 키 get 함수는 없을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
있는데 안 쓰고 있네요. 사용하도록 수정했습니다. 감사합니다~!
📌 개요
🗒 상세 설명
1. 투표 글 작성/수정 페이지 및 폼 컴포넌트 추가
/main/community/votesboard도메인에 투표 글 작성/수정 플로우를 붙였습니다.핵심 기술 및 구현사항
새 페이지 추가
apps/web/src/app/main/community/votesboard/new/page.tsxsearchParams.category를 읽어 유효한 카테고리일 경우 초기 카테고리로 전달VoteboardForm initialCategory={category}로 생성 모드 진입apps/web/src/app/main/community/votesboard/[votesboardId]/edit/page.tsxuseParams()로votesboardId추출 후useGetVotePost(voteId)로 상세 데이터 조회VoteboardFormSkeleton, 완료 후에는VoteboardForm voteboardId, initialData렌더공통 폼 컴포넌트
apps/web/src/app/main/community/votesboard/components/VoteboardForm.tsxreact-hook-form + zodResolver기반 폼 상태 관리voteboardId유무로 isEdit 모드 계산필드 구성
<Select>+ControllerInputTextArea1d,3d,7d)useFieldArray+VoteboardOptionField)ImageUploader재사용적용 이미지
2. 투표 폼 스키마 및 마감 기한(duration) 유틸 추가
투표 폼 전용 Zod 스키마와 마감 기한 계산 유틸을 분리했습니다.
핵심 기술 및 구현사항
apps/web/src/app/main/community/votesboard/schema/voteboardSchema.tsVOTE_DURATION_VALUES = ['1d', '3d', '7d'] as constduration필드: 셀렉터에서 선택한 기간을 표현category:CategoryEnum기반 enum으로 제한title,content: 공백만 입력 불가 + 길이 제한voteOptions: 최소 2개 ~ 최대 5개, 각 옵션은 1~50자 + 공백-only 방지images:Blob[]기반, 최대 4장 제한VoteboardFormData타입 exportapps/web/src/utils/voteTime.tsgetDaysFromDuration(duration: VoteDuration): number'1d' -> 1,'3d' -> 3,'7d' -> 7buildEndTimeFromDuration(duration: VoteDuration): stringyyyy-MM-ddTHH:mm:ss로컬 시간 문자열로 포맷useVoteboardMutation에서duration을 받아 실제endTime문자열을 생성해 서버로 전달3. 투표 옵션 필드 컴포넌트 및 수정 모드 옵션 잠금
투표 옵션 필드 UI를 컴포넌트로 분리하고, 수정 모드에서 옵션 편집/추가/삭제를 막았습니다.
핵심 기술 및 구현사항
apps/web/src/app/main/community/votesboard/components/VoteboardForm.tsxuseFieldArray({ name: 'voteOptions' })기반 동적 옵션 관리생성 모드
[{ content: '찬성' }, { content: '반대' }]fields.length < 5일 때만 append)수정 모드
isEdit = !!voteboardIdVoteboardOptionField에editable={!isEdit},canRemove={!isEdit && fields.length > 2}로 내려서 옵션 텍스트/개수 변경 불가apps/web/src/app/main/community/votesboard/components/VoteoptionField.tsxVoteboardOptionField컴포넌트 분리editable플래그로Input.disabled제어canRemove플래그로 삭제 버튼 노출 제어voteOptions.${index}.content를 register적용 이미지
4. 투표 폼 스켈레톤(VoteboardFormSkeleton) 추가
로딩 시 CLS를 줄이기 위해 VoteboardForm과 거의 동일한 레이아웃의 스켈레톤을 추가했습니다.
apps/web/src/app/main/community/votesboard/components/VoteBoardForm.Skeleton.tsx5. 투표 게시글 생성/수정 API 클라이언트(FormData) 정리
기존 Orval에서 생성한 createVotePost는 application/json + JSON body 기준이라
실제 서버에서 요구하는 multipart/form-data + voteOptions[0].content 형식과 맞지 않았습니다.
그래서 FormData를 직접 생성해서 백엔드 스펙에 맞는 필드 구조로 다시 맞춰 주었습니다.
핵심 기술 및 구현사항
생성 플로우
createVotePost는 그대로 두고,apps/web/src/app/main/community/votesboard/new/api/votePostCreate.ts의 커스텀 헬퍼만 사용합니다.voteOptions[${index}].content형식으로 옵션을 넣어 백엔드 스펙에 맞췄습니다.6. 투표 게시글 생성/수정 통합 훅(useVoteboardMutation) 추가
폼에서 API 호출을 깔끔하게 사용할 수 있도록 생성/수정 로직을 하나의 훅에 통합했습니다.
핵심 기술 및 구현사항
apps/web/src/hooks/useVoteboardMutation.tsuseVoteboardMutation(voteboardId?: number)voteboardId유무로 생성 vs 수정 분기생성:
useMutation+ 커스텀createVotePostwrapper 사용성공 시:
/community/votesboard리스트 쿼리 invalidate/main/community/votesboard수정:
useUpdateVotePost(Orval 생성 훅) 사용votesboardId+VotePostUpdateRequest형태로 호출성공 시:
/main/community/votesboard/${voteboardId}공통:
isPending로 버튼 로딩/비활성 제어7. API 모델/타입 스펙 업데이트
Orval 재생성에 따라 투표 관련 모델이 변경되었습니다.
핵심 기술 및 구현사항
카테고리 enum 분리 및 타입 정의
votePostCreateRequestCategory.tsvotePostDetailResponseCategory.tsvotePostSummaryResponseCategory.ts이미지 관련 필드 변경
VotePostCreateRequest.images?: Blob[](기존 imageUrls 제거)VotePostUpdateRequest.images?: Blob[],deleteImageIds?: number[]VotePostDetailResponse.images: ImageInfo[]추가 (기존 imageUrls 제거)요약 응답에 카테고리 추가
VotePostSummaryResponse.category필드 추가🔗 이슈
closes #76
✅ 체크리스트
🧪 테스트 방법
투표 글 작성 플로우 수동 테스트
투표 글 수정 플로우 수동 테스트
마감 기간(duration) 선택에 따른
endTime포맷 확인yyyy-MM-ddTHH:mm:ss형식으로 서버에 전송되는지 네트워크 탭으로 확인이미지 업로드/삭제 동작 확인
📝 추가 노트
후속 작업