Skip to content

[feature] 동아리 상세페이지 디자인 개편에 따른 최종 구현 통합 및 API 연동#972

Merged
oesnuj merged 13 commits intodevelop-fefrom
feature/#968-club-detail-integrate-api-MOA-456
Dec 26, 2025
Merged

[feature] 동아리 상세페이지 디자인 개편에 따른 최종 구현 통합 및 API 연동#972
oesnuj merged 13 commits intodevelop-fefrom
feature/#968-club-detail-integrate-api-MOA-456

Conversation

@oesnuj
Copy link
Member

@oesnuj oesnuj commented Dec 23, 2025

#️⃣연관된 이슈

#968, MOA-456

📝작업 내용

1. 기존 코드 완전 삭제 후 신규 코드로 교체

  • 이전 버전 ClubDetailPage 코드 완전 삭제
  • clubDetailPage2(개편 작업 폴더) → ClubDetailPage로 승격
  • 라우팅: /club2/:clubId (테스트용) → /club/:clubId (정식)

2. Mock 데이터 제거 및 실제 API 연동

  • mockClubApi 삭제
  • useGetClubDetail hook으로 실제 데이터 사용
  • ClubDetail 타입 구체화: description: stringdescription: DetailedDescription
  • ClubFeed props 이름 통일: photosfeed

3. 기존 구현된 컴포넌트들을 페이지에 통합

  • ClubIntroContent: 동아리 소개 탭에 통합 (플레이스홀더 텍스트 제거)
  • ClubFeed: 활동사진 탭에 통합 (플레이스홀더 텍스트 제거)
  • ClubProfileCard: 좌측 프로필 카드로 배치

4. 세세한 디자인 재작업

  • 탭 버튼: 고정 너비(167px) 적용, padding-bottom(4px) 추가
  • 탭 색상: colors.base.black → colors.gray[800]로 통일
  • 반응형 정렬: 태블릿(좌측 20px 여백), 모바일(중앙 정렬)
  • ContentWrapper: 태블릿에서 gap: 0 적용
  • TabContent: padding 제거하여 각 컴포넌트가 자체 여백 관리

5. ClubProfileCard 수정

  • logo, cover Props optional 처리
  • 기본 이미지 에셋(DefaultLogo, DefaultCover) 추가 및 자동 적용
  • SNS URL 표시 형식 변경: 전체 URL → @username
  • 이미지 데이터 없을 때 404 오류 방지

중점적으로 리뷰받고 싶은 부분

  • ClubIntroContent 조건부 렌더링 구조
  • SNS username 추출 로직이 모든 플랫폼에서 안전한지

논의하고 싶은 부분

  • 기본 커버 이미지의 비율 맞추기

🫡 참고사항

  • 기존 ClubDetailPage는 완전히 삭제되고 clubDetailPage2가 정식 버전으로 승격됨

Summary by CodeRabbit

  • Refactor

    • 동아리 상세 페이지를 프로필(왼쪽) + 콘텐츠(오른쪽) 2단 레이아웃으로 재구성했습니다.
    • 페이지 컴포넌트 구조를 단순화하고 탭 전환 흐름을 개선했습니다.
  • New Features

    • 탭으로 "소개글"과 "활동사진"을 명확히 전환할 수 있도록 상태 기반 탭 인터페이스를 추가했습니다.
    • 사진 보기 모달에 현재 이미지 위치(예: 3/10) 표시를 도입했습니다.
    • 커버 이미지에 뒤로가기 버튼(태블릿 이상)을 추가하여 탐색 편의성을 향상했습니다.
  • Style

    • 탭 버튼 크기·타이포·색상·간격을 조정해 시각적 계층과 반응형 표시를 개선했습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

- 이전 ClubDetailPage 및 모든 하위 컴포넌트 삭제
- BackNavigationBar, ClubApplyButton, ClubCard 등 구 컴포넌트 제거
- ClubProfile, InfoBox, IntroduceBox, PhotoList 등 레거시 UI 제거
- 페이지 라우트 정리
- 기존 /club2/:clubId 라우트를 /club/:clubId로 변경
- clubDetailPage2 import 제거 및 ClubDetailPage로 통합
- 기존: mockClubApi 사용
- 변경: useGetClubDetail hook의 실제 clubDetail 데이터 사용
- ClubIntroContent 컴포넌트로 플레이스홀더 텍스트 대체
- ClubFeed 컴포넌트로 활동사진 탭 구현
- logo, cover Props를 optional로 변경하여 데이터 없을 때 대응
- 기본 로고/커버 이미지 에셋 추가 및 import
- 이미지 없을 때 하드코딩 경로 대신 실제 에셋 자동 적용
- SNS URL 표시 형식 간소화: 전체 URL → @username
- 이미지 데이터 누락 시 404 오류 방지
- 기존: 가변 너비, padding 기반 레이아웃
- 변경: 고정 너비(167px) + padding-bottom(4px) 구조
- 색상: colors.base.black → colors.gray[800]로 통일
- 반응형 개선: 태블릿(좌측 20px), 모바일(중앙 정렬)
- TabContent padding 제거하여 컴포넌트 자체 여백 관리
- 태블릿 ContentWrapper gap 조정으로 레이아웃 개선
- Props 이름: photos → feed (ClubDetail.feeds에 맞춤)
- import 경로: clubDetailPage2 → ClubDetailPage로 수정
- 함수 선언 방식을 export default로 통일
- description 타입 변경: string → DetailedDescription
- DetailedDescription 인터페이스로 상세 설명 구조 정의
  (introDescription, activityDescription, awards, idealCandidate, benefits, faqs)
@oesnuj oesnuj requested a review from seongwon030 December 23, 2025 17:09
@oesnuj oesnuj self-assigned this Dec 23, 2025
@oesnuj oesnuj added ✨ Feature 기능 개발 🎨 Design 마크업 & 스타일링 💻 FE Frontend labels Dec 23, 2025
@vercel
Copy link

vercel bot commented Dec 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
moadong Ready Ready Preview, Comment Dec 25, 2025 5:26pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

동아리 상세페이지 레이아웃을 두 칼럼 구조로 재구성하고 다수의 기존 하위 컴포넌트를 제거 및 교체했습니다. 로컬 탭 상태와 Mixpanel 추적을 추가하고, ClubDetail.description 타입을 문자열에서 DetailedDescription으로 변경했습니다.

Changes

코호트 / 파일(s) 변경 요약
페이지 엔트리 / 라우팅
frontend/src/App.tsx, frontend/src/pages/clubDetailPage2/ClubDetailPage2.tsx
ClubDetailPage2 임포트 및 /club2/:clubId 라우트 제거; 해당 페이지 컴포넌트 파일 삭제.
메인 페이지 재구성
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx, frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
페이지 구조를 두 칼럼으로 재구성하고 탭 상태(activeTab)와 탭 클릭 추적(클럽 소개/피드 이벤트) 추가. 기존 모바일 백 네비게이션/헤더 로직 일부 변경. 스타일(TabList/TabButton) 조정.
신규 프로필 카드
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx, .../ClubProfileCard.styles.ts
새로운 ClubProfileCard 도입: 커버/로고 기본값, 뒤로가기 네비게이션(탭렛에서), SNS 핸들 추출 로직 등. 스타일에 BackButton, CoverImageWrapper 추가 및 컨테이너 반응형 조정.
콘텐츠/피드 컴포넌트 변경
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx, .../ClubIntroContent.styles.ts, .../ClubFeed/ClubFeed.tsx
ClubDetailContent → ClubIntroContent로 명칭/props 변경(introDescription 제거), 패딩/렌더링 조건 수정. ClubFeed prop명 photosfeed로 변경 및 default export로 전환.
타입 변경
frontend/src/types/club.ts
ClubDetail.description 타입을 stringDetailedDescription으로 변경하고 DetailedDescription, Award, IdealCandidate, FAQ 타입 추가.
삭제: 모바일 백 네비게이션 / 헤더 관련
frontend/src/pages/ClubDetailPage/components/BackNavigationBar/*, .../ClubDetailHeader/*
BackNavigationBar 및 ClubDetailHeader(및 스타일) 파일 전체 삭제.
삭제: 지원/하단 영역 관련
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/*, .../ClubDetailFooter/*, .../DeadlineBadge/*
ClubApplyButton, ClubDetailFooter, DeadlineBadge 컴포넌트와 스타일 전부 제거(모달/네비게이션/마감일 표시 흐름 제거).
삭제: 정보/탭/정보박스 관련
frontend/src/pages/ClubDetailPage/components/InfoTabs/*, .../InfoBox/*
InfoTabs, InfoBox 및 관련 스타일·로직 삭제(모집정보/동아리정보 탭 제거).
삭제: 소개 및 사진 UI 관련
frontend/src/pages/ClubDetailPage/components/IntroduceBox/*, .../PhotoList/*, .../PhotoCardList/*, .../PhotoList/LazyImage/*, .../PhotoList/LazyImage/LazyImage.test.tsx
IntroduceBox, PhotoList, PhotoCardList, LazyImage 및 관련 스타일/테스트 제거(사진 캐러셀과 연관된 모든 파일 삭제).
삭제: 카드/추천/공유/SNS 관련
frontend/src/pages/ClubDetailPage/components/ClubCard/*, .../RecommendedClubs/*, .../ShareButton/*, .../SnsLinkIcons/*
ClubCard, RecommendedClubs, ShareButton, SnsLinkIcons 및 스타일 삭제.
수정: PhotoModal 스타일/뷰어
frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts, .../PhotoModal/PhotoModal.tsx
모달 중앙 정렬 및 크기/섀도우/스러스트 개선, 썸네일/스크롤바 스타일 및 이미지 카운터 UI 추가(헤더에 "현재/전체" 인디케이터 표시).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ClubPage as ClubDetailPage (UI)
  participant API as useGetClubDetail (API)
  participant Mixpanel

  Note over ClubPage,API: 초기 로드
  ClubPage->>API: fetch clubDetail(clubId)
  API-->>ClubPage: clubDetail data

  User->>ClubPage: 클릭 "소개" 또는 "활동사진" 탭
  ClubPage->>ClubPage: setState(activeTab)
  ClubPage->>Mixpanel: trackEvent(CLUB_INTRO_TAB_CLICKED / CLUB_FEED_TAB_CLICKED)
  ClubPage-->>User: 렌더러가 해당 컴포넌트(ClubIntroContent / ClubFeed) 표시
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • seongwon030
  • lepitaaar
  • suhyun113

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목은 '동아리 상세페이지 디자인 개편에 따른 최종 구현 통합 및 API 연동'으로, 변경사항의 주요 내용(디자인 개편, 구현 통합, API 연동)을 정확히 반영합니다.
Linked Issues check ✅ Passed PR의 코드 변경사항이 MOA-456 이슈의 요구사항을 충족합니다: 기존 ClubDetailPage 대체, 실제 API 연동(useGetClubDetail), 컴포넌트 통합(ClubProfileCard, ClubIntroContent, ClubFeed)이 모두 구현되었습니다.
Out of Scope Changes check ✅ Passed ClubDetailPage2 삭제, App.tsx 라우팅 정리, 불필요한 컴포넌트(BackNavigationBar, ClubApplyButton, InfoTabs 등) 제거는 모두 디자인 개편 통합과 관련된 범위 내 변경입니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#968-club-detail-integrate-api-MOA-456

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@oesnuj oesnuj changed the title [feature] feature] MOA-456 동아리 상세페이지 디자인 개편에 따른 최종 구현 통합 및 API 연동 [feature] 동아리 상세페이지 디자인 개편에 따른 최종 구현 통합 및 API 연동 Dec 23, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts (1)

50-67: 매직 넘버를 상수로 추출 권장

탭 버튼의 고정 너비 167px가 하드코딩되어 있습니다. 디자인 시스템에서 재사용되거나 향후 조정이 필요할 경우를 대비해 명명된 상수로 추출하는 것을 권장합니다.

🔎 제안하는 리팩토링

파일 상단에 상수를 정의:

+const TAB_BUTTON_WIDTH = 167;
+
 export const Container = styled.div`

TabButton에서 사용:

 export const TabButton = styled.button<{ $active: boolean }>`
   font-size: 14px;
   font-weight: 700;
-  width: 167px;
+  width: ${TAB_BUTTON_WIDTH}px;
   padding-bottom: 4px;

코딩 가이드라인 참고: 매직 넘버를 명명된 상수로 교체하여 명확성을 높입니다.

frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)

36-80: 에러 바운더리 고려 제안

새로 추가된 컴포넌트들(ClubProfileCard, ClubIntroContent, ClubFeed)을 에러 바운더리로 감싸는 것을 고려해보세요. 개별 컴포넌트에서 에러 발생 시 전체 페이지가 깨지는 것을 방지할 수 있습니다.

🔎 에러 바운더리 적용 예시
<Styled.ContentWrapper>
  <ErrorBoundary fallback={<ProfileCardError />}>
    <ClubProfileCard
      name={clubDetail.name}
      logo={clubDetail.logo}
      cover={clubDetail.cover}
      recruitmentStatus={clubDetail.recruitmentStatus}
      socialLinks={clubDetail.socialLinks}
      activityDescription={clubDetail.description.activityDescription}
    />
  </ErrorBoundary>

  <Styled.RightSection>
    <Styled.TabList>
      {/* ... tabs ... */}
    </Styled.TabList>

    <Styled.TabContent>
      <ErrorBoundary fallback={<ContentError />}>
        {activeTab === 'intro' && (
          <ClubIntroContent {...clubDetail.description} />
        )}
        {activeTab === 'photos' && (
          <ClubFeed feed={clubDetail.feeds} clubName={clubDetail.name} />
        )}
      </ErrorBoundary>
    </Styled.TabContent>
  </Styled.RightSection>
</Styled.ContentWrapper>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8eded52 and f030cd4.

⛔ Files ignored due to path filters (1)
  • frontend/src/assets/images/logos/default_cover_image.png is excluded by !**/*.png
📒 Files selected for processing (46)
  • frontend/src/App.tsx
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.styles.ts
  • frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubCard/ClubCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubCard/ClubCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfile/ClubProfile.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubProfile/ClubProfile.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/DeadlineBadge/DeadlineBadge.styles.ts
  • frontend/src/pages/ClubDetailPage/components/DeadlineBadge/DeadlineBadge.tsx
  • frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.styles.ts
  • frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx
  • frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts
  • frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx
  • frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.styles.ts
  • frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/LazyImage/LazyImage.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoList/LazyImage/LazyImage.test.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/LazyImage/LazyImage.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoCardList/PhotoCardList.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx
  • frontend/src/pages/ClubDetailPage/components/RecommendedClubs/RecommendedClubs.styles.tsx
  • frontend/src/pages/ClubDetailPage/components/RecommendedClubs/RecommendedClubs.tsx
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
  • frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.styles.ts
  • frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx
  • frontend/src/pages/ClubDetailPage/mockData.ts
  • frontend/src/pages/clubDetailPage2/ClubDetailPage2.tsx
  • frontend/src/types/club.ts
💤 Files with no reviewable changes (34)
  • frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.styles.ts
  • frontend/src/pages/ClubDetailPage/components/RecommendedClubs/RecommendedClubs.styles.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/LazyImage/LazyImage.styles.ts
  • frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx
  • frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoList/LazyImage/LazyImage.tsx
  • frontend/src/pages/ClubDetailPage/components/DeadlineBadge/DeadlineBadge.styles.ts
  • frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts
  • frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.styles.ts
  • frontend/src/pages/ClubDetailPage/components/BackNavigationBar/BackNavigationBar.tsx
  • frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.tsx
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoList/LazyImage/LazyImage.test.tsx
  • frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubCard/ClubCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubProfile/ClubProfile.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoList.styles.ts
  • frontend/src/pages/ClubDetailPage/components/DeadlineBadge/DeadlineBadge.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoList/PhotoCardList/PhotoCardList.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubCard/ClubCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.styles.ts
  • frontend/src/pages/ClubDetailPage/components/RecommendedClubs/RecommendedClubs.tsx
  • frontend/src/pages/ClubDetailPage/components/InfoTabs/InfoTabs.styles.ts
  • frontend/src/App.tsx
  • frontend/src/pages/ClubDetailPage/components/SnsLinkIcons/SnsLinkIcons.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfile/ClubProfile.tsx
  • frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.styles.ts
  • frontend/src/pages/clubDetailPage2/ClubDetailPage2.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubDetailHeader/ClubDetailHeader.tsx
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries with if/else or IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling

Files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx
  • frontend/src/types/club.ts
frontend/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling

Files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

Use consistent return types for similar functions/hooks

Files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx
  • frontend/src/types/club.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Abstract complex logic/interactions into dedicated components/HOCs

Applied to files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling

Applied to files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Separate significantly different conditional UI/logic into distinct components

Applied to files:

  • frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx
📚 Learning: 2025-07-19T05:05:10.196Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.

Applied to files:

  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Use Component Composition instead of Props Drilling to reduce coupling

Applied to files:

  • frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx
🧬 Code graph analysis (3)
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (2)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts (1)
  • Header (64-69)
frontend/src/constants/eventName.ts (1)
  • USER_EVENT (1-38)
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
  • media (8-14)
frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx (1)
frontend/src/hooks/PhotoList/usePhotoModal.ts (1)
  • usePhotoModal (3-14)
🔇 Additional comments (11)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts (1)

13-16: 태블릿 반응형 스타일 조정 확인

태블릿 브레이크포인트에서 border-radius를 0으로 변경하여 전체 너비 레이아웃에 맞게 조정한 것이 적절합니다.

frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.styles.ts (1)

11-22: 반응형 패딩 조정 확인

컨테이너의 패딩 구조를 변경하여 태블릿에서 좌우 20px 여백을 추가한 것이 ClubDetailPage의 전체 레이아웃 구조와 일관성 있게 적용되었습니다.

frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts (1)

35-48: 탭 UI 개선 사항 확인

TabList의 디자인 변경사항이 잘 적용되었습니다:

  • 하단 마진 추가로 콘텐츠와의 간격 확보
  • 테두리 색상을 gray[200]으로 조정하여 더 부드러운 시각적 구분
  • 태블릿/모바일 반응형 정렬 처리
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (1)

49-57: 기본 이미지 폴백 처리 확인

로고와 커버 이미지를 optional prop으로 변경하고 기본 이미지로 폴백하는 로직이 잘 구현되었습니다. 이미지 데이터가 없을 때 404 오류를 방지하는 좋은 접근입니다.

frontend/src/pages/ClubDetailPage/components/ClubFeed/ClubFeed.tsx (1)

5-51: prop 이름 변경 및 일관성 확인

photos에서 feed로 prop 이름을 변경하고 모든 참조를 일관되게 업데이트했습니다. 더 명확한 의미 전달을 위한 좋은 리팩토링입니다.

frontend/src/types/club.ts (1)

17-64: 타입 구조 개선 확인

ClubDetail.descriptionstring에서 DetailedDescription으로 변경하여 구조화된 데이터를 지원하도록 개선했습니다. 이는 PR 목표에 명시된 대로 mock 데이터 제거 및 실제 API 연동을 위한 의도적인 breaking change입니다.

추가된 타입들(Award, IdealCandidate, FAQ)이 명확하게 정의되어 있으며, 새로운 필드들(feeds, presidentName, presidentPhoneNumber, 모집 관련 필드)이 API 요구사항과 일치합니다.

frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx (2)

21-35: 컴포넌트 리네임 및 props 인터페이스 정리 확인

ClubDetailContent에서 ClubIntroContent로 컴포넌트 이름을 변경하고, introDescription prop을 public API에서 제거한 것이 PR 목표에 부합합니다. 컴포넌트의 역할이 더 명확해졌습니다.


54-139: 조건부 렌더링 구조 확인

각 섹션(activityDescription, awards, idealCandidate, benefits, faqs)에 대해 적절한 null 체크와 조건부 렌더링을 수행하고 있습니다. 데이터가 없을 때 불필요한 UI를 렌더링하지 않는 좋은 구조입니다.

frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (3)

15-20: 로컬 상태 관리 및 추적 로직 추가 확인

탭 상태를 로컬 state로 관리하고 Mixpanel 이벤트 추적을 통합한 것이 적절합니다. 간단한 탭 전환에는 별도의 상태 관리 라이브러리가 필요하지 않으므로 현재 구조가 효율적입니다.

코딩 가이드라인 참고: 브로드한 상태 관리를 더 작고 집중된 hooks/contexts로 분리


34-83: 모듈형 컴포넌트 구조로 개선 확인

기존의 모놀리식 레이아웃을 ClubProfileCard, ClubIntroContent, ClubFeed로 분리하여 명확한 책임을 가진 모듈형 구조로 개선했습니다. 각 컴포넌트가 독립적인 역할을 수행하며 컴포넌트 컴포지션을 통해 결합도를 낮췄습니다.

코딩 가이드라인 참고: 컴포넌트 컴포지션을 사용하여 Props Drilling 대신 결합도를 낮춤


50-68: 탭 전환 로직 및 이벤트 추적 구현 확인

탭 클릭 시 상태 업데이트와 함께 사용자 이벤트를 추적하도록 구현되어 있습니다. 각 탭에 대해 별도의 이벤트(CLUB_INTRO_TAB_CLICKED, CLUB_FEED_TAB_CLICKED)를 발생시켜 사용자 행동 분석이 가능합니다.

- 데스크탑/랩탑에서 90vw/90vh 크기의 둥근 모달로 표시
- 모바일에서는 전체 화면으로 표시
- 현재 이미지 번호 표시 기능 추가 (예: 1 / 15)
- 모바일/태블릿에서 네비게이션 화살표 숨김 처리
- 썸네일 크기를 반응형으로 조정 (데스크탑: 44px, 태블릿: 32px, 모바일: 28px)
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (1)

86-90: LGTM! 이전 리뷰 제안이 올바르게 구현되었습니다.

username 추출 로직이 query parameter(?)와 fragment(#)를 올바르게 제거하도록 구현되었습니다. IIFE를 사용한 명확한 구조로 가독성도 좋습니다.

이전 리뷰에서 제안된 정확한 구현이 적용되었습니다:

  • https://www.instagram.com/valid_id?igsh=xyz@valid_id
  • https://www.youtube.com/@channel#section@channel

코딩 가이드라인 참조: 복잡한 boolean 조건은 명명된 변수에 할당하거나 IIFE를 사용하여 명확한 의미를 전달하세요.

🧹 Nitpick comments (3)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts (1)

24-48: 매직 넘버를 명명된 상수로 교체하는 것을 고려하세요.

BackButton의 위치(top: 58px, left: 25px) 및 크기(width: 25px, height: 25px) 값이 하드코딩되어 있습니다. 이러한 값들을 명명된 상수로 추출하면 유지보수성이 향상되고 의도가 더 명확해집니다.

🔎 제안하는 리팩토링
+const BACK_BUTTON = {
+  SIZE: 25,
+  TOP: 58,
+  LEFT: 25,
+} as const;
+
 export const BackButton = styled.button`
   position: absolute;
-  top: 58px;
-  left: 25px;
-  width: 25px;
-  height: 25px;
+  top: ${BACK_BUTTON.TOP}px;
+  left: ${BACK_BUTTON.LEFT}px;
+  width: ${BACK_BUTTON.SIZE}px;
+  height: ${BACK_BUTTON.SIZE}px;
   padding: 0;
   border: none;
   background-color: transparent;

코딩 가이드라인 참조: 매직 넘버를 명확성을 위해 명명된 상수로 교체하세요.

frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx (1)

54-56: 이미지 카운터 표시 구현이 깔끔합니다.

현재 이미지 위치를 사용자에게 명확하게 전달하는 좋은 UX 개선입니다.

선택적 개선: 스크린 리더 접근성

스크린 리더 사용자를 위해 aria-live 속성을 추가하면 이미지 변경 시 카운터 업데이트가 음성으로 안내됩니다:

-          <Styled.ImageCounter>
+          <Styled.ImageCounter aria-live="polite" aria-atomic="true">
             {currentIndex + 1} / {urls.length}
           </Styled.ImageCounter>
frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts (1)

11-13: 모달 중앙 정렬 및 반응형 레이아웃이 잘 구현되었습니다.

Flex를 이용한 중앙 정렬과 데스크탑/모바일 반응형 처리가 적절합니다.

코딩 가이드라인에 따라, 반복적으로 사용되거나 의미 있는 매직 넘버들을 상수로 추출하면 유지보수성이 향상됩니다:

🔎 상수 추출 제안

파일 상단에 상수를 정의하세요:

const MODAL_DIMENSIONS = {
  DESKTOP: {
    WIDTH: '90vw',
    HEIGHT: '90vh',
    MAX_WIDTH: '1200px',
    BORDER_RADIUS: '18px',
  },
  MOBILE: {
    WIDTH: '100vw',
    HEIGHT: '100vh',
    BORDER_RADIUS: '0',
  },
} as const;

그리고 스타일에 적용:

 export const ModalContent = styled.div`
-  width: 90vw;
-  height: 90vh;
-  max-width: 1200px;
+  width: ${MODAL_DIMENSIONS.DESKTOP.WIDTH};
+  height: ${MODAL_DIMENSIONS.DESKTOP.HEIGHT};
+  max-width: ${MODAL_DIMENSIONS.DESKTOP.MAX_WIDTH};
   background: #fff;
-  border-radius: 18px;
+  border-radius: ${MODAL_DIMENSIONS.DESKTOP.BORDER_RADIUS};
   // ...

   ${media.mobile} {
-    width: 100vw;
-    height: 100vh;
-    border-radius: 0;
+    width: ${MODAL_DIMENSIONS.MOBILE.WIDTH};
+    height: ${MODAL_DIMENSIONS.MOBILE.HEIGHT};
+    border-radius: ${MODAL_DIMENSIONS.MOBILE.BORDER_RADIUS};
   }
 `;

참고: 코딩 가이드라인에 따라 매직 넘버를 명명된 상수로 교체하여 명확성을 높이는 것을 권장합니다.

Also applies to: 33-48

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f030cd4 and 2cc9bef.

📒 Files selected for processing (6)
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/ClubDetailPage/ClubDetailPage.styles.ts
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries with if/else or IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling

Files:

  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
frontend/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling

Files:

  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
frontend/**/*.{ts,tsx}

📄 CodeRabbit inference engine (frontend/.cursorrules)

Use consistent return types for similar functions/hooks

Files:

  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts
  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts
  • frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx
🧠 Learnings (2)
📓 Common learnings
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.

Applied to files:

  • frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx
  • frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx
🧬 Code graph analysis (2)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
  • media (8-14)
frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts (2)
frontend/src/styles/mediaQuery.ts (1)
  • media (8-14)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.styles.ts (1)
  • ClubName (102-113)
🔇 Additional comments (9)
frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx (2)

56-139: 조건부 렌더링 구조가 안전하고 명확합니다.

각 섹션의 조건부 렌더링 로직이 적절하게 구현되었습니다:

  • 문자열 필드(activityDescription, benefits)는 ?.trim() 체크로 빈 값과 공백만 있는 경우를 모두 방지
  • 중첩 객체(idealCandidate?.content?.trim())도 optional chaining으로 안전하게 처리
  • 배열 필드(awards, faqs)는 존재 여부와 길이를 모두 확인

이 구조는 API에서 누락되거나 비어있는 필드가 있어도 안전하게 동작합니다.


12-12: TODO 주석 확인 요청.

tags 필드 추가와 관련된 TODO 주석이 있습니다. 이 작업이 별도 이슈로 추적되고 있는지 확인해주세요.

frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (4)

1-9: LGTM!

필요한 임포트가 올바르게 추가되었습니다. useNavigate, 기본 이미지, 뒤로가기 버튼 아이콘이 적절하게 임포트되었습니다.


13-14: LGTM!

logocover를 선택적 프로퍼티로 변경한 것은 API 연동 시 데이터가 없을 수 있는 상황을 적절히 처리합니다. 기본 이미지 폴백과 함께 사용하면 404 오류를 방지할 수 있습니다.


28-32: LGTM!

navigate(-1)을 사용한 뒤로가기 구현이 React Router의 표준 패턴을 따르고 있으며, 접근성을 위한 aria-label도 적절히 추가되었습니다(line 61).


64-64: LGTM!

기본 이미지를 폴백으로 사용하여 API에서 이미지 데이터가 없을 때 깨진 이미지를 방지합니다. 방어적 프로그래밍의 좋은 예시입니다.

Also applies to: 69-69

frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts (3)

106-136: 이미지 컨테이너 및 이미지 스타일이 잘 구성되었습니다.

데스크탑에서 이미지를 85%로 제한하고 모바일에서 100%로 복원하는 반응형 처리가 적절합니다. border-radiuspadding 조정도 UX를 개선합니다.


181-227: 썸네일 컨테이너 및 스크롤바 커스터마이징이 훌륭합니다.

반응형 gappadding 조정, 그라디언트 배경, 커스텀 스크롤바 스타일이 모두 사용자 경험을 향상시킵니다.


229-265: 썸네일 크기 및 반응형 처리가 적절합니다.

데스크탑(44px) → 태블릿(32px) → 모바일(28px)로 단계적으로 크기를 조정하는 반응형 디자인이 잘 구현되었습니다.

Comment on lines +89 to +96
export const ImageCounter = styled.div`
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 0.95rem;
font-weight: 500;
color: #666;
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the PhotoModal files
find frontend -name "PhotoModal*" -type f | head -20

Repository: Moadong/moadong

Length of output: 206


🏁 Script executed:

# Let's check the complete PhotoModal.styles.ts file to see all styled components
cat -n frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts | head -150

Repository: Moadong/moadong

Length of output: 3981


🏁 Script executed:

# Let's also check the PhotoModal.tsx component structure
cat -n frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.tsx | head -100

Repository: Moadong/moadong

Length of output: 3966


ImageCounter의 절대 위치 지정으로 인한 겹침 가능성을 확인하세요.

ModalHeader에서 ClubName은 flex 레이아웃에만 참여하고 overflow 처리가 없어서, 긴 동아리 이름이 중앙의 ImageCounter와 겹칠 수 있습니다.

다음 중 하나를 적용하세요:

  1. ClubName에 제약 추가 (권장)

    export const ClubName = styled.div`
      font-size: 1.3rem;
      font-weight: 600;
      color: #000;
    +  max-width: 35%;
    +  overflow: hidden;
    +  text-overflow: ellipsis;
    +  white-space: nowrap;
    `;
  2. Grid 레이아웃으로 변경 (더 견고한 구조)

    export const ModalHeader = styled.div`
      width: 100%;
      height: 60px;
    -  display: flex;
    -  justify-content: space-between;
    +  display: grid;
    +  grid-template-columns: 1fr auto 1fr;
      align-items: center;
      padding: 0 32px;

긴 동아리 이름으로 테스트하여 레이아웃이 올바르게 동작하는지 확인하세요.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
frontend/src/pages/ClubDetailPage/components/PhotoModal/PhotoModal.styles.ts
lines 89-96: ImageCounter is absolutely positioned centered which can overlap
with a long ClubName in the ModalHeader; update styles to prevent overlap by
either (A) constraining ClubName (recommended) — give ClubName a max-width (e.g.
calc(100% - reservedSpace)), set overflow: hidden; text-overflow: ellipsis;
white-space: nowrap; and ensure its container is flex with align-items:center so
it shrinks properly, or (B) switch ModalHeader to a grid layout with three areas
(left, center, right) and place ImageCounter in its own center area so it does
not overlap; after applying either change, test with very long club names to
confirm no overlap.

@oesnuj oesnuj merged commit 44227fd into develop-fe Dec 26, 2025
3 checks passed
@oesnuj oesnuj deleted the feature/#968-club-detail-integrate-api-MOA-456 branch January 25, 2026 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎨 Design 마크업 & 스타일링 💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants