Skip to content

Conversation

@DreamPaste
Copy link
Member

📌 개요

투표 게시판 리스트 페이지 구현 및 관련 컴포넌트 제작

  • 투표 리스트 페이지 (SSR + CSR) 구현
  • 투표 카드 컴포넌트 구현
  • 투표 상태 칩 컴포넌트 구현
  • 투표 마감 기한 표시 유틸 함수 추가
  • FloatingButton 및 FloatingCategoryMenu 리팩토링
  • 기본 dev https 기반으로 변경

🗒 상세 설명

1. 투표 리스트 페이지 구현

투표 게시판의 메인 리스트 페이지를 SSR과 CSR로 구현했습니다.

핵심 기술 및 구현사항

  • SSR 페이지 구현 (page.tsx)
    • 서버 컴포넌트에서 초기 데이터 prefetch
    • HydrationBoundary를 통한 클라이언트로 상태 전달
    • SEO 최적화 및 초기 로딩 성능 개선
  • CSR 페이지 구현 (ClientPage.tsx)
    • TanStack Query의 useInfiniteQuery를 사용한 무한 스크롤
    • 투표 상태 필터링 (전체/진행중/완료)
    • 정렬 옵션 (최신순, 인기순 등)
    • 커스텀 CommunityPostList 컴포넌트를 통한 가상 스크롤 및 스크롤 위치 저장

사용 예시

// SSR 페이지에서 데이터 prefetch
export default async function VotesboardPage() {
  const queryClient = new QueryClient();

  await queryClient.prefetchInfiniteQuery({
    queryKey: getGetVotePostsByCursorQueryKey({ sort: 'LATEST' }),
    queryFn: ({ pageParam, signal }) =>
      getVotePostsByCursor(
        { sort: 'LATEST', cursor: pageParam, size: 10 },
        signal,
      ),
    initialPageParam: undefined,
    pages: 1,
  });

  return (
    <HydrationBoundary state={dehydrate(queryClient)}>
      <VotesboardClientPage />
    </HydrationBoundary>
  );
}

2. 투표 카드 컴포넌트 구현

투표 게시글을 표시하는 카드 컴포넌트를 구현했습니다.

핵심 기술 및 구현사항

  • VoteBoardCard 컴포넌트 (VoteBoardCard.tsx)
    • 카테고리 칩 및 투표 상태 칩 표시
    • 썸네일 이미지 지원
    • 투표 마감 시간 표시 (사용자 친화적 형식)
    • 투표 참여 수, 댓글 수 표시
    • 투표 참여 여부에 따른 아이콘 색상 변경
    • Next.js Image 컴포넌트를 사용한 이미지 최적화

사용 예시

<VoteBoardCard post={votePostSummary} />

3. 투표 상태 칩 구현

투표 상태를 시각적으로 표시하는 칩 컴포넌트를 구현했습니다.

핵심 기술 및 구현사항

  • VoteStatusChip 컴포넌트 (VoteStatusChip.tsx)
    • 4가지 상태 지원:
      • 진행 중: 마감까지 24시간 이상 남음 (파란색)
      • 마감 임박: 마감까지 24시간 이내 (주황색)
      • 마감: 마감 시간이 지남 (회색)
      • 완료: 투표가 완료됨 (녹색)
    • voteStatusendTime을 기반으로 자동 상태 계산
    • CategoryChip과 동일한 디자인 시스템 적용

사용 예시

<VoteStatusChip voteStatus={post.voteStatus} endTime={post.endTime} />

4. 투표 마감 기한 표시 유틸 함수

사용자 친화적인 마감 시간 표시를 위한 유틸 함수를 구현했습니다.

핵심 기술 및 구현사항

  • formatVoteDeadline 함수 (vote-deadline.ts)
    • date-fns를 활용한 시간 계산
    • 규칙:
      • 7일 초과: "25.12.25 마감" (날짜 표시)
      • 7일 이내: "7일 후 마감", "6일 후 마감"
      • 하루 이내: "12시간 후 마감", "20시간 후 마감"
      • 1시간 이내: "30분 후 마감", "45분 후 마감"
      • 마감된 경우: "마감됨"

사용 예시

import { formatVoteDeadline } from '@/utils/vote-deadline';

const deadlineText = formatVoteDeadline(post.endTime);
// 출력 예시: "5일 후 마감", "12시간 후 마감", "25.12.25 마감" 등

5. FloatingButton 및 FloatingCategoryMenu 리팩토링

플로팅 버튼과 카테고리 메뉴의 관심사를 분리하고 재사용성을 개선했습니다.

핵심 기술 및 구현사항

  • FloatingButton 리팩토링 (FloatingButton.tsx)

    • 오버레이 제어 로직을 부모 컴포넌트로 이동
    • 단순히 onClick 핸들러만 받아서 실행
    • 버튼 UI에만 집중
  • FloatingCategoryMenu 리팩토링 (FloatingCategoryMenu.tsx)

    • useParams로 경로를 추론하던 로직을 route prop으로 변경
    • 명시적인 라우팅 제어 가능
    • 자유게시판과 투표게시판 모두에서 재사용 가능

사용 예시

// 부모 컴포넌트에서 오버레이 제어
const { open } = useOverlay();

const handleFloatingButtonClick = () => {
  open(
    ({ close }) => (
      <FloatingCategoryMenu
        route="votesboard"
        categories={CATEGORIES}
        onClose={() => close(null, { duration: 200 })}
      />
    ),
    { backdrop: true, closeOnBackdrop: true },
  );
};

<FloatingButton onClick={handleFloatingButtonClick} />;

6. Next.js 이미지 설정 개선

S3 버킷의 모든 경로에서 이미지를 로드할 수 있도록 설정을 개선했습니다.

핵심 기술 및 구현사항

  • next.config.js 수정 (next.config.js)
    • S3 버킷의 pathname/freeboard/**에서 /**로 변경
    • 투표게시판 이미지 로드 가능하도록 개선
    • 보안을 유지하면서 모든 게시판 타입 지원

📸 스크린샷

  1. 투표 리스트 페이지

    • 투표 상태 탭 (전체/진행중/완료)
    • 정렬 헤더 (게시글 수, 정렬 옵션)
    • 무한 스크롤 적용된 투표 카드 리스트
    • 플로팅 버튼 (카테고리별 글쓰기)
  2. 투표 카드

    • 카테고리 칩 + 투표 상태 칩
    • 제목 및 내용 미리보기
    • 썸네일 이미지
    • 마감 시간 표시
    • 투표 참여 수, 댓글 수
  3. 투표 상태 칩

    • 진행 중 (파란색)
    • 마감 임박 (주황색)
    • 마감 (회색)
    • 완료 (녹색)
image

🔗 이슈

closes #97


✅ 체크리스트

  • 코드가 스타일 가이드를 따릅니다
  • 자체 코드 리뷰를 완료했습니다
  • 복잡/핵심 로직에 주석을 추가했습니다
  • 관심사 분리를 확인했습니다
  • 잠재적 사이드이펙트를 점검했습니다
  • Vercel Preview로 테스트를 완료했습니다

@DreamPaste DreamPaste added Feat 💡 새로운 기능을 구현하고 추가합니다! 휘건 labels Dec 11, 2025
@linear
Copy link

linear bot commented Dec 11, 2025

@github-actions
Copy link

@github-actions
Copy link

📦 번들 분석 결과

📊 번들 크기 요약

항목
📦 전체 번들 크기 3.7M
📄 JavaScript 크기 1.6M
🗂️ JavaScript 파일 수 64개

🔍 주요 청크 파일 (크기순)

fe98bb7c-75056deddb8826d9.js - 169K
framework-69e0f7d37422957b.js - 137K
main-448b0e12e0910adc.js - 130K
8047-6c88f11af4ffbcae.js - 122K
7147-b4b28a8f9d4487de.js - 122K
polyfills-42372ed130431b0a.js - 110K
2877-3c61ab12293ea4d2.js - 90K
1214-7d146fc36196a70c.js - 75K
page-e1e5d77f1e93ef46.js - 30K
2906-961db7df3fbbb1e4.js - 28K

🤖 자동 생성된 번들 분석 리포트

@github-actions
Copy link

⚡ Lighthouse 성능 분석 결과

📊 전체 평균 점수

지표 점수
🚀 Performance 73점
♿ Accessibility 87점
✅ Best Practices 100점
🔍 SEO 100점

📈 측정 현황

  • 측정 성공: 15/16 페이지
  • 상태: success

📄 페이지별 상세 분석

🏠 커뮤니티 페이지: /main/community

지표 점수
🚀 Performance 67점
♿ Accessibility 80점
✅ 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점

📊 상세 분석 보기

🔗 전체 상세 분석 결과

📊 전체 상세 Lighthouse 분석 결과 보기

📄 측정된 페이지

  • /main/community
  • /main/founder
  • /main/home
  • /main/maps
  • /main/profile

모든 페이지에서 성능 측정이 완료되었습니다.


🤖 자동 생성된 Lighthouse 성능 리포트

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

이 PR은 투표 게시판 리스트 페이지를 구현하고 관련 컴포넌트들을 추가하며, 기존 FloatingButton 컴포넌트의 재사용성을 개선하는 작업입니다.

주요 변경사항:

  • SSR과 CSR을 결합한 투표 게시판 리스트 페이지 구현 (무한 스크롤, 필터링, 정렬 기능 포함)
  • 투표 카드 컴포넌트와 투표 상태 칩 컴포넌트 신규 구현
  • 투표 마감 시간을 사용자 친화적으로 표시하는 유틸 함수 추가
  • FloatingButton 및 FloatingCategoryMenu 리팩토링을 통한 관심사 분리 및 재사용성 향상
  • date-fns 라이브러리 추가 및 개발 환경 HTTPS 기본 설정

Reviewed changes

Copilot reviewed 14 out of 19 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pnpm-lock.yaml date-fns v4.1.0 의존성 추가
package.json dev 스크립트를 HTTPS 기반으로 변경
apps/web/package.json date-fns 의존성 추가 및 dev 스크립트 HTTPS 기본 설정
apps/web/src/utils/vote-deadline.ts 투표 마감 시간을 사용자 친화적인 형식으로 변환하는 유틸 함수 구현
apps/web/src/types/options.types.ts 조회순(VIEW) 정렬 옵션 추가
apps/web/src/generated/api/models/* API 응답 모델에 contentPreview, hasVoted, isAuthorized 필드 추가
apps/web/src/components/chips/VoteStatusChip.tsx 투표 상태를 시각적으로 표시하는 칩 컴포넌트 구현 (진행중/마감임박/마감/완료)
apps/web/src/components/buttons/FloatingCategoryMenu.tsx useParams 대신 route prop을 사용하도록 리팩토링하여 재사용성 향상
apps/web/src/components/buttons/FloatingButton.tsx 오버레이 제어 로직을 부모 컴포넌트로 이동하여 단일 책임 원칙 준수
apps/web/src/app/main/community/votesboard/page.tsx SSR 서버 컴포넌트로 초기 데이터 prefetch 구현
apps/web/src/app/main/community/votesboard/components/VoteBoardCard.tsx 투표 게시글 카드 컴포넌트 구현 (썸네일, 상태, 마감시간, 투표수, 댓글수 표시)
apps/web/src/app/main/community/votesboard/ClientPage.tsx 무한 스크롤, 필터링, 정렬 기능이 있는 투표 게시판 클라이언트 페이지 구현
apps/web/src/app/main/community/freeboard/ClientPage.tsx FloatingButton 리팩토링 적용
apps/web/src/app/main/community/constants/votesOptions.tsx 투표 상태 필터 상수 정의 (진행중/완료)
apps/web/src/app/main/community/constants/sortOptions.ts 조회순 정렬 옵션 추가
apps/web/next.config.js S3 이미지 경로 설정을 모든 게시판 타입 지원하도록 확장
.gitignore monorepo-docs 디렉토리 무시 설정 추가
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

@@ -0,0 +1,104 @@
// src/components/CommunityCard.tsx
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

파일 경로 주석이 잘못되었습니다. 이 파일은 src/components/CommunityCard.tsx가 아니라 src/app/main/community/votesboard/components/VoteBoardCard.tsx입니다. 파일 위치를 정확히 반영하도록 주석을 수정해야 합니다.

Suggested change
// src/components/CommunityCard.tsx
// src/app/main/community/votesboard/components/VoteBoardCard.tsx

Copilot uses AI. Check for mistakes.
import { CategoryChip } from '@/components/chips/CategoryChip';
import { Category } from '../../constants/categories';
import { LaptopMinimalCheck, MessageSquareMore } from 'lucide-react';
//import { useRouter } from 'next/navigation';
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석 처리된 import와 코드를 제거해야 합니다. 추후 구현 예정인 기능이라면, 구현 시점에 추가하는 것이 코드 가독성과 유지보수 측면에서 더 좋습니다.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +38
console.warn(
'추후 투표 상세 페이지로 이동할 예정입니다. => postId:',
postId,
);
//router.push(`/main/community/votesboard/${postId}`);
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석 처리된 코드를 제거해야 합니다. 추후 구현 예정인 기능이라면, 구현 시점에 추가하는 것이 더 좋습니다. 또한 console.warn 대신 실제 동작을 구현하거나, 임시 구현이라면 주석으로만 남기는 것을 권장합니다.

Suggested change
console.warn(
'추후 투표 상세 페이지로 이동할 예정입니다. => postId:',
postId,
);
//router.push(`/main/community/votesboard/${postId}`);
// TODO: 투표 상세 페이지로 이동 기능은 추후 구현 예정입니다.

Copilot uses AI. Check for mistakes.
>
<LaptopMinimalCheck
className={cn(
'w-4 h-4 ',
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스명에 불필요한 공백이 있습니다. 'w-4 h-4 ' 대신 'w-4 h-4'로 수정해야 합니다.

Suggested change
'w-4 h-4 ',
'w-4 h-4',

Copilot uses AI. Check for mistakes.
renderItem={(post) => <VoteBoardCard post={post} />}
/>

{/* TODO: FloatingButton 추가 */}
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO 주석이 이미 완료된 작업을 가리키고 있습니다. FloatingButton이 이미 115번 라인에 구현되어 있으므로 이 TODO 주석을 제거해야 합니다.

Suggested change
{/* TODO: FloatingButton 추가 */}

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +51
return `${minutesLeft}분 후 마감`;
}
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마감까지 1분 미만일 경우 처리 로직이 누락되었습니다. minutesLeft가 0이면 "0분 후 마감"이 표시되는데, 이 경우 "곧 마감" 또는 "1분 미만" 같은 더 명확한 메시지를 표시하는 것이 좋습니다.

Suggested change
return `${minutesLeft}분 후 마감`;
}
if (minutesLeft < 1) {
return '곧 마감';
}
return `${minutesLeft}분 후 마감`;

Copilot uses AI. Check for mistakes.
return 'closed';
}

// 24시간 이내 마감 임박 (마감 임박)
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주석이 중복되어 있습니다. "마감 임박" 텍스트가 불필요하게 반복되고 있으므로 "24시간 이내 마감 임박"만 남기는 것이 좋습니다.

Suggested change
// 24시간 이내 마감 임박 (마감 임박)
// 24시간 이내 마감 임박

Copilot uses AI. Check for mistakes.
Copy link
Member

@youdaeng2 youdaeng2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

투표게시판 잘 구현해주셨네요 플로팅 버튼도 수정해주신 방향이 깔끔한 것 같습니다. 다음에 저도 참고하겠습니다! 코파일럿이 남겨준 주석부분, 마감기한 유틸 부분만 살짝 수정해주시면 완성도가 올라갈 것 같습니다. 고생하셨습니다!

@DreamPaste DreamPaste merged commit f16354d into dev Dec 23, 2025
10 checks passed
@DreamPaste DreamPaste deleted the feat/SOS-52-voteslist-page branch December 23, 2025 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feat 💡 새로운 기능을 구현하고 추가합니다! 휘건

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 투표 리스트 페이지 구현

3 participants